Skip to content

Push Notifications

Setting up FCM Capability for an Application

The SAP BTP SDK for Android (6.0.0 and later) separates Firebase Push Service into an independent module, foundation-push-fcm. To enable FCM for your application, follow the Google documentation at Set Up a Firebase Cloud Messaging Client App on Android and add the foundation-push-fcm module to your project.

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.

Setting up Baidu Push for an Application

The SAP BTP SDK for Android (5.1.0 and later) separates Baidu Push Service into an independent module, foundation-push-baidu. Before you use the Baidu Push Service you should familiarize yourself with the Baidu push development documentation. Additionally, there are some prerequisites that need to be fulfilled:

  • Download the Baidu push SDK from the Baidu website.
  • Follow the tutorial and add the dependency to your project.
  • Follow the tutorial and declare the customized receiver in AndroidManifest.xml.
  • Add the foundation-push-baidu module to your project.
  • Add the BaiduPushService instance with baiduPushApiKey from the Baidu push console.
val services = mutableListOf<MobileService>()
services.add(BaiduPushService().apply { baiduPushApiKey = "YourApiKey" })

Note

Note that the SDK implements FirebaseMessagingService from FCM and PushMessageReceiver from Baidu, so there is no need to implement them manually. If needed, you can override them in AndroidManifest.xml.

Handling Push Notifications Using FirebasePushService and BaiduPushService

The SAP BTP SDK for Android (4.0.0 and later) provides an abstract BasePushService Class that simplifies push utilization. FirebasePushService and BaiduPushService extend BasePushService: FirebasePushService is used for the Google Firebase push service, and BaiduPushService is used for the Baidu push service. To start the push service, add the FirebasePushService or BaiduPushService instance to the SDKInitializer.start method. They will then handle the entire push process, including registration, unregistration, notification arrival, and so on.

//Firebase push service
SDKInitializer.INSTANCE.start(application, new MobileService[]{new FirebasePushService()}, null);
//Baidu push service
SDKInitializer.INSTANCE.start(application, new MobileService[]{new BaiduPushService()}, null);
//Firebase push service
SDKInitializer.start(application, FirebasePushService())
//Baidu push service
SDKInitializer.start(application, BaiduPushService())

Add a PushCallbackListener to Display Notification Messages

Using FirebasePushService and BaiduPushService you can add listeners to receive messages sent from the server. You can also customize the content of the displayed notification messages.

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

Handling Notifications From Foreground and Background

BasePushService provides a new way to display notifications, providing ForegroundNotificationInterceptor and BackgroundNotificationInterceptor to handle notifications from the foreground and background. You need to set EnableAutoMessageHandling to true when initializing FirebasePushService and BaiduPushService. For the foreground notification, use a ForegroundPushNotificationReady handler to decide when the notification should be displayed.

pushService.setEnableAutoMessageHandling(true);
pushService.setBackgroundNotificationInterceptor(pushNotificationEvent -> pushNotificationEvent.displayNotification(pushNotificationEvent.getPushRemoteMessage()));
pushService.setForegroundNotificationInterceptor(pushNotificationEvent -> {
    pushNotificationEvent.displayNotificationWhen(pushNotificationEvent.getPushRemoteMessage(), ()->{
        Activity activity = AppLifecycleCallbackHandler.getInstance().getActivity();
        if (activity == null)
            return false;
        else
            return !activity.getClass().getName().equals("packageName.app.WelcomeActivity");
    });
});
services.add(PushService().apply {
    setPushCallbackListener(FCMPushCallbackListener())
    setPushServiceConfig(configPushServiceConfig())
    isEnableAutoMessageHandling = true
    setBackgroundNotificationInterceptor { pushEvent ->
        pushEvent.displayNotification(pushEvent.pushRemoteMessage)
    }
    setForegroundNotificationInterceptor { pushEvent->
            pushEvent.displayNotificationWhen(pushEvent.pushRemoteMessage, object: ForegroundPushNotificationReady{
                override fun onConditionReady(): Boolean {
                    return  AppLifecycleCallbackHandler.getInstance().activity?.let {
                        it.javaClass.name != "packageName.app.WelcomeActivity"
                    }?:false
                }
            })
        }
    })

BasePushService has predefined some conditions, such as notifications not being displayed for activities that belong to the SDK. You can also add conditions using addForegroundPushNotificationConditions. The following code sample adds the condition that notifications will not be displayed in the launcher activity, for example:

firebasePushService.addForegroundPushNotificationConditions(()->{
    Intent packageIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
    String actName = packageIntent.resolveActivity(getPackageManager()).getClassName();
    return !actName.equals(AppLifecycleCallbackHandler.getInstance().getActivity().getClass().getName());
});
val service = getService(FirebasePushService::class.java.kotlin)
service!!.storeNotificationMessage(true, message, object : ForegroundPushNotificationReady {
    override fun onConditionReady(): Boolean {
        return !AppLifecycleCallbackHandler.getInstance().activity!!.javaClass.name.contains("WelcomeActivity")
    }
})

You can also set customized ForegroundPushNotificationReady for ForegroundNotificationInterceptor. The following sample ensures that notifications from the foreground will not be displayed in packageName.app.WelcomeActivity:

pushService.setForegroundNotificationInterceptor(pushNotificationEvent -> {
    pushNotificationEvent.displayNotificationWhen(pushNotificationEvent.getPushRemoteMessage(), ()->{
        Activity activity = AppLifecycleCallbackHandler.getInstance().getActivity();
        if (activity == null)
            return false;
        else
            return !activity.getClass().getName().equals("packageName.app.WelcomeActivity");
    });
});
services.add(PushService().apply {
    setForegroundNotificationInterceptor { pushEvent->
            pushEvent.displayNotificationWhen(pushEvent.pushRemoteMessage, object: ForegroundPushNotificationReady{
                override fun onConditionReady(): Boolean {
                    return  AppLifecycleCallbackHandler.getInstance().activity?.let {
                        it.javaClass.name != "packageName.app.WelcomeActivity"
                    }?:false
                }
            })
        }
    })

In addition, BasePushService can store the notification when it is not appropriate to display it. When ForegroundPushNotificationReady is satisfied, BasePushService will display the stored notification. For example, the following code shows the scenario where the notification will not be displayed in the activity whose name contains WelcomeActivity. This will be useful when the user handles the notification message sent by the system.

FirebasePushService service = SDKInitializer.INSTANCE.getService(JvmClassMappingKt.getKotlinClass(FirebasePushService.class));
service.storeNotificationMessage(true, message, () -> !AppLifecycleCallbackHandler.getInstance().getActivity().getClass().getName().contains("WelcomeActivity"));
val service = getService(FirebasePushService::class.java.kotlin)
service!!.storeNotificationMessage(true, message, object : ForegroundPushNotificationReady {
    override fun onConditionReady(): Boolean {
        return !AppLifecycleCallbackHandler.getInstance().activity!!.javaClass.name.contains("WelcomeActivity")
    }
})

For data messages that you do not want to be displayed, for example, update operations on certain entities, you can handle them using BackgroundNotificationInterceptor and you do not have to invoke the displayNotification method. BackgroundNotificationInterceptor and ForegroundNotificationInterceptor can be used independently of each other.

Update Badge Number

SAP BTP SDK for Android (5.1.0 and later) exposes a new property named isEnableBadgeMessage in FirebasePushService and BaiduPushService to let end users use a unique notification ID to update the badge. This property needs to be set during the initialization of FirebasePushService and BaiduPushService. If the push request payload contains badge property , then SAP BTP SDK for Android will recognize it as a badge message. When SAP BTP SDK for Android inserts the notification into the system tray, the Android notification ID will always be -1. This means that there will be only one badge message at a time. For the isBadgeMessage and Badge properties of PushRemoteMessage you can also change it in BackgroundNotificationInterceptor and ForegroundNotificationInterceptor, depending on your requirements, before the notification is sent to the system tray. You can set your own badge channel badgeServiceChannel when you initialize the push service. If you do not set a badge channel, the SAP BTP SDK for Android will provide a default channel with the ID, Badge Message Channel, which will only be used by badge messages.

Make Notifications Persistent

By default, notifications will be stored in the cache and will be cleaned when consumed or when the app crashes. In order to avoid lost notifications or collection goals, you can call setPersistedNotification with true then notification will be stored in local database which is provided by SDK. The database is located at: "/data/data/{packagename}/databases/".

Initialize Remote Notification Client Parameters And Update Registration With Parameters

By default, register for push to SAP Mobile Services does not contain remote notification parameters. However, the SAP BTP SDK for Android (5.0.0 and later) provides a new method, initRemoteNotificationParameters, to initialize the register for push parameters. The first time this method is invoked, the SDK will save the parameters into the SharedPreference XML file and also read these parameters from SharedPreference if not null. The purpose of this action is to save the last set of changes to the remote notification parameters. To update the existing parameters stored in SharedPreference, you invoke the updateRegistration method. This generates a new register for push request to mobile services and updates the parameter values in SharedPreference.

Push To Topic

It is important to strive for the right balance of mobile notifications so that users maintain interest and don't ignore important notifications. One way to maintain interest is to group notifications into topics and allow device users to subscribe and unsubscribe to topics. Use the Push to Topic method to send a notification to either all users or a list of users that are subscribed to the topic. The SAP BTP SDK for Android (6.0.0 and later) exposes three push to topic methods:

  • subscribeTopic
  • unsubscribeTopic
  • getAllSubscribedTopics

Note the following:

  • Both FCM and Baidu support push to topic.
  • Push to topic methods only work after a successful onboarding.
  • Push topics are removed after a user switch on a device.

Subscribe to a Topic

Use the BasePushService subscribeTopic method to subscribe to a topic.

FirebasePushService service = SDKInitializer.INSTANCE.getService(JvmClassMappingKt.getKotlinClass(FirebasePushService.class));
service.subscribeTopic("TopicName", new RemoteNotificationClient.CallbackListener() {
        @Override
        public void onSuccess() {
            //TO-DO
        }
        @Override
        public void onError(@NonNull Throwable e) {
            //TO-DO
        }
    });
val service = getService(FirebasePushService::class.java.kotlin)
service.s.subscribeTopic("TopicName", object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //TO-DO
        }
        override fun onError(e: Throwable) {
            //TO-DO
        }
    })

Unsubscribe From a Topic

Use the unsubscribeTopic method to unsubscribe from a topic.

service.unsubscribeTopic("TopicName", new RemoteNotificationClient.CallbackListener() {
        @Override
        public void onSuccess() {
            //TO-DO
        }
        @Override
        public void onError(@NonNull Throwable e) {
            //TO-DO
        }
    });
service.unsubscribeTopic("TopicName", object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //TO-DO
        }
        override fun onError(e: Throwable) {
            //TO-DO
        }
    })

Get All Subscribed Topics

Use the getAllSubscribedTopics method to get all subscribed topics.

service.getAllSubscribedTopics(new RemoteNotificationClient.CallbackListenerWithResult() {
        @Override
        public void onSuccess(@NonNull ServiceResult.SUCCESS<String[]> result) {
            //TO-DO
        }
        @Override
        public void onError(@NonNull Throwable e) {
            //TO-DO
        }
    });
service.getAllSubscribedTopics(object : RemoteNotificationClient.CallbackListenerWithResult {
        override fun onSuccess(result: ServiceResult.SUCCESS<Array<String?>>) {
            //TO-DO
        }
        override fun onError(e: Throwable) {
            //TO-DO
        }
    })

Legacy Register For Push

Registering a Device Token

If you choose to override your own FirebaseMessagingService from FCM or PushMessageReceiver from Baidu, mobile services provides APIs for registering and unregistering device tokens. Device tokens are the UUID codes generated by the push provider. The device token obtained from push infrastructures such as FCM (or Baidu) should be registered with mobile services using the registerDeviceToken method of the RemoteNotificationClient class. The default push provider is FCM. For Baidu push you need to call setPushProvider before registering the token.

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()
    .build();
RemoteNotificationClient remoteNotificationClient = new RemoteNotificationClient();

remoteNotificationClient.registerDeviceToken(token,parameters,
new RemoteNotificationClient.CallbackListener() {
    @Override
    public void onSuccess() {
        //Code for processing a successful response...
    }

    @Override
    public void onError(Throwable result) {
        //Handle failure 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()
    .build()
val remoteNotificationClient = RemoteNotificationClient()

remoteNotificationClient.registerDeviceToken(token, parameters,
    object : RemoteNotificationClient.CallbackListener {
        override fun onSuccess() {
            //Code for processing a successful response...
        }
        override fun onError(result: Throwable) {
            //Handle failure 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() {
            //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() {
            //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 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 two statuses for feedback: NOTIFICATIONSTATUS.RECEIVED 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() {
            //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() {
            //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)
            }
        }
    }
)

Last update: August 1, 2024