Skip to Content
Technical Articles
Author's profile photo Daniel Van Leeuwen

Getting Started with Kapsel – Part 4 — Push(SP13+)

Previous   Home   Next

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, Push API Notification Scenarios 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.

 

Previous   Home   Next

Assigned Tags

      23 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      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

       

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog 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

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog 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

      Author's profile photo Former Member
      Former Member

      Hi Daniel,

      Thank you for the update. Looking forward to this feature on Android.

      BR,
      Vigil

       

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog 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

      Author's profile photo Sandeep Kamireddy
      Sandeep Kamireddy

      Hello Daniel,

      Thanks for the post.

      I have tried the above scenario in SP14 Android. When I'm running the application, getting an error message "Uncaught ReferenceError: Context is not defined".

      Could you please help me in resolving this issue.

      Thanks in Advance.

      Regards,

      Sandeep K S

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog Post Author

      I wonder if the import for the severContext.js file is failing?

      Have a look at the serverContext file in logon plugin section of the guide.

      https://blogs.sap.com/2016/10/19/getting-started-kapsel-part-2-logon-sp13/

       

      Hope that helps,

      Dan van Leeuwen

      Author's profile photo Sandeep Kamireddy
      Sandeep Kamireddy

      Thanks Dan.. Now it's working..!!

      Regards

      Sandeep K S

      Author's profile photo KRISHNA SRIVASTAVA
      KRISHNA SRIVASTAVA

      Hello Daniel,

      Thanks for the post.

      I am facing issue whenever I am trying to send push notification to the particular user with format 'Domain\userid' as our users are registered on the SMP with domain name as we are using AD for authentication purpose via security profile

      I have also posted this question on SCN-

      https://answers.sap.com/questions/370798/error-when-sending-push-message-from-rest-client.html#

      Can you please check & help me.

       

      Thanks in advance!

      Kind regards,

      Krishna

      Author's profile photo KRISHNA SRIVASTAVA
      KRISHNA SRIVASTAVA

      Issue is resolved now.

      Thanks

      Author's profile photo Nikita Elsakov
      Nikita Elsakov

      Hi, Daniel.
      Thank you very much for your great blog.
      Could you please tell if there is a way to add action buttons to push notification using kapsel-plugin-push?
       

      Best regards,
      Nikita

      Author's profile photo Imen BS
      Imen BS

      Hi Daniel,

      A great post once again 🙂
      I'm trying to send notifications to an Android device since a few days now.
      I'm using a SMP server and I followed all the steps but I'm getting the following error in SMP events logs: Google server certificate not found in truststore.
      According to SAP support, I need to import a new valid FCM certificate and its CA certificate. How can I do that?

      Thank you 🙂

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog Post Author

      Perhaps the following link might help.

      https://help.sap.com/viewer/313e7789125149b3b5bb6f1c7e1ea322/3.0.14/en-US/7ebb5f28cf7142cc9eccd5c0d7b4388e.html

      Is it an old instance of the SMP server?  Perhaps try installing a new local instance using the latest version?

      I suppose one other option would be to use the SAP Cloud Platform Mobile Services trial account.

      Hope that helps,

      Dan van Leeuwen

      Author's profile photo Imen BS
      Imen BS

      Thank you for the reply !
      My SMP version is 3.0.13.0. It's not that old, is it?
      The link explains how to import a certificate, but I don't know how to generate it 🙁

       

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog Post Author

      I have not myself encountered this error before.  Does the management console show an expired certificate?  I would recommend contacting support or perhaps trying the SAP Cloud Platform Mobile Services trial.

      Regards,

      Dan van Leeuwen

      Author's profile photo Christoffer Fuss
      Christoffer Fuss

      Hello,

      the Push Plugin is not working anymore for Android since Version 8.0. It seems the GCM Push Notification Payload was changed from data to notification in android 8:

      https://help.sap.com/viewer/33c4b62fdc174d89a47d4baee3ced08a/Cloud/en-US/5a30bd01a76849b4a430ae17c932fda0.html

      {
      "GCM": "{ \"data\": { \"text\": \"test message\" } }"
      }
      
      {
      "GCM": "{ \"notification\": { \"text\": \"test message\" } }"
      }
      

      I am using SDK 3.1 SP01 with Cordova Android 7.0.0.

      Can you please check this?

      Best regards,

      Chris

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog Post Author

      I tried this today with an Android 8.1.0 emulator and the sample app in this tutorial was able to display the notification successfully when the app was in the foreground.  If the app was in the background or not running, I saw a toast that said Failed to post notification on channel "null".  I will forward this on to a colleague to investigate further but wanted to verify if I am reproducing the issue you are seeing.

      Regards,

      Dan van Leeuwen

       

       

      Author's profile photo Christoffer Fuss
      Christoffer Fuss

      Hi Daniel,

      I have not tested it with an emulator. I tried it with an android device with android 8.0: Here the app crahses if the app is in the background and a push notification is recevied. If the app is not open at all, the push notfication is not received.

      On an Android 9 device the app is not crashing when a push notifcation arrives but the push notification is never received.

      Should I create an incident on that? We need a solution really fast....

      Thanks for your help,

      Chris

       

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog Post Author

      Yes, creating an incident would be recommended.

      Dan

      Author's profile photo Christoffer Fuss
      Christoffer Fuss

      I created an incident in Component MOB-SDK, I am not sure if it is the correct one. Here is the incident number: 441044 / 2018.

      As I said, we need a solution really fast because customers have upgraded their devices to android 8 or higher.

      Best Regards and Thanks for helping,

      Chris

       

      Author's profile photo Mikhail Groshev
      Mikhail Groshev

      Hi Daniel!Thank you for yet another great post (again)!

       

      Could you please clarify two more things:

      1.   How to manage the action taken upon the notification click?

      I mean the user has received the notification about one more item in his inbox and i want him to launch specific inbox application tile after click on notification (not the general launchpad as default).Is it possible to do that?

       

      2. I don't understand the badge thing.

      Again assuming the user is being received the notifications about new items in his inbox.

      The general behavior you may expect is the badge counter iteratively increased with each new notification and cleared after the inbox application is launched.

      But what we see here is that badge is always the exact value from the POST.

      It doesn't iterate and it doesn't even zeroed upon the application launch neither.

      How is it managed?

       

      Thank you and best regards,

      Mikhail.

      Author's profile photo Daniel Van Leeuwen
      Daniel Van Leeuwen
      Blog Post Author
      1. If this is a custom app that you are developing, then in the processNotification method, after parsing the message, you can open a specific screen to handle the notification.
      2. I believe the two things you can control is the badge that appears via the badge value in the post and there is also an API (resetBage) that can be called to clear the badging after the application has processed the message.
        https://help.sap.com/doc/acbf025f18854e63a7f3bf20038356c8/3.1/en-US/api/sap.Push.html

        Regards,
        Dan van Leeuwen

      Author's profile photo Mikhail Groshev
      Mikhail Groshev

      Thank you, Daniel!

       

      Best regards,

      Mikhail