Skip to Content

Push

Push messages provide a way to inform or remind a user that an action should be taken by delivering a short message that appears as a notification on the device.

When clicked on, the app that is associated with the message is opened.
The Kapsel push plugin provides an abstraction layer over the Google Cloud Messaging for Android (GCM) (SP14-), Firebase Cloud Messaging (SP15+), Apple Push Notification Service (APNS) and Windows Push Notification Services (WNS) overview. A notification can be sent to a device via a REST call to a specific registration id, a user id that may have registered on multiple devices, or to all users of an application as shown in the following examples of a URL used in a REST call.

http://localhost:8080/restnotification/registration/application_registration_id
http://localhost:8080/restnotification/application/com.kapsel.gs/user/daniel_v
http://localhost:8080/restnotification/application/com.kapsel.gs

The REST call should also include a payload for the message such as the following.

{"alert":"New Vacation Approval Request", "data":"For (i826565) request (7)" }

If the SAP Cloud Platform mobile service is being used, the Push Desk can also be used to send a notification. Note a filter must be applied to see the registration list.

Note that push requires open ports that may be blocked on some corporate networks or routers. GCM uses ports between 5228 and 5230 on older Android devices as per Google Cloud Messaging not working on 4.1.2 devices on a corporate network. APNS uses the ports described here.

For additional details see C:\SAP\MobileSDK3\KapselSDK\docs\api\sap.Push.html or Push Notification Plugin.

The following steps will demonstrate this plugin using Google Cloud Messaging. Note, it requires either an Android 4.2.2 or higher device with Google Play Store installed (Setting Up Google Play Services) or an Android emulator that targets the Google API’s.

Note, the Push plugin on Android requires the below two checked components from the SDK Manager.

