Android Push
Learn how to implement push notifications.
Overview
There are two ways to implement push notifications on a device.
-
Default CleverTap Implementation - This is the easiest way to implement push notifications. For more information, refer to Adding CleverTap's listener service.
-
Custom Android Push - Use it only if you already have your implementation or use another push provider. For more information, refer to Custom Android Push Notifications Handling.
Set up Push Notifications Credentials
Follow these steps to enable push notifications:
- Log in to the CleverTap dashboard and navigate to Settings > Channels > Mobile Push
- Click the FCM credentials section and enter the FCM credentials as shown in the following image:

Enter FCM credentials
- Click Save.

Push Notifications Credentials
You can fetch these FCM credentials from the Firebase Developer Console.
Find FCM Credentials
Firebase Cloud Messaging is a service that allows you to send notifications to your applications and receive information from them. Your FCM Sender ID and API key authenticate against CleverTap, enabling you to send notifications to your users from CleverTap.
To find the FCM credentials:
- Log in to the Firebase Developer Console and go to your Dashboard.
- Click the Gear icon and access Project settings.

Navigate to Firebase Project Settings Page
- Go to the Cloud Messaging tab to copy the sender ID and the API Key.

Copy Sender ID and API Key
Firebase Cloud Messaging (FCM) APIs Update
It is mandatory to migrate from the legacy FCM APIs to the latest FCM APIs as the former will be discontinued starting June 20, 2024.
To ensure uninterrupted delivery of the push notifications, you must migrate to the latest FCM APIs. In accordance with the Firebase deprecation notice about legacy APIs, CleverTap has already migrated to the new FCM APIs.
- To ensure your campaigns are routed through the new APIs, upload the FCM Credential File by clicking Upload FCM Credential File from the Settings > Channels > Mobile Push page of the CleverTap dashboard. You can obtain this file by clicking Generate new private key from the Service Accounts tab of the Project Settings page of the Firebase dashboard.

Generate New Private Key
For more information, refer to Migrate to HTTP v1 API.
Prerequisite
To improve the delivery rate of personalized Push campaigns, ensure that Firebase Cloud Messaging API is enabled. To do so:
- Go to console.cloud.google.com and click API & Services.
- Click Cloud Messaging API link from the left menu.
- Click ENABLE if it is not already enabled.
Push Notifications for Android O
Android has modified sending push notifications to an app in the Oreo version. Starting with CleverTap SDK 3.1.7, we support Android Oreo's latest features like Notification Channels and Notification Badges.
Step 1: Prerequisites
Since Notification channels and badges are the features of the latest Android version, you as a developer will have to update your SDK and Build Tools to the latest versions. Make sure that in your build.gradle file, the compileSdkVersion
is 26(or above) and buildToolsVersion
is 26.0.1 (or above)
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
}
Step 2: Creating Notification Channels
Once the CleverTap Android SDK is integrated successfully, You can create stand-alone notification channels in your app by using the following line of code.
CleverTapAPI.createNotificationChannel(getApplicationContext(),"YourChannelId","Your Channel Name","Your Channel Description",NotificationManager.IMPORTANCE_MAX,true);
// Creating a Notification Channel With Sound Support
CleverTapAPI.createNotificationChannel(getApplicationContext(),"got","Game of Thrones","Game Of Thrones",NotificationManager.IMPORTANCE_MAX,true,"gameofthrones.mp3");
// How to add a sound file to your app : https://developer.clevertap.com/docs/add-a-sound-file-to-your-android-app
CleverTapAPI.createNotificationChannel(getApplicationContext(),"YourChannelId","Your Channel Name","Your Channel Description",NotificationManager.IMPORTANCE_MAX,true)
// Creating a Notification Channel With Sound Support
CleverTapAPI.createNotificationChannel(getApplicationContext(),"got","Game of Thrones","Game Of Thrones",NotificationManager.IMPORTANCE_MAX,true,"gameofthrones.mp3")
// How to add a sound file to your app : https://developer.clevertap.com/docs/add-a-sound-file-to-your-android-app
Step 3(Optional) : Creating Notification Channel Groups
Additionally, you can also create a notification channel group. Notification channel groups allow you to manage multiple notification channels with identical names within a single app, which is helpful if your app supports multiple accounts. You can create a notification group using the following line of code:
CleverTapAPI.createNotificationChannelGroup(getApplicationContext(),"YourGroupId","YourGroupName");
CleverTapAPI.createNotificationChannelGroup(getApplicationContext(), "YourGroupId", "YourGroupName")
Once you have created a notification channel group, you can use the group Id to create notification channels for that specific group. You can create a notification channel specifying the group id using the following line of code.
CleverTapAPI.createNotificationChannel(getApplicationContext(),"YourChannelId","YourChannelName","YourChannelDescription",NotificationManager.IMPORTANCE_MAX,"YourGroupId",true);
CleverTapAPI.createNotificationChannel(getApplicationContext(), "YourChannelId", "YourChannelName", "YourChannelDescription", NotificationManager.IMPORTANCE_MAX, "YourGroupId", true)
You can create more than one channel using the above lines of code. Just make sure that the channel ID differs in every Notification Channel. Also, this Channel ID will be used to send Push Notifications using the CleverTap dashboard.
Step 4: Deleting Notification Channels
You can delete the notification channels created previously in your app. There is no error thrown when you try to delete a notification channel that doesn't exist. You can delete the notification channels using the following line of code.
CleverTapAPI.deleteNotificationChannel(getApplicationContext(),"YourChannelId");
CleverTapAPI.deleteNotificationChannel(getApplicationContext(),"YourChannelId")
Step 5(Optional): Deleting Notification Groups
CleverTap SDK also allows you to remove the notification groups you have created previously. Note that you will need to delete all the channels associated with a group before deleting a group. You can delete a notification group using the following line of code:
CleverTapAPI.deleteNotificationChannelGroup(getApplicationContext(),"YourGroupId");
CleverTapAPI.deleteNotificationChannelGroup(getApplicationContext(),"YourGroupId")
Step 6: Sending Notifications via Dashboard
To send Push Notifications, login to the CleverTap Dashboard, and on the left navigation menu, click on Messages > Campaigns. Then click on the +Campaign button on the top right to select Mobile Push. Now, set up your Push Notification campaign by determining when you want to send out your notification, who you want to send it out to (in this case, it will be Android users), and what kind of push notification you want to send. Once you reach the what section, select the Single Message option. Select Send Message to Android O.
Fill in the Channel with the channel id you used to create the notification channel in your app. This field is mandatory as Android O requires a valid channel to send notifications. Badge Icon can be set as the app icon or the small icon to denote push notifications. The default is the app icon. Badge ID is the number you want to set the Notification Badge with. By default, it auto-increments. Both Badge Icon and Badge ID are optional.

Settings for Android 8.0 and Above
Step 7(Optional): Saving a channel id
You can choose a notification channel ID as optional or mandatory for creating a campaign. Select Add notification channels for Android 8.0 and above option. It is available at Settings>Channel>Mobile Push. You can create a selection list of channel IDs for your messages.

Save the Notification Settings
Step 8(Optional): Adding a custom notification icon
If your app targets devices with Android version Lollipop and above, you can set up a custom notification icon for your notifications. For more information, refer to Set the Small Icon Notification.
Default Notification Channel (Optional)
Starting from core v5.1.0
we have introduced a new feature that allows developers to define a
default notification channel for their app. This feature provides flexibility in handling push
notifications.
Note
Please note that this is only supported for CleverTap core notifications. Support for
push templates will be released soon.
To specify the default notification channel ID, you can add the following metadata to your app's
manifest file:
<meta-data
android:name="CLEVERTAP_DEFAULT_CHANNEL_ID"
android:value="your_default_channel_id" />
By including this metadata, you can define a specific notification channel that CleverTap will use
if the channel provided in the push payload is not registered by your app. This ensures that push
notifications are displayed consistently even if the app's notification channels are not set up.
In case the SDK does not find the default channel ID specified in the manifest or the default channel ID is specified in the manifest but not registered by the app, it will automatically fall back to using a default channel called Miscellaneous. It ensures that push
notifications are still delivered, even if no specific default channel is specified in the manifest.
This enhancement provides developers with greater control over the default notification channel used
by CleverTap for push notifications, ensuring a seamless and customizable user experience.
Add CleverTap's FcmMessageListenerService
Inside the tags, register the following services:
<service
android:name="com.clevertap.android.sdk.pushnotification.fcm.FcmMessageListenerService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
For CleverTap SDK v3.4.3 and above, the CleverTap SDK supports specifying a custom FCM Sender ID. The SDK will request a token with the specified FCM Sender ID if it is present in the AndroidManifest.xml
. If the FCM Sender ID is not available in the AndroidManifest.xml
file, the SDK will request the token in the default manner, which uses the app's google-services.json
file. To use a custom FCM Sender ID, add the following field in your app's AndroidManifest.xml
:
`
The Custom FCM Sender ID feature has been discontinued in the versions of the CleverTap Android SDK after v4.3.1. CleverTap Android SDK only fetches the FCM token from the
google-services.json
file.
Opening a notification using CleverTap’s implementation will cause your app to be launched. If you send a deeplink, the link will be opened.
Add a Sound File to your Android App
Learn how you can add a sound file to your Android app, thereby allowing you to use custom sounds for your Android push notifications.
Android supports .mp3, .ogg and .wav files for playing custom sounds.
Adding Sound Files to Your Android Project
Right-click on Resources (res) folder and select New -> Android Resource Directory.

Add Source Files to Android Project
Select Resource Type as raw.

Select Resource Type-Raw
Put your MP3 file in the raw folder.

Add MP3 File in raw Folder
Registering Notification Channel For the Custom Sound File
// Creating a Notification Channel With Sound Support
CleverTapAPI.createNotificationChannel(getApplicationContext(),"got","Game of Thrones","Game Of Thrones",NotificationManager.IMPORTANCE_MAX,true,"gameofthrones.mp3");
CleverTapAPI.createNotificationChannel(getApplicationContext(),"got","Game of Thrones","Game Of Thrones",NotificationManager.IMPORTANCE_MAX,true,"gameofthrones.mp3")
Creating Push Notifications with Custom Sounds
In the CleverTap dashboard, select Advanced > Custom Sound file and enter the name of your file with the extension in the text box.
Note
If the notification channel has a custom sound, then this sound will always override the default OS sound.

Add Custom Sound File
Note
Modifying Custom Sound only applies to Android 8 and below. Modifying Custom Sound for Android versions higher than Android 8 is based on the notification channel. For more information about setting notification sound in Android, refer to Setting Sound.
Android 13 Changes
Android 13 (API 33) has rolled out new features and API changes. Refer to Android Behavior changes for Apps targeting Android 13.
There are changes for all apps, whether targeting API 33 or just running on Android 13. Some of these changes include runtime notification permission, Foreground Service Task Manager, and Battery Resource Utilization, among others. You can find the complete list on the Android Developer documentation page.
CleverTap Android SDK 4.7.0 supports Android 13
If you plan to increase the target API of your app to 33, you must upgrade the CleverTap Android SDK to version 4.7.0 or higher. The CleverTap SDK 4.7.0 and higher now support Android SDK from API 19 (Android 4.4) and higher.
Push Notification Permission
All apps targeting Android 13 and above must request permission from the user before pushing notifications. All newly-installed apps must get user permission before they can send notifications. This means that you have one chance to ask for permission from the user. However, existing apps will get grandfathered in after a short grace period until the next app launch on Android 13. For more information, refer to Android Developer Documentation - notification permission.
With the latest update, Android 13 allows you to monitor opt-in rates for Push Notifications. To access this feature, ensure you upgrade to SDK version 50100 or higher. As soon as the user grants permission to receive push notifications on the push primer, CleverTap raises an event named Channel Subscribed.
A Push Primer can explain to your users the need for push notifications and help with your engagement rates. It is an InApp notification that you can show to your users before requesting notification permission. It helps with the following :
- Allows you to educate your users on why you are asking for this permission before invoking a system dialog that asks the user to allow/deny this permission.
- Acts as a precursor to the hard system dialog and thus allows you to ask for permission multiple times if previously denied, without making your users search the device settings.
There are 3 ways to handle the new push notification changes:
- Use a push primer using Half-Interstitial InApp notification

Half Interstitial InApp Notification
- Use a push primer using In-App alert

In App Alert Notification

Push Notification without Push Primer
Invoke the Android Permission Dialog (without push primer)
ThepromptForPushPermission(boolean)
method is a public method that can be used to request permission on behalf of your application directly. If showFallbackSettings
is true then it shows an alert dialog that routes to the app's notification settings page.
cleverTapAPI.promptForPushPermission(true) //Takes boolean as a parameter. If true and the permission is denied then we fallback to app’s notification settings, if it’s false then we just throw a verbose log saying permission is denied.
cleverTapAPI.promptForPushPermission(true) // Takes boolean as a parameter. If true and the permission is denied then we fallback to app’s notification settings, if it’s false then we just throw a verbose log saying permission is denied.
Check the status of push permission
The isPushPermissionGranted
method can be used to check the status of the push notification permission for your application.
cleverTapAPI.isPushPermissionGranted() // Returns true if permission is granted, else returns false if permission is denied.
cleverTapAPI.isPushPermissionGranted // Returns true if permission is granted, else returns false if permission is denied.
Invoke the Push Primer flow
promptPushPrimer(JSONObject)
- Calls the push primer flow for Android 13 and above devices.
cleverTapAPI.promptPushPrimer(jsonObject);
Available Callbacks for Push Primer
Based on notification permission grant/deny, we’ll be providing a callback on PushPermissionResponse
(boolean).
-
Register
PushPermissionResponseListener
instance to receive the Push Primer result:
TheCleverTapAPI
class exposes aregisterPushPermissionNotificationResponseListener(PushPermissionResponseListener)
method. Use this method to register yourPushPermissionResponseListener
instance. EachPushPermissionResponseListener
instance passed in this method is added to aList
of thePushPermissionResponseListener
type and the Push Primer result is notified to all the elements of that list. -
Unregister
PushPermissionResponseListener
instance to stop receiving the Push Primer result:
TheCleverTapAPI
class exposes aunregisterPushPermissionNotificationResponseListener(PushPermissionResponseListener)
method.
Use this method to unregister your previously registeredPushPermissionResponseListener
instance.
Note
Call
registerPushPermissionNotificationResponseListener(PushPermissionResponseListener)
only from theonCreate()
method of the Activity/Fragment and unregister the listener instance from theonDestroy()
method usingunregisterPushPermissionNotificationResponseListener(PushPermissionResponseListener)
.
Below is a sample implementation to get the permission result:
public class MainActivity extends AppCompatActivity implements PushPermissionResponseListener {
private CleverTapAPI cleverTapDefaultInstance;
@Override
protected void onCreate(Bundle savedInstanceState) {
cleverTapDefaultInstance = CleverTapAPI.getDefaultInstance(this);
if (cleverTapDefaultInstance != null) {
cleverTapDefaultInstance.registerPushPermissionNotificationResponseListener(this);
}
}
@Override
public void onPushPermissionResponse(boolean accepted) {
Log.i(TAG, "onPushPermissionResponse : InApp---> response() called accepted="+accepted);
if(accepted){
CleverTapAPI.createNotificationChannel(getApplicationContext(), "BRTesting", "Testing Channel",
"Testing Channel for BR", NotificationManager.IMPORTANCE_HIGH, true);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (cleverTapDefaultInstance != null) {
cleverTapDefaultInstance.unregisterPushPermissionNotificationResponseListener(this);
}
}
}
class MainActivity : AppCompatActivity(), PushPermissionResponseListener {
var cleverTapDefaultInstance: CleverTapAPI? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
cleverTapDefaultInstance = CleverTapAPI.getDefaultInstance(this)
cleverTapDefaultInstance?.registerPushPermissionNotificationResponseListener(this)
}
override fun onPushPermissionResponse(accepted: Boolean) {
Log.i(TAG, "onPushPermissionResponse : InApp---> response() called accepted=$accepted")
if (accepted) {
CleverTapAPI.createNotificationChannel(getApplicationContext(), "BRTesting", "Testing Channel",
"Testing Channel for BR", NotificationManager.IMPORTANCE_HIGH, true);
}
}
override fun onDestroy() {
super.onDestroy()
cleverTapDefaultInstance?.unregisterPushPermissionNotificationResponseListener(this)
}
}
New method for InAppNotificationListener
Use the following method from CleverTap SDK version 4.7.0 and above.
`@Override
public void onShow(CTInAppNotification ctInAppNotification) {}`
Android 12 Changes
Notification Trampolines
The system prevents components that start other activities inside services or broadcast receivers when a notification is opened. The system will prevent the activity from starting if this requirement is not met. The most prominent one is restrictions on Notification trampoline.
SDK Changes
The new restrictions on Notification Trampolines have changed how CleverTap handles some actions in SDK version 4.3.0. The most important change is the handling of push notifications.
Notification Body Click Handling
The push notification body is handled differently after the Android 12 update.
Android 11 and Lower
The PendingIntent
is a unique instance that the CleverTap SDK creates inside each push notification. The Android OS identifies this instance to open the CTPushNotificationReceiver
broadcast receiver. After opening the broadcast receiver, the CleverTap SDK completes the processing, such as starting the activity/deep-link and tracking the Notification Clicked event, and so on.
Android 12 and Above
Android has restricted the usage of Notification Trampolines. The push notification must start the activity directly after the notification tap. It means that the broadcast receiver or service is now redundant for opening the activity.
-
Automatic Handling by CleverTap SDK - When there is no target activity in the activity stack, the CleverTap SDK uses the
onActivityCreated()
method fromcom.clevertap.android.sdk.ActivityLifecycleCallback
class to perform the following:- Raise the Notification Clicked event
- Provide a callback to the application
-
Manual Handling by the Application - If a target activity is available in the activity stack, then you must handle these actions manually. For example, the payment screen is visible to the user, and the target activity of the deeplink is the payment screen. The
onNewIntent()
method can process the Notification Clicked event and provide the callback to the application using theclevertapApi.pushNotificationClickedEvent()
method.
The following is an example of how you can set up actions in your application. Set up the automatic handling followed by the manual handling.
- For automatic handling, register the activity lifecycle callback using the
ActivityLifecycleCallback.register(this
method. - For manual handling, override the
onNewIntent()
method in all your activities or the base activity and call theclevertapApi.pushNotificationClickedEvent()
.
class HomeScreenActivity : AppCompatActivity(){
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// On Android 12, Raise notification clicked event when Activity is already running in activity backstack
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
cleverTapDefaultInstance?.pushNotificationClickedEvent(intent!!.extras)
}
}
}
class HomeScreenActivity extends AppCompatActivity {
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
/**
* On Android 12, Raise notification clicked event when Activity is already running in activity backstack
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
cleverTapDefaultInstance.pushNotificationClickedEvent(intent.getExtras());
}
}
}
The tables below show Notification Clicked handling in Android 12.
- Without a deeplink or with an internal deeplink:
Application Running Status | Activity in Backstack | Activity not in Backstack |
---|---|---|
Foreground/Background | Call the clevertapApi.pushNotificationClickedEvent() method to raise notification clicks and callback from the onNewIntent callback. | The onActivityCreated callback from the com.clevertap.android.sdk.ActivityLifecycleCallback class raises the Notification Clicked event and provides a callback to the application. |
Killed | Not Applicable. | The onActivityCreated callback from the com.clevertap.android.sdk.ActivityLifecycleCallback class raises the Notification Clicked event and provides a callback to the application. |
- With an external deeplink:
Application Running Status | Activity in Backstack | Activity not in Backstack |
---|---|---|
Foreground/Background | No Notification Clicks tracked. | No Notification Clicks tracked. |
Killed | Not Applicable | No Notification Clicks tracked. |
Tracking Deeplinks
The notification trampoline restrictions mean that we cannot track the Notification Clicked events when the deeplinks point to a third-party application.
Notification CTA Click Handling
The handling for Push Notifications with an action button is the same as the handling for notifications body click. Additionally, when the Notification Clicked event is triggered, the event properties indicate the exact button click.
Android 11 and Lower
The PendingIntent
is a unique instance that the CleverTap SDK creates inside each push notification. The Android OS identifies this instance to open the CTPushNotificationReceiver
broadcast receiver. After opening the broadcast receiver, the CleverTap SDK completes the processing, such as starting the activity/deep-link and tracking the Notification Clicked event, and so on.
Android 12 and Above
Android has restricted the usage of Notification Trampolines in all OS versions above version 12. The push notification must start the activity directly after the notification tap. It means that the broadcast receiver or service is now redundant for opening the activity.
-
Automatic Handling by CleverTap SDK - When there is no target activity in the activity stack, the CleverTap SDK uses the
onActivityCreated()
method from thecom.clevertap.android.sdk.ActivityLifecycleCallback
class to perform the following: -
Raise the Notification Clicked event
-
Provide a callback to the application
-
Manual Handling by the Application - If a target activity is available in the activity stack, then you must handle these actions manually. For example, the payment screen is visible to the user, and the target activity of deeplink is the payment screen. The
onNewIntent()
method can process the Notification Clicked event and provide the callback to the application using theclevertapApi.pushNotificationClickedEvent()
method.
The following is an example of how you can set up actions in your application. Set up the automatic handling followed by the manual handling.
- For automatic handling, register the activity lifecycle callback using the
ActivityLifecycleCallback.register(this)
method. - For manual handling, override the
onNewIntent()
method in all your activities or the base activity and call theclevertapApi.pushNotificationClickedEvent()
method.
class HomeScreenActivity : AppCompatActivity(){
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// On Android 12, Raise notification clicked event when Activity is already running in activity backstack
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
cleverTapDefaultInstance?.pushNotificationClickedEvent(intent!!.extras)
}
}
}
class HomeScreenActivity extends AppCompatActivity {
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
/**
* On Android 12, Raise notification clicked event when Activity is already running in activity backstack
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
cleverTapDefaultInstance.pushNotificationClickedEvent(intent.getExtras());
}
}
}
The tables below show Notification Clicked handling in Android 12.
- Without a deeplink or with an internal deeplink:
Application Running Status | Activity in Backstack | Activity not in Backstack |
---|---|---|
Foreground/Background | Call the clevertapApi.pushNotificationClickedEvent() method to raise notification clicks and callback from the onNewIntent callback. | The onActivityCreated callback from the com.clevertap.android.sdk.ActivityLifecycleCallback class raises the Notification Clicked event and provides a callback to the application. |
Killed | Not Applicable. | The onActivityCreated callback from the com.clevertap.android.sdk.ActivityLifecycleCallback class raises the Notification Clicked event and provides a callback to the application. |
- With an external deeplink:
Application Running Status | Activity in Backstack | Activity not in Backstack |
---|---|---|
Foreground/Background | No Notification Clicks tracked. | No Notification Clicks tracked. |
Killed | Not Applicable. | No Notification Clicks tracked. |
Tracking Deeplinks
The notification trampoline restrictions mean that we cannot track the Notification Clicked events when the deeplinks point to a third-party application.
Clear Notification on CTA Click
The clearing of notifications on CTA click is handled differently after the Android 12 update. The application must now handle these actions.
Android 11 and Lower
The PendingIntent
is a unique instance that the CleverTap SDK creates inside each push notification. The Android OS identifies this instance to open the CTNotificationIntentService
intent service. The CleverTap SDK then clears the notification.
Android 12 and Above
Android has restricted the usage of Notification Trampolines in all OS versions above version 12. The push notification must start the activity directly after the notification tap. It means that the broadcast receiver or service is now redundant for opening the activity.
-
Automatic handling by CleverTap SDK - This handling is not applicable for clearing a notification.
-
Manual handling by the application - There are two cases of manual handling for clearing notifications:
-
Target activity available in the activity stack - You must handle these actions manually. For example, the payment screen is visible to the user, and the target activity of deeplink is the payment screen. The
onNewIntent()
method can process the clearing of notifications. -
Target activity not available in the activity stack - Register the activity lifecycle callbacks of the
android.app.Application.ActivityLifecycleCallbacks
class to clear the notification in theonActivityCreated
method.
The following is an example of how you can clear notifications in your application:
class HomeScreenActivity : AppCompatActivity(){
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// On Android 12, clear notification on CTA click when Activity is already running in activity backstack
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
NotificationUtils.dismissNotification(intent, applicationContext)
}
}
}
class MyApplication : MultiDexApplication(),
ActivityLifecycleCallbacks {
override fun onCreate() {
registerActivityLifecycleCallbacks(this)
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
// On Android 12, clear notification on CTA click when Activity is already running in activity backstack
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
NotificationUtils.dismissNotification(intent, applicationContext)
}
}
}
object NotificationUtils {
//Require to close notification on action button click
fun dismissNotification(intent: Intent?, applicationContext: Context){
intent?.extras?.apply {
var autoCancel = true
var notificationId = -1
getString("actionId")?.let {
Log.d("ACTION_ID", it)
autoCancel = getBoolean("autoCancel", true)
notificationId = getInt("notificationId", -1)
}
/**
* If using InputBox template, add ptDismissOnClick flag to not dismiss notification
* if pt_dismiss_on_click is false in InputBox template payload. Alternatively if normal
* notification is raised then we dismiss notification.
*/
val ptDismissOnClick = intent.extras!!.getString(PTConstants.PT_DISMISS_ON_CLICK,"")
if (autoCancel && notificationId > -1 && ptDismissOnClick.isNullOrEmpty()) {
val notifyMgr: NotificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notifyMgr.cancel(notificationId)
}
}
}
}
class HomeScreenActivity extends AppCompatActivity {
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
/**
* On Android 12, clear notification on CTA click when Activity is already running in activity backstack
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
NotificationUtils.dismissNotification(intent, applicationContext);
}
}
}
class MyApplication extends MultiDexApplication implements ActivityLifecycleCallbacks {
@Override
public void onCreate(){
registerActivityLifecycleCallbacks(this);
}
@Override
public void onActivityCreated(@NonNull final Activity activity, @Nullable final Bundle savedInstanceState) {
/**
* On Android 12, clear notification on CTA click when Activity is already running in activity backstack
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
NotificationUtils.dismissNotification(intent, applicationContext);
}
}
}
public class NotificationUtils {
//Require to close notification on action button click
public static void dismissNotification( Intent intent, Context applicationContext){
Bundle extras = intent.getExtras();
if (extras != null) {
String actionId = extras.getString("actionId");
if (actionId != null) {
boolean autoCancel = extras.getBoolean("autoCancel", true);
int notificationId = extras.getInt("notificationId", -1);
if (autoCancel && notificationId > -1) {
NotificationManager notifyMgr =
(NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
notifyMgr.cancel(notificationId); }
}
}
}
}
Other Android 12 Changes
Pending Intents Mutability
To improve the security of your app, you must specify the mutability of each PendingIntent
object that your app creates. CleverTap Android SDK 4.3.0 complies with this.
Safer Component Exporting
You must explicitly declare the android:exported
attribute if your app contains activities, services, or broadcast receivers that use intent filters. Refer to Safer Component Exporting. CleverTap Android SDK 4.3.0 complies with this.
Custom Notifications
To streamline User Experience when apps use entirely Custom Notification layouts, Android 12 will prevent such notifications from using the whole notification area.
Android 12 introduces a standard template for notifications, ensuring the same decoration for all notifications. It has a fixed header with the app name and a consistent expand/collapse appearance.
Detailed documentation and illustration are available in the Android user experience documentation.
CleverTap Push Templates SDK will be released soon, supporting Android 12 changes related to Custom Notifications.
Set the Small Notification Icon
The small icon is a notification bar icon displayed in the push notifications, as shown in the image below:

Set the Small Notification Icon
With the launch of Android 5 (Lollipop), the notification icons are rendered differently in the notification bar at the top. By default, our SDK uses the app's icon for both the notification icon and the notification bar icon; however, since Android 5, all non-alpha channels are ignored while drawing the main notification icon. For more information, refer to Material design style in the Android documentation.
The small icon must be an alpha only image, which means the icon must have only one color, and the rest must be transparent. The alpha only icon image combines the colored and transparent regions.
In general, the logo displayed in the icon has a single color, and the background is transparent. The alpha channel provides the ability to embed transparency within an image. This can be accomplished with tools such as Adobe Photoshop, Illustrator, and so on. You can also change the colored region of the small icon from the CleverTap dashboard.
The CleverTap SDK fills the transparent section of the small icon with the selected color:

Set Small App Icon Color
Following is a small icon sample:

Example of a Small Icon
After creating an icon with the alpha channel, you can use it for your project in Android Studio. Save the icon file in the Drawable folder of your project.
To set a custom notification icon (only for the small icon), add the following metadata entry in your AndroidManifest.xml.
<meta-data
android:name="CLEVERTAP_NOTIFICATION_ICON"
android:value="ic_stat_red_star"/> <!-- name of your file in the drawable directory without the file extension. -->
Warning
Use only the file name. Do not use the path such as @drawable/ic_stat_red_star .
Push Notification Callback
The CleverTap Android SDK v3.8.1 SDK gives you a callback when a Push Notification is clicked along with the entire payload. Implement the CTPushNotificationListener
in your Application class or Main Activity to receive this payload.
To set the listener, use the following code -
CleverTapAPI cleverTapAPI = CleverTapAPI.getDefaultInstance(getApplicationContext());
cleverTapAPI.setCTPushNotificationListener(this);
CleverTapAPI.getDefaultInstance(applicationContext)?.apply {
ctPushNotificationListener = this@YourAndroidActivity
}
Implement the following override method to receive the payload as a Map on the click of a notification -
@Override
public void onNotificationClickedPayloadReceived(HashMap<String, Object> payload) {
//Use your custom logic for the payload
}
override fun onNotificationClickedPayloadReceived(payload: HashMap<String, Any>?) {
//Use your custom logic for the payload
}
Advanced Android Push Notification Options
Apart from the Title and Message, you have the below-mentioned options to add to your Android push notification. Note that each of these is optional.
Image URL
If an image link is specified, a large image is added to your push notification.
- Recommended resolution: 600×300
- Max size: 40 kb
- Supported file formats are .jpg and .png
Large Icon URL
If a large icon link is specified, the large icon will be appended to the push notification. The large icon will be displayed either far left or far right based on the device.
- Max resolution – 72×72
- Max size – 1 kb
Deeplink/External URL
A deeplink helps you open a particular activity in your app after a click on the notification. If left empty, the notification on click will open the launcher activity of the app.
To use external URLs, whitelist the IPs or prefix the URL with http or https. For more information, refer to Deeplinking.
Action Buttons
You can add up to three call to action buttons for every push notification. For every action button, you have the following options:
- Title: contains the Call to Action text (mandatory)
- Deeplink: the deeplink that should open on click of that button
- Action ID: a user-defined string (applicable to apps custom handling their android push notifications: This string will be available as an extra on the notification click intent for you to identify the action button clicked). This is a mandatory field.
- Icon Resource Identifier: A drawable icon in your app’s resources folder to display the icon along with the notification for Android devices below OS version Nougat. Android Nougat does not display icons by default to allow more buttons.
If the user clicks on the main body of the notification, the app will open, and the notification will disappear. If the user clicks on one of the action buttons, then by default, Android will not remove the notification from the tray. We provide two user options for this.
The first option is to handle closing the notification yourself (applies to apps that are custom handling the push notification). The click intent has the notification Id in its extras to accomplish this. So to close, add the following code in the activity that would get called.
Bundle extras = intent.getExtras();
if (extras != null) {
String actionId = extras.getString("actionId");
if (actionId != null) {
Log.d("ACTION_ID", actionId);
boolean autoCancel = extras.getBoolean("autoCancel", true);
int notificationId = extras.getInt("notificationId", -1);
if (autoCancel && notificationId > -1) {
NotificationManager notifyMgr =
(NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notifyMgr.cancel(notificationId); // the bit that cancels the notification
}
Toast.makeText(getBaseContext(),"Action ID is: "+actionId,
Toast.LENGTH_SHORT).show();
}
}
intent.extras?.apply {
getString("actionId")?.let {
Log.d("ACTION_ID", it)
val autoCancel = getBoolean("autoCancel", true)
val notificationId = getInt("notificationId", -1)
if (autoCancel && notificationId > -1) {
val notifyMgr: NotificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notifyMgr.cancel(notificationId) // the bit that cancels the notification
}
Toast.makeText(baseContext, "Action ID is: $it", Toast.LENGTH_SHORT).show()
}
}
The second option is that CleverTap handles closing the notification. You will have to add CleverTap IntentService to your Manifest.xml, and the SDK will do it for you automatically.
<service
android:name="com.clevertap.android.sdk.pushnotification.CTNotificationIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.clevertap.PUSH_EVENT" />
</intent-filter>
</service>
Notification Action Buttons
Sound Files
You can choose to have no notification sound, the default OS sound, or use a custom sound. It has to be present in the resources folder of your app. refer to the Android sound guide to learn how to add a sound file to your android app. Android only supports .mp3, .ogg and .wav files. Refer to the section Add sound file to your Android app.
Notification Tray Priority
It is the relative priority for this notification in the device tray. Priority indicates how much of the user’s valuable attention must be consumed by this notification.
- MAXIMUM: Use for critical and urgent notifications that alert the user to a time-critical condition or need to be resolved before continuing with a particular task. A notification with priority set to the maximum will be a heads up notification. It will always be at the top of the notification tray.
- HIGH: Use primarily for important communication, such as message or chat events with exciting content for the user. High-priority notifications trigger the heads-up notification display. A notification with priority set to high will be a heads up notification.
- DEFAULT: Use for all notifications that do not fall into any of the other priorities. A notification with default priority will show up in the notification tray, and its order in the notification tray is subject to the presence of other notifications.
Note
Notification tray priority is only applicable for Android 7.1 and below. For Android 8 and above, the notification tray priority is based on the notification channel.
Notification Delivery Priority
You have two options for assigning delivery priority to downstream messages on Android: normal and high priority.
Normal priority - This is the default priority for messages on Android. Normal priority messages are delivered immediately when the app is in the foreground. When the device is in Doze, the delivery may be delayed to conserve battery. For less time-sensitive messages, such as notifications of new email, keeping your UI in sync, or syncing app data in the background, choose normal delivery priority.
High priority - FCM attempts to deliver high priority messages immediately, allowing the FCM service to wake a sleeping device when necessary and to run some limited processing (including very limited network access). High priority messages generally should result in user interaction with your app or its notifications. If FCM detects a pattern in which they don't, your messages may be de-prioritized.
Push Impressions
You can raise and record push notifications delivered onto your users’ Android devices.
- Navigate to Settings > Schema > Events.
- Search for Push Impressions, then click on the vertical ellipsis.

Push Impressions
- Click on Setup push impressions. A new window displays.
- Click on Save to enable the option.
If you are custom-handling push notifications, add the following code after you render the push notifications:
CleverTapAPI.getDefaultInstance(this).pushNotificationViewedEvent(extras);
CleverTapAPI.getDefaultInstance(this@YourAndroidActivity)?.pushNotificationViewedEvent(extras)
Custom Android Push Notification Handling
Step 1. Using Your Custom Implementation
If you have your custom implementation for managing Android push notifications, you can inform CleverTap about the user’s FCM registration ID.
Follow the instructions below if you are using FCM.
String fcmRegId = FirebaseInstanceId.getInstance().getToken();
clevertapDefaultInstance.pushFcmRegistrationId(fcmRegId,true);
val fcmRegId = FirebaseInstanceId.getInstance().token
clevertapDefaultInstance?.pushFcmRegistrationId(fcmRegId, true)
Step 2: Using Multiple Android Push Notification Providers
CleverTap plays well with other Android push notification providers. You can configure your app to work with multiple push notification providers.
If you are using FCM, please follow these instructions:
- Subclass
FirebaseMessagingService
as shown below:
public class MyFcmMessageListenerService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage message){
try {
if (message.getData().size() > 0) {
Bundle extras = new Bundle();
for (Map.Entry<String, String> entry : message.getData().entrySet()) {
extras.putString(entry.getKey(), entry.getValue());
}
NotificationInfo info = CleverTapAPI.getNotificationInfo(extras);
if (info.fromCleverTap) {
CleverTapAPI.createNotification(getApplicationContext(), extras);
} else {
// not from CleverTap handle yourself or pass to another provider
}
}
} catch (Throwable t) {
Log.d("MYFCMLIST", "Error parsing FCM message", t);
}
}
}
class MyFcmMessageListenerService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
message.data.apply {
try {
if (size > 0) {
val extras = Bundle()
for ((key, value) in this) {
extras.putString(key, value)
}
val info = CleverTapAPI.getNotificationInfo(extras)
if (info.fromCleverTap) {
CleverTapAPI.createNotification(applicationContext, extras)
} else {
// not from CleverTap handle yourself or pass to another provider
}
}
} catch (t: Throwable) {
Log.d("MYFCMLIST", "Error parsing FCM message", t)
}
}
}
}
If you are using CleverTap Android SDK v4.4.0 and above, you can add the following code instead of the above code:
public class MyFcmMessageListenerService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage message){
CTFcmMessageHandler()
.createNotification(getApplicationContext(), message);
}
}
class MyFcmMessageListenerService : FirebaseMessagingService() {
@Override
public void onMessageReceived(RemoteMessage message){
CTFcmMessageHandler()
.createNotification(applicationContext, message)
}
}
Note
From CleverTap Android SDK v5.1.0 onwards, the following API now runs on the caller's thread. Ensure to call it in
onMessageReceive()
of messaging service:
CTFcmMessageHandler().createNotification(getApplicationContext(), message)
CleverTapAPI.createNotification(getApplicationContext(),extras);
- Add your service to the
AndroidManifest.xml
in place of the CleverTapFcmMessageListenerService
.
<service
android:name="com.your.package.MyFcmMessageListenerService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
-
Replace
com.your.package
with the correct package name which contains the newly created class,MyFcmMessageListenerService
. -
To track the push notification events and deeplinks add the following receiver in your
AndroidManifest.xml
:
<receiver
android:name="com.clevertap.android.sdk.pushnotification.CTPushNotificationReceiver"
android:exported="false"
android:enabled="true">
</receiver>
Step 3: Structure of CleverTap FCM Payload
Here is the structure of the payload with a CleverTap FCM push notification.
Key | Type | Description |
---|---|---|
wzrk_pn | N/A | If present, this notification is sent from CleverTap. |
wzrk_id | string | Open rate tracking ID (can be empty or it might not be present). |
wzrk_bp | string | If present, the value will be a URL of an image, that needs to be displayed in the notification. |
wzrk_sound | boolean or string | If present, it signifies that the default or custom Android notification sound must be played. |
nt | string | Notification Title, if absent or empty, fallback to the app name. |
nm | string | Notification Body, if absent or empty, ignore this notification. |
wzrk_dl | string | If present, this is a deeplink that must be followed at the time of notification open. |
wzrk_d | N/A | If present, ignore this notification. |
ico | string | If present and not empty, it contains the URL of an image, that must be used as the small notification icon. |
wzrk_pivot | string | If present and not empty, it signifies the type of variant in A/B campaigns. |
wzrk_rnv | string | If present and not empty, it will raise Push Impressions event |
wzrk_nms | string | If present and not empty, it contains the summary text to be displayed along with the image. |
wzrk_st | string | If present and not empty, it contains the subtitle text which is displayed next to the App name. |
wzrk_clr | string | If present and not empty, it contains the Hex value of the colour to be applied on the small icon (and app name for Android versions below Pie) |
If the field nm is empty, ignore the notification. CleverTap sends out a dummy notification with the nm field empty to test for app uninstalls. In addition to the above, attach all keys starting with wzrk_ to your notification extras (For example, wzrk_id is used to track notification opens).
Enable RenderMax with Android
Starting from CleverTap Core SDK version 5.1.0, the RenderMax Push SDK functionality is now supported directly within the CleverTap Core SDK. We strongly recommend updating to CleverTap Core SDK 5.1.0 to take advantage of this integration and to ensure stability.
Please remove the integrated Rendermax SDK before you upgrade to Android SDK v5.1.0
Custom Rendering with RenderMax Push SDK
If the app is custom rendering the push notification and not passing the payload to CleverTap SDK, add the following code before you render the notification:
CleverTapAPI.processPushNotification(getApplicationContext(),extras);
To implement custom handling with RenderMax Push SDK, please note the following updates:
Handling APIs
In CleverTap Core SDK 5.1.0 and above, the following APIs now run on the caller's thread. Make sure to call it in
onMessageReceive()
of messaging service:
CTFcmMessageHandler().createNotification(getApplicationContext(), message)
CleverTapAPI.createNotification(getApplicationContext(), extras)
CTXiaomiMessageHandler().createNotification(getApplicationContext(), message)
Ensure that
CTHmsMessageHandler().createNotification(getApplicationContext(), message)
API is always called on a background thread.
Please ensure that these APIs are called first thing within the onMessageRecieved
callback of your Firebase Messaging Service. For example,
public class MyFcmMessageListenerService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage message){
try {
if (message.getData().size() > 0) {
Bundle extras = new Bundle();
for (Map.Entry<String, String> entry : message.getData().entrySet()) {
extras.putString(entry.getKey(), entry.getValue());
}
NotificationInfo info = CleverTapAPI.getNotificationInfo(extras);
// Do not do any network/db IO before this
if (info.fromCleverTap) {
CleverTapAPI.createNotification(getApplicationContext(), extras);
// Do any other work
} else {
// not from CleverTap handle yourself or pass to another provider
}
// Do any other work
}
} catch (Throwable t) {
Log.d("MYFCMLIST", "Error parsing FCM message", t);
}
}
}
If you are custom rendering the notification:
- Notification should render within 9 seconds of receiving the payload.
- All network IO operations should have a timeout in such a way that the 9-second limit is not breached.
- The CleverTap SDK should be notified to raise the Push Impression event on the same thread and within 9 seconds of receiving the notification.
By following these guidelines, you can ensure a smooth integration of the RenderMax Push SDK with your Android app, leveraging the enhanced capabilities provided by CleverTap Core SDK 5.1.0.
In addition to the RenderMax Push SDK integration, we have introduced two new APIs in CleverTapAPI that help custom rendering.
Below are the details of the new APIs:
This API allows you to retrieve a notification bitmap from the specified bitmapSrcUrl
with a specified timeout and size. In case the bitmap retrieval fails, you can choose to fallback to the app icon by setting the fallbackToAppIcon
parameter. This API provides more control over the bitmap retrieval process for custom rendering.
@Override
public void onMessageReceived(RemoteMessage message) {
Bundle messageBundle = mParser.toBundle(message);
// this method must be called on background thread
// context, messageBundle must be non null.
// timeout must be in range of 1 - 20000 millis.
Bitmap notificationBitmap = CleverTapAPI.getNotificationBitmapWithTimeout(
context,messageBundle, "https://www.pushicons.com/icon",
true, 5000);
// Pass the retrieved bitmap to your notification builder.
}
Below API extends the functionality of the previous one by additionally allowing you to specify the desired size in bytes for the retrieved bitmap.
@Override
public void onMessageReceived(RemoteMessage message) {
Bundle messageBundle = mParser.toBundle(message);
// this method must be called on background thread
// context, messageBundle must be non null.
// timeout must be in range of 1 - 20000 millis and size must be greater than 0.
CleverTapAPI.getNotificationBitmapWithTimeoutAndSize(
context,messageBundle, "https://www.pushicons.com/icon",
true, 5000,1024);
}
This is useful when you need to limit the size of the bitmap to optimize memory usage.
By utilizing these new APIs, you can enhance the push delivery experience for custom rendering and ensure efficient handling of notification bitmaps in your Android app.
Security Concerns for Broadcast Receiver
The Broadcast receiver, as a part of the com.clevertap.android.sdk.pushnotification.fcm package, is the Broadcast Receiver used by our RenderMax technology. It complies with Android rules and causes no security concerns because the intent will only be passed to the App’s Broadcast Receiver and not any other app.
Pull Notification
Starting with v3.4.0, the SDK supports Pull Notification. This feature provides the capability to reach users on devices that suppress notifications via GCM/FCM.
To allow your app to use CleverTap's Pull Notification via background ping service, add the following fields in your app's AndroidManifest.xml
:
<meta-data
android:name="CLEVERTAP_BACKGROUND_SYNC"
android:value="1"/>
Custom Handling
Additional integration is required if the app is rendering push notifications and not the CleverTap SDK. For more information, see custom handling of pull notification.
Custom Handling of Pull Notification
- In your custom FCM Receiver class, add the following code snippet when you receive the RemoteMessage object from Firebase. This method allows you to ensure that duplicate notification is not rendered on your device via Pull Notification.
if (message.getData().size() > 0) {
Bundle extras = new Bundle();
Iterator var = message.getData().entrySet().iterator();
while(var.hasNext()) {
Map.Entry entry = (Map.Entry)var.next();
extras.putString((String)entry.getKey(), (String)entry.getValue());
}
CleverTapAPI.processPushNotification(getApplicationContext(),extras);
}
}
if (message.getData().size() > 0)
{
val extras = Bundle()
val `var` = message.getData().entrySet().iterator()
while (`var`.hasNext())
{
val entry = `var`.next() as Map.Entry
extras.putString(entry.getKey() as String, entry.getValue() as String)
}
CleverTapAPI.processPushNotification(getApplicationContext(), extras)
}
- In your Application class, implement the CTPushAmpListener interface.
public class MyApplication extends Application implements CTPushAmpListener {
// your application class code
}
class MyApplication:Application(), CTPushAmpListener// your application class code
- In the onCreate() method of your Application class, create an instance of CleverTapAPI and set the
CTPushAmpListener
as following:
CleverTapAPI cleverTapAPI = CleverTapAPI.getDefaultInstance(getApplicationContext());
cleverTapAPI.setCTPushAmpListener(this);
val cleverTapAPI = CleverTapAPI.getDefaultInstance(getApplicationContext())
cleverTapAPI.setCTPushAmpListener(this)
- After the listener is set, whenever a notification is sent to the device via Pull Notification, the app will receive the payload in the onPushPayloadReceived method. Implement this method as follows and ensure that your rendering logic of the Push Notification is written here:
@Override
public void onPushPayloadReceived(Bundle bundle) {
//write push notification rendering logic here
}
fun onPushPayloadReceived(bundle:Bundle) {
//write push notification rendering logic here
}
Updated 2 days ago