Skip to content

Push Notifications

The remote notification client class helps your mobile applications use push notification capabilities provided by SAP Cloud Platform Mobile Services. First, the mobile applications will need to obtain a push token from the FCM infrastructure and register it with the mobile services Push service. Once the Push token is registered, the mobile services administration infrastructure can be used to send the notification to devices.

Setting up FCM Capability for an Application

To enable FCM for your application, follow the Google documentation at Set Up a Firebase Cloud Messaging Client App on Android.

Note

Firebase Cloud Messaging (FCM) is the new version of GCM. It inherits the reliable and scalable GCM infrastructure and provides new features. See the FAQ to learn more. If you are integrating messaging in a new app, start with FCM. GCM users are strongly recommended to upgrade to FCM in order to benefit from new FCM features today and in the future.

You can find how to set up GCM at Set up a Firebase Cloud Messaging client app on Android.

Notifications

Registering a Device Token

Device tokens are the UUID codes generated by the FCM. The device token obtained from push infrastructures such as FCM should be registered with Mobile Services, using the registerDeviceToken method of the RemoteNotificationClient class.

This method must be invoked from the UI thread. It takes the device token and RemoteNotificationParameters as parameters. The callback methods are invoked on the UI thread.

RemoteNotificationParameters parameters = new RemoteNotificationParameters.Builder()
    .lastKnownLocation(new RemoteNotificationParameters.LastKnownLocation("32.80776", "12.456546"))
    .build();
RemoteNotificationClient remoteNotificationClient = new RemoteNotificationClient();

remoteNotificationClient.registerDeviceToken(token,parameters,
new RemoteNotificationClient.CallbackListener() {
    @Override
    public void onSuccess() {
        //Here goes the code for processing successful response...
    }

    @Override
    public void onError(Throwable result) {
        //Handle your failure handling code here...
        if (result instanceof HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                HttpException ne = (HttpException)result;
                Log.e("Http Exception: " , ne.message() + ", with Error code: " + ne.code());
        } else {
                Log.e("Exception occurred: ", result.getMessage());
        }
    }
    }
);
val parameters = RemoteNotificationParameters.Builder()
    .lastKnownLocation(RemoteNotificationParameters.LastKnownLocation("32.80776", "12.456546"))
    .build()
val remoteNotificationClient = RemoteNotificationClient()

remoteNotificationClient.registerDeviceToken(token, parameters,
    object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //Here goes the code for processing successful response...
        }

        override fun onError(result: Throwable) {
            //Handle your failure handling code here...
            if (result is HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                Log.e("Http Exception: ", result.message() + ", with Error code: " + result.code())
            } else {
                Log.e("Exception occurred: ", result.message)
            }
        }
    }
)

Unregistering a Device Token

To disable notifications for mobile applications, delete the device token from the mobile service using the unregisterDeviceToken method. Once the unregistration is done, the server will not be able to send a push notification to the device.

This method must be invoked from the UI thread. The callback methods are invoked on the UI thread.

RemoteNotificationClient remoteNotificationClient = new RemoteNotificationClient();

remoteNotificationClient.unregisterDeviceToken(
    new RemoteNotificationClient.CallbackListener() {
        @Override
        public void onSuccess() {
            //Here goes the code for processing successful response...
        }
        @Override
        public void onError(Throwable result) {
            //Handle error here...
            if (result instanceof HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                HttpException ne = (HttpException)result;
                Log.e("Http Exception: " , ne.message() + ", with Error code: " + ne.code());
            } else {
                Log.e("Exception occurred: ", result.getMessage());
            }
        }
}
);
val remoteNotificationClient = RemoteNotificationClient()

remoteNotificationClient.unregisterDeviceToken(
    object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //Here goes the code for processing successful response...
        }

        override fun onError(result: Throwable) {
            //Handle error here...
            if (result is HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                Log.e("Http Exception: ", result.message() + ", with Error code: " + result.code())
            } else {
                Log.e("Exception occurred: ", result.message)
            }
        }
    }
)

Notification Feedback

SAP Cloud Platform Mobile Services provides a feedback service that a mobile application can use to acknowledge the notification. Every notification message from the service carries a notificationid that uniquely identifies the notification. Using this unique identification value, the updateNotificationStatus method can be used to update the notification status.

The mobile service supports three statuses for feedback: NOTIFICATIONSTATUS.RECEIVED, NOTIFICATIONSTATUS.CONFIRMED and NOTIFICATIONSTATUS.CONSUMED. The code sample below shows how to provide received feedback.