The process for iOS and Windows is covered by others. Here are a few posts to check out.
Apple Push Notification Services in iOS 6
[Kapsel] How to use the Push plugin on iOS
[Kapsel] How to use the Push plugin on Windows

  • Create a server key by creating a new project in the Firebase console. After the project is created, open it and then select Project Settings , then click on the tab CLOUD MESSAGING to display the sender ID and Server API Key.

    Note, either the Server key or Legacy Server key can be used.For more details on the sender ID and Server API key see Credentials.See also Firebase Cloud Messaging and GCM and FCM Frequently Asked Questions.
  • Fill in the Push settings for the application.
  • The following instructions set the user name and password that can be used to send notifications when using an SMP server.
    Edit the Notification security provider and add a System Login (Admin Only) authentication provider. Set the user name and password to a value like smpPushUser and smpPushPwd and the role Notification User. Note this user name and password will need to be entered later when we send a notification to the device using the Advanced REST client.

    Restart the SMP 3.0 server.
  • The following instructions specify the user that can send notifications with the SAP Cloud Platform Mobile Services server.
    Add your user to this role in the SAP Cloud Platform Cockpit.
    Choose Services > Development & Operations > Configure Development & Operations > Roles > Notification User > Assign.
  • Delete the app KapselGSDemo from the device.
  • Remove the AppUpdate plugin and add push plugin.
    cordova plugin remove kapsel-plugin-appupdate
    cordova plugin add kapsel-plugin-push --searchpath %KAPSEL_HOME%/plugins
    
  • Replace www\index.html with the following contents.
    <html>
        <head>
            <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
            <script type="text/javascript" charset="utf-8" src="serverContext.js"></script>
            <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
            <script>
                applicationContext = null;
    
                window.onerror = onError; 
                var startTime = null;
                
                function onError(msg, url, line) {
                    console.log("EventLogging: onError");
                    var idx = url.lastIndexOf("/");
                    var file = "unknown";
                    if (idx > -1) {
                        file = url.substring(idx + 1);
                    }
                    alert("An error occurred in " + file + " (at line # " + line + "): " + msg);
                    console.log("EventLogging: An error occurred in " + file + " (at line # " + line + "): " + msg);
                    return false; //suppressErrorAlert;
                }
                
                function init() {
                    console.log("EventLogging: init");
                    if (sap.Logger) {
                        sap.Logger.setLogLevel(sap.Logger.DEBUG);  //enables the display of debug log messages from the Kapsel plugins.
                        sap.Logger.debug("Log level set to DEBUG");
                    }
                    sap.Logon.init(logonSuccessCallback, logonErrorCallback, appId, context);
                }
                
                function register() {
                   console.log("EventLogging: register");
                   sap.Logon.init(logonSuccessCallback, logonErrorCallback, appId, context);
                }
    
                function logonErrorCallback(error) {
                    console.log("EventLogging: logonErrorCallback:  " + JSON.stringify(error));
                    if (device.platform == "Android") {  //Not supported on iOS
                        navigator.app.exitApp();
                    }
                }
                
                function unRegister() {
                    console.log("EventLogging: unRegister");
                    try {
                        sap.Logon.core.deleteRegistration(logonUnregisterSuccessCallback, errorCallback);
                    }
                    catch (e) {
                        alert("problem with unregister");
                    }
                }
                
                function logonSuccessCallback(result) {
                    console.log("EventLogging: logonSuccessCallback " + JSON.stringify(result));
                    applicationContext = result;
                    startTime = performance.now();
                    if (window.localStorage.getItem("isPushRegistered") == "true") {
                        initPush();  //faster then calling registerForPush
                    }
                    else {
                        window.localStorage.setItem("isPushRegistered", "false");
                        registerForPush();
                    }
                }
                
                function logonUnregisterSuccessCallback(result) {
                    console.log("EventLogging: logonUnregisterSuccessCallback");
                    alert("Logon Unregistration success");
                    applicationContext = null;
                    window.localStorage.setItem("isPushRegistered", "false");
                }
                
                function errorCallback(e) {
                    console.log("EventLogging: errorCallback " + JSON.stringify(e));
                    alert("An error occurred " + JSON.stringify(e));
                }
    
                function registerForPush() {
                    console.log("EventLogging: registerForPush");
                    var nTypes = sap.Push.notificationType.SOUNDS | sap.Push.notificationType.ALERT;
                    sap.Push.registerForNotificationTypes(nTypes, pushRegistrationSuccess, pushRegistrationFailure, processNotification, null /* optional GCM Sender ID */);
                }
    
                function initPush() {
                    console.log("EventLogging: initPush");
                    sap.Push.initPush(processNotification);
                }
    
                function unregisterForPush() {
                    console.log("EventLogging: unregisterForPush");
                    var nTypes = sap.Push.notificationType.SOUNDS | sap.Push.notificationType.ALERT;
                    sap.Push.unregisterForNotificationTypes(pushUnregistrationCallback);
                }
    
                function pushRegistrationSuccess(result) {
                    console.log("EventLogging: pushRegistrationSuccess" + JSON.stringify(result));
                    window.localStorage.setItem("isPushRegistered", "true");
                }
                
                function pushRegistrationFailure(errorInfo) {
                    console.log("EventLogging: pushRegistrationFailure  " + JSON.stringify(errorInfo));                
                    alert("Error while registering for Push.  " + JSON.stringify(errorInfo));
                }
    
                function pushUnregistrationCallback(result) {
                    console.log("EventLogging: pushUnregistration: " + JSON.stringify(result));
                    alert("Successfully unregistered for Push: " + JSON.stringify(result));
                    window.localStorage.setItem("isPushRegistered", "false");
                }
    
                function processNotification(notification) {
                    var endTime = performance.now();
                    console.log("EventLogging Perf: " + ((endTime - startTime)/1000).toFixed(3) + " seconds from logonSuccess till message received");
                    alert("in processNotification: " + JSON.stringify(notification));
                    console.log("EventLogging: processNotification: " + JSON.stringify(notification));
                    if (sap.Push.setPushFeedbackStatus && notification.additionalData) {  //SP15 new feature
                        sap.Push.setPushFeedbackStatus('consumed', notification.additionalData.notificationId, pushFeedbackStatusSuccessCallback, pushFeedbackStatusErrorCallback);
                    }
                }
    
                function pushFeedbackStatusSuccessCallback(status) {
                    console.log("EventLogging: set the push feedback status to consumed");
                }
    
                function pushFeedbackStatusErrorCallback(status) {
                    console.log("EventLogging: push feedback error: " + JSON.stringify(status));
                }
    
                function onLoad() {
                    console.log("EventLogging: onLoad");
                }
    
                function onBeforeUnload() {
                    console.log("EventLogging: onBeforeUnLoad");
                }
    
                function onUnload() {
                    console.log("EventLogging: onUnload");
                }
    
                function onPause() {
                    console.log("EventLogging: onPause");
                }
    
                function onResume() {
                    console.log("EventLogging: onResume");
                }
    
                function onSapResumeSuccess() {
                    console.log("EventLogging: onSapResumeSuccess");
                }
    
                function onSapResumeError(error) {
                    console.log("EventLogging: onSapResumeError " + JSON.stringify(error));
                }
    
                function onSapLogonSuccess() {
                    console.log("EventLogging: onSapLogonSuccess");
                }
    
                document.addEventListener("deviceready", init, false);
                document.addEventListener("pause", onPause, false);
                document.addEventListener("resume", onResume, false);
                document.addEventListener("onSapResumeSuccess", onSapResumeSuccess, false);
                document.addEventListener("onSapLogonSuccess", onSapLogonSuccess, false);
                document.addEventListener("onSapResumeError", onSapResumeError, false);
            </script>
        </head>
        <body onload="onLoad()" onbeforeunload="onBeforeUnload()" onunload="onUnload()" >
            <h1>Push Sample</h1>
            <button id="register" onclick="register()">Register</button>
            
            <button id="unregister" onclick="unRegister()">Unregister</button>
            <button onclick="unregisterForPush()">Unregister For Push</button>
            <br>
    
        </body>
    </html>
    ​
  • If you are using SP15 or above, follow the instructions at https://support.google.com/firebase/answer/7015592#android to download the google-services.json and copy it to C:\Kapsel_Projects\KapselGSDemo.
  • Prepare, build and deploy the project.
    cordova run android
  • Note that in the logonSuccessCallback the registerForPush method is called which registers the device to receive push messages the first time it is called and on subsequent calls initializes the push functionality in the app.
  • Send a REST request to the SMP server to send a notification to the Kapsel app. Note the example below is using Postman and the provided collection from the section Accessing the Application using the REST API. Remember to use the credential smpPushUser and smpPushPassword under the Authorization tab after selecting Basic Auth.For further details see Notification Data Sent Using Push API and GCM Push Notification Payload Handling.

    http://127.0.0.1:8080/restnotification/application/com.kapsel.gs/
    {"alert":"New Vacation Approval Request", "data":"For (i82XXXX) request (7)" }
    
    http://localhost:8080/restnotification/registration/59a72605-2a64-4778-953d-c215a7d565eb
    {"alert":"New Vacation Approval Request", "data":"For (i82XXXX) request (7)" }
    
    http://localhost:8080/restnotification/application/com.kapsel.gs/user/i82XXXX
    {"alert":"New Vacation Approval Request", "data":"For (i82XXXX) request (7)" }
  • Now press the back button to exit the app and send the notification again. The notification should appear on the device or simulator and clicking on it will open the app.

    The details of the selected notification will be processed in the processNotification method.
  • A new feature in SP15 is the ability for the push plugin to report to the SAP Cloud platform server that the notification is received and if the notification was opened or consumed by the app.


    In the above graph, 1 notification has been sent but not received and one notification has been received but not consumed. Once the device or emulator receives the notification, the chart would change to have 0 notifications in a sent state and 2 notifications in a received state.Note, it is up to the application to call the setPushFeedbackStatus API when the app processes the notification and pass in ‘consumed’ as a parameter.
    The Kapsel push plugin automatically notifies the server when it receives the notification on the device.
    The application should add a call to setPushFeedbackStatus in the processNotification method if they wish to notify the server that the message was processed by the user.
  • A logcat filter using the log tag value of PUSH will filter out the messages sent from the Kapsel push plugin. Note, this filter will not show the messages displayed using console.log().
  • Push notifications can be disabled on Android and iOS.
    On iOS open Settings > Notifications > PushDemo
    On Android open Settings > Apps > PushDemo > Uncheck Show Notifications
  • On Android, the sound played when a notification arrives can be selected. Settings > Sound & notification> Default notification ringtone.On an Android emulator, the sound file first needs to be copied onto the device. Open the DDMS perspective in eclipse, select the File Explorer and copy C:\adt-bundle-windows-x86_64-20130917\sdk\platforms\android-19\data\res\raw\fallbackring.ogg to storage\sdcard\Notifications. Restart the emulator. Fallbackring should now be accessible in the settings.
  • You may also be interested in the some of the other push plugins available such as the phonegap-plugin-push.
  • The following posts may also be of interest.
    Keeping Google Cloud Messaging For Android Working Reliably
    How to call the REST Notification API of SMP 3 from ABAP in an Username based scenario
    Delay in receiving messages of 15 minutes or 28 minutes

Troubleshooting

  • On some Android devices, there is an error while registering for notification.
    java.lang.UnsupportedOperationException: Device does not have package com.google.android.gsf

    This may occur if the Google Play Services are missing. In order to receive push notifications, follow the below instructions.
    Open the Android SDK Manager click on the Obsolete checkbox and download Extras >Google Cloud Messaging for Android Library (Obsolete)
    Copy the below jar into the libs folder of the PushDemo project.

    C:\Android\adt-bundle-windows-x86_64-20130522\sdk\extras\google\gcm\gcm-client\dist\gcm.jar
  • If the status is 403, this indicates a permission problem. Double check that that the correct user id and password for the Notification security profile were correctly entered. Also, double check that the correct role name of Notification User was assigned to the Roles of the authentication provider for the Notification security profile.
  • If the status is 404, this may indicate that the app did not successfully register for notifications. Try pressing the Register for Push button again and verify that the Successfully registered alert displays.The message Invalid Notification target id indicates that device did not successfully register for push. This can be investigated further via the below select command.
    SELECT * from SMP-NOTIFICATION-TARGET where TARGETID = '<connection id>'
    Verify that the 
    - row exists, 
    - TARGETID value is the connection ID, 
    - CONFIGID value is the application ID
    - DEVICENOTIFICATIONKEY1 is the device ID from the gateway/ device ID from APNS or GCM
    - NOTIFICATIONTYPE  must not be null, 0 for APNS, 1 for GCM, 2 for Blackberry, 4 for WNS and 5 for MPNS
  • If the status is 200, this may indicate that the app has not registered and received a registration id from Google or perhaps that there is some issue reaching the Google GCM service. Double check that the sender ID was added to the index.html.

 

Back to Getting Started With Kapsel

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Vigil Jacob

    Hi Daniel,

    Thank you for the wonderful blog! Quick question on sending multi-line push notification for Android devices. We are able to send for iOS using \n character. However, for Android, the notification gets cut at the point where \n occurs.

    As per online blogs, NotificationCompat.BigTextStyle() must be used at the server side while building the notification. Is this feature available at the server level?

    If yes, how can we invoke this process for Android using Push Notification APIs.

    Thank You!

    Cheers,
    Vigil George

     

    (0) 
    1. Daniel Van Leeuwen Post author

      Thanks for the feedback.

      I believe the change may need to be in the Kapsel Push plugin.  I have sent your comments along to the development team.  Will post back once I hear more.

      Regards,

      Dan van Leeuwen

      (0) 
      1. Daniel Van Leeuwen Post author

        I believe a fix was made to display notifications on multiple lines on Android and should appear in an upcoming patch, possibly 3.15.4.

        Regards,

        Dan van Leeuwen

        (0) 
          1. Daniel Van Leeuwen Post author

            I believe the SP 15 PL03 is now available.  It should contain the change to enable multiline notifications on Android.

            Regards,

            Dan van Leeuwen

            (0) 

Leave a Reply