This method must be invoked from the UI thread. It takes the device notification id and SettingsParameters as parameters. The callback methods are invoked on the UI thread.

RemoteNotificationClient remoteNotificationClient = new RemoteNotificationClient();
remoteNotificationClient.updateNotificationStatus(notificationId, RemoteNotificationClient.NOTIFICATIONSTATUS.RECEIVED,
    new RemoteNotificationClient.CallbackListener() {
        @Override
        public void onSuccess() {
            //Here goes the code for processing successful response...
        }

        @Override
        public void onError(Throwable result) {
            //Handle error here...
            if (result instanceof HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                HttpException ne = (HttpException)result;
                Log.e("Http Exception: " , ne.message() + ", with Error code: " + ne.code());
            } else {
                Log.e("Exception occurred: ", result.getMessage());
            }
        }
    }
);
val remoteNotificationClient = RemoteNotificationClient()
remoteNotificationClient.updateNotificationStatus(notificationId, RemoteNotificationClient.NOTIFICATIONSTATUS.RECEIVED,
    object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //Here goes the code for processing successful response...
        }

        override fun onError(result: Throwable) {
            //Handle error here...
            if (result is HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                Log.e("Http Exception: ", result.message() + ", with Error code: " + result.code())
            } else {
                Log.e("Exception occurred: ", result.message)
            }
        }
    }
)

Support for Baidu Push

The remote notification API can be used to support the Baidu push service. Register and unregister the device token by specifying Baidu as the push service using SettingsParameter. The settings parameter should be configured prior to creating a RemoteNotificationClient instance.

The following example shows how to register a Baidu push token to Mobile Services:

RemoteNotificationParameters parameters = new RemoteNotificationParameters.Builder()
    .lastKnownLocation(new RemoteNotificationParameters.LastKnownLocation("32.80776", "12.456546"))
    .build();
//This following line specify that the target we are using is for BAIDU
settingsParameters.setPushService(SettingsParameters.PushService.BAIDU);
RemoteNotificationClient remoteNotificationClient = new RemoteNotificationClient();

remoteNotificationClient.registerDeviceToken(token,parameters,
new RemoteNotificationClient.CallbackListener() {
    @Override
    public void onSuccess() {
        //Here goes the code for processing successful response...
    }

    @Override
    public void onError(Throwable result) {
        //Handle your failure handling code here...
        if (result instanceof HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                HttpException ne = (HttpException)result;
                Log.e("Http Exception: " , ne.message() + ", with Error code: " + ne.code());
        } else {
                Log.e("Exception occurred: ", result.getMessage());
        }
    }
    }
);
val parameters = RemoteNotificationParameters.Builder()
    .lastKnownLocation(RemoteNotificationParameters.LastKnownLocation("32.80776", "12.456546"))
    .build()
//This following line specify that the target we are using is for BAIDU
settingsParameters.pushService = SettingsParameters.PushService.BAIDU
val remoteNotificationClient = RemoteNotificationClient()

remoteNotificationClient.registerDeviceToken(token, parameter,
    object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //Here goes the code for processing successful response...
        }

        override fun onError(result: Throwable) {
            //Handle your failure handling code here...
            if (result is HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                Log.e("Http Exception: ", result.message() + ", with Error code: " + result.code())
            } else {
                Log.e("Exception occurred: ", result.message)
            }
        }
    }
)

The following example shows how to unregister the Baidu push token from Mobile Services:

//This following line specify that the target we are using is for BAIDU
settingsParameters.setPushService(SettingsParameters.PushService.BAIDU);

RemoteNotificationClient remoteNotificationClient = new RemoteNotificationClient();

remoteNotificationClient.unregisterDeviceToken(
    new RemoteNotificationClient.CallbackListener() {
        @Override
        public void onSuccess() {
            //Here goes the code for processing successful response...
        }
        @Override
        public void onError(Throwable result) {
            //Handle error here...
            if (result instanceof HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                HttpException ne = (HttpException)result;
                Log.e("Http Exception: " , ne.message() + ", with Error code: " + ne.code());
            } else {
                Log.e("Exception occurred: ", result.getMessage());
            }
        }
}
);
//This following line specify that the target we are using is for BAIDU
settingsParameters.pushService = SettingsParameters.PushService.BAIDU

val remoteNotificationClient = RemoteNotificationClient()

remoteNotificationClient.unregisterDeviceToken(
    object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //Here goes the code for processing successful response...
        }

        override fun onError(result: Throwable) {
            //Handle error here...
            if (result is HttpException) {
                //HttpException type com.sap.cloud.mobile.foundation.networking.HttpException
                Log.e("Http Exception: ", result.message() + ", with Error code: " + result.code())
            } else {
                Log.e("Exception occurred: ", result.message)
            }
        }
    }
)

Using PushService to Register Push Notifications

The SAP Cloud Platform Mobile Services Android SDK (3.0.0 and later) provides a new PushService API, simplifying push registration. Add the PushService instance to the SDKInitializer.start method to start the push service.

SDKInitializer.INSTANCE.start(application, new MobileService[]{new PushService()}, null);
SDKInitializer.start(application, PushService())

Using PushService to Display Notification Messages

PushService can add a listener to receive messages sent from the server. It can also customize the displayed notification messages.

PushService pushService = new PushService();
pushService.setPushCallbackListener((context, message) -> {
    ...
});
SDKInitializer.INSTANCE.start(application, new MobileService[]{pushService}, null);
val pushService = PushService().apply {
    this.setPushCallbackListener { context, message ->
        ...
    }
}
SDKInitializer.start(application, pushService)

Here is an example of how to implement the listener. The example shows how to handle messages in both foreground and background situations. For the background scenario, the message will toast by Android system notification and it will jump to your application when the message is clicked. In the foreground scenario, it will toast a dialog to notify the message regarding your current activity.

public class FCMPushCallbackListener implements PushCallbackListener {

    @Override
    public void onReceive(Context context, PushRemoteMessage message) {
            // The following section is a sample for handling push messages. This could be customized
            // according to the needs of the concrete application.
            // ---------------------start of suggested customization--------------------------
        String textMsg = context.getResources().getString(com.sap.cloud.mobile.foundation.R.string.push_text);
        String notificationTitle = context.getResources().getString(com.sap.cloud.mobile.foundation.R.string.push_message);
        String notificationID = message.getNotificationID();

            if (message != null) {
                if(message.getTitle() != null && !message.getTitle().isEmpty()){
                    notificationTitle = message.getTitle();
                }else {

                }
                if(message.getAlert() != null && !message.getAlert().isEmpty()){
                    textMsg = message.getAlert();
                }else {

                }

            }
            if (isAppInBackground()) {
                // background
                Intent pushActivityStarter = new Intent(context.getApplicationContext(), PushNotificationActivity.class);
                pushActivityStarter.putExtra(NOTIFICATION_DATA, message);
                pushActivityStarter.putExtra(NOTIFICATION_ID_EXTRA,notificationID);
                NotificationUtilities notUtils = new NotificationUtilities(context.getApplicationContext());
                notUtils.showNotificationMessage(notificationTitle, textMsg, pushActivityStarter);
            } else {
                // foreground
                Activity foregroundActivity = AppLifecycleCallbackHandler.getInstance().getActivity();
                foregroundActivity.getIntent().putExtra(NOTIFICATION_ID_EXTRA, notificationID);
                foregroundActivity.getIntent().putExtra(NOTIFICATION_DATA, message);
                PushNotificationActivity.presentPushMessage(foregroundActivity, notificationID);
            }

    }
    /**
    * Method checks if the app is in background or not
    */
    public boolean isAppInBackground() {
        Lifecycle.State currentState = ProcessLifecycleOwner.get().getLifecycle().getCurrentState();
        return !currentState.isAtLeast(Lifecycle.State.RESUMED);
    }
}
class FCMPushCallbackListener : PushCallbackListener {

    override fun onReceive(context: Context, message: PushRemoteMessage) {
        // The following section is a sample for handling push messages. This could be customized
        // according to the needs of the concrete application.
        // ---------------------start of suggested customization--------------------------
        var textMsg = context.resources.getString(com.sap.cloud.mobile.foundation.R.string.push_text)
        var notificationTitle = context.resources.getString(com.sap.cloud.mobile.foundation.R.string.push_message)
        val notificationID = message.notificationID

        if (message != null) {
            if (message.title != null) {
                notificationTitle = message.title
            } else {

            }
            if (message.alert != null) {
                textMsg = message.alert
            } else {

            }

        }
        if (isAppInBackground) {
            // background
            val pushActivityStarter = Intent(context.applicationContext, PushNotificationActivity::class.java)
            pushActivityStarter.putExtra(NOTIFICATION_DATA, message)
            pushActivityStarter.putExtra(NOTIFICATION_ID_EXTRA, notificationID)
            val notUtils = NotificationUtilities(context.applicationContext)
            notUtils.showNotificationMessage(notificationTitle, textMsg, pushActivityStarter)
        } else {
            // foreground
            val foregroundActivity = AppLifecycleCallbackHandler.getInstance().activity
            foregroundActivity!!.intent.putExtra(NOTIFICATION_ID_EXTRA, notificationID)
            foregroundActivity.intent.putExtra(NOTIFICATION_DATA, message)
            PushNotificationActivity.presentPushMessage(foregroundActivity, notificationID)
        }
    }

    /**
    * Method checks if the app is in background or not
    */
    val isAppInBackground: Boolean
        get() {
            val currentState = ProcessLifecycleOwner.get().lifecycle.currentState
            return !currentState.isAtLeast(Lifecycle.State.RESUMED)
        }

}

NotificationUtilities code example:

public class NotificationUtilities {

    private static final String PUSH_CHANNEL = "my_push_channel";
    public static final String NOTIFICATION_ID_EXTRA = "NotificationID";
    public static final String NOTIFICATION_DATA = "NotificationData";
    private Context context;
    private String negativeButton;

    /**
    * id to handle the notifications in the notification tray.
    * This is declared static intentionally so that increment of this variable is shared with multiple background notifications.
    * Each time a background notification is received, this value will be incremented to keep a unique id for the intents
    */
    private static int notificationIdStart = 100;

    public NotificationUtilities(Context context) {
        this.context = context;
        negativeButton = context.getApplicationContext().getResources().getString(R.string.cancel);
    }

    public void showNotificationMessage(final String title, final String message, Intent intent) {
        // Check for empty push message
        if (TextUtils.isEmpty(message))
            return;

        // status bar icon
        final int smallIcon = R.mipmap.ic_statusbar;

        // notification bar icon
        final int largeIcon = R.mipmap.ic_launcher;
        intent.putExtra(RemoteNotificationConfig.CURRENT_NOTIFICATION_ID, notificationIdStart );
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        final PendingIntent resultPendingIntent =
        PendingIntent.getActivity(
                context,
                notificationIdStart,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT
            );

        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        // versions >= 26 we need to have notification channel, as well
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            // user-visible name of the channel.
            CharSequence name = "push-channel";
            // user-visible description of the channel.
            String description = "notification channel for push messages";
            // importance
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(PUSH_CHANNEL, name, importance);
            notificationChannel.setDescription(description);
            notificationChannel.enableLights(true);
            // sets the notification light color for notifications posted to this
            // channel, if the device supports this feature
            notificationChannel.setLightColor(Color.RED);
            notificationChannel.enableVibration(true);
            notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
            notificationManager.createNotificationChannel(notificationChannel);
        }

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, PUSH_CHANNEL);

        showSmallNotification(notificationBuilder, smallIcon, largeIcon, title, message, resultPendingIntent);
    }


    private void showSmallNotification(NotificationCompat.Builder notificationBuilder, int smallIcon, int largeIcon, String title, String message, PendingIntent resultPendingIntent) {

        NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();

        inboxStyle.addLine(message);

        Intent intentCancel = new Intent(context, PushNotificationActionReceiver.class);
        intentCancel.setAction(negativeButton);
        intentCancel.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intentCancel.putExtra(RemoteNotificationConfig.CURRENT_NOTIFICATION_ID,notificationIdStart );
        //This Intent will be called when Cancel button from notification will be clicked by user.
        PendingIntent pendingIntentCancel = PendingIntent.getBroadcast(context, notificationIdStart, intentCancel, PendingIntent.FLAG_CANCEL_CURRENT);

        Notification notification = notificationBuilder
                .setSmallIcon(smallIcon)
                .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), largeIcon))
                .setTicker(title)
                .setWhen(0)
                .setAutoCancel(true)
                .setContentTitle(title)
                .setContentIntent(resultPendingIntent)
                .setStyle(inboxStyle)
                .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
                .setContentText(message)
                .addAction(R.drawable.ic_close_black_24dp, negativeButton, pendingIntentCancel)
                .build();

        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(notificationIdStart++, notification);
    }

    // Clears notification tray messages
    public static void clearNotifications(Context context) {
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancelAll();
    }
}
class NotificationUtilities(private val context: Context) {
    private val negativeButton: String = context.applicationContext.resources.getString(R.string.cancel)

    fun showNotificationMessage(title: String, message: String, intent: Intent) {
        // Check for empty push message
        if (TextUtils.isEmpty(message))
            return

        // status bar icon
        val smallIcon = R.mipmap.ic_statusbar

        // notification bar icon
        val largeIcon = R.mipmap.ic_launcher
        intent.putExtra(RemoteNotificationConfig.CURRENT_NOTIFICATION_ID, notificationIdStart)
        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
        val resultPendingIntent = PendingIntent.getActivity(
                context,
                notificationIdStart,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT
        )

        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        // versions >= 26 we need to have notification channel, as well
        if (SDK_INT >= O) {

            // user-visible name of the channel.
            val name = "push-channel"
            // user-visible description of the channel.
            val description = "notification channel for push messages"
            // importance
            val importance = NotificationManager.IMPORTANCE_HIGH
            val notificationChannel = NotificationChannel(PUSH_CHANNEL, name, importance)
            notificationChannel.description = description
            notificationChannel.enableLights(true)
            // sets the notification light color for notifications posted to this
            // channel, if the device supports this feature
            notificationChannel.lightColor = Color.RED
            notificationChannel.enableVibration(true)
            notificationChannel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
            notificationManager.createNotificationChannel(notificationChannel)
        }
        val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, PUSH_CHANNEL)
        showSmallNotification(notificationBuilder, smallIcon, largeIcon, title, message, resultPendingIntent)
    }


    private fun showSmallNotification(notificationBuilder: NotificationCompat.Builder, smallIcon: Int, largeIcon: Int, title: String, message: String, resultPendingIntent: PendingIntent) {

        val inboxStyle = NotificationCompat.InboxStyle()

        inboxStyle.addLine(message)

        val intentCancel =  Intent(context, PushNotificationActionReceiver::class.java)
        intentCancel.action = negativeButton
        intentCancel.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        intentCancel.putExtra(RemoteNotificationConfig.CURRENT_NOTIFICATION_ID, notificationIdStart)

        //This Intent will be called when Cancel button from notification will be clicked by user.
        val pendingIntentCancel = PendingIntent.getBroadcast(context, notificationIdStart, intentCancel, PendingIntent.FLAG_CANCEL_CURRENT)

        val notification = notificationBuilder
                .setSmallIcon(smallIcon)
                .setLargeIcon(BitmapFactory.decodeResource(context.resources, largeIcon))
                .setTicker(title)
                .setWhen(0)
                .setAutoCancel(true)
                .setContentTitle(title)
                .setContentIntent(resultPendingIntent)
                .setStyle(inboxStyle)
                .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
                .setContentText(message)
                .addAction(R.drawable.ic_close_black_24dp, negativeButton, pendingIntentCancel)
                .build()

        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(notificationIdStart++, notification)
    }

    companion object {

        private const val PUSH_CHANNEL = "my_push_channel"
        val NOTIFICATION_ID_EXTRA = "NotificationID"
        val NOTIFICATION_DATA = "NotificationData"
        /**
        * id to handle the notifications in the notification tray.
        * This is declared static intentionally so that increment of this variable is shared with multiple background notifications.
        * Each time a background notification is RECEIVED, this value will be incremented to keep a unique id for the intents
        */
        private var notificationIdStart = 100

        // Clears notification tray messages
        fun clearNotifications(context: Context) {
            val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.cancelAll()
        }
    }
}

Using PushService to Unregister Device Tokens

PushService also provides the API to unregister device tokens from the server.

PushService.unregisterPush(new RemoteNotificationClient.CallbackListener() {
            @Override
            public void onSuccess() {
              //handle success message
            }

            @Override
            public void onError(@NonNull Throwable throwable) {
              //handle failed message
            }
        });
PushService.unregisterPush(object : RemoteNotificationClient.CallbackListener {
                override fun onSuccess() {
                  //handle success message
                }
                }

                override fun onError(throwable: Throwable) {
                  //handle failed message
                }
            })

Using PushService for Notification Feedback

The PushService provides a simplified way to manage notifications.

PushService.setPushMessageStatus(notificationId, PushService.NotificationStatus.CONSUMED);
PushService.setPushMessageStatus(notificationId, PushService.NotificationStatus.CONSUMED)

Last update: October 27, 2020