Android Push

Learn how to implement push notifications.

Overview

There are two ways to implement push notifications on a device.

Set up Push Notifications Credentials

Follow these steps to enable push notifications:

  1. Log in to the CleverTap dashboard and navigate to Settings > Channels > Mobile Push
  2. Click the FCM credentials section and enter the FCM credentials as shown in the following image:
Enter FCM credentials

Enter FCM credentials

  1. Click Save.
Push Notifications Credentials

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:

  1. Log in to the Firebase Developer Console and go to your Dashboard.
  2. Click the Gear icon and access Project settings.
1281

Navigate to Firebase Project Settings Page

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

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.

  1. 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

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:

  1. Go to console.cloud.google.com and click API & Services.

  1. Click Cloud Messaging API link from the left menu.
  2. 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

Notification Channel Importance Levels

The notification channel's importance levels allow you to customize app notifications based on user preferences. The following are the different levels and their usage:

Notification Channel ImportanceDescription
IMPORTANCE_MIN or 1Minimum notification importance. It only shows in the shade, below the fold in the Notification Drawer.
IMPORTANCE_LOW or 2Low notification importance. It shows in the shade and potentially in the status bar.
IMPORTANCE_DEFAULT or 3Default notification importance. It shows everywhere and makes noise but does not visually intrude.
IMPORTANCE_HIGH or 4Higher notification importance. It shows everywhere, makes noise, and peeks.
IMPORTANCE_MAX or 5Deprecated in Android 8.0. Use IMPORTANCE_HIGH or 4

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

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

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.0we 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

Add Source Files to Android Project

Select Resource Type as raw.

Select Resource Type-Raw

Select Resource Type-Raw

Put your MP3 file in the raw folder.

788

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

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. And the Channel Unsubscribed event is raised when end-users unsubscribe from the push campaigns. For more information about the event and its properties, refer to System Events.

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:

  1. Use a push primer using Half-Interstitial InApp notification
398

Half Interstitial InApp Notification

  1. Use a push primer using In-App alert
398

In App Alert Notification

  1. Without a push primer
293

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:
    The CleverTapAPI class exposes a registerPushPermissionNotificationResponseListener(PushPermissionResponseListener) method. Use this method to register your PushPermissionResponseListener instance. Each PushPermissionResponseListener instance passed in this method is added to a List of the PushPermissionResponseListener 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:
    The CleverTapAPI class exposes a unregisterPushPermissionNotificationResponseListener(PushPermissionResponseListener) method.
    Use this method to unregister your previously registered PushPermissionResponseListener instance.

📘

Note

Call registerPushPermissionNotificationResponseListener(PushPermissionResponseListener) only from the onCreate() method of the Activity/Fragment and unregister the listener instance from the onDestroy() method using unregisterPushPermissionNotificationResponseListener(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 from com.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.

  1. For automatic handling, register the activity lifecycle callback using the ActivityLifecycleCallback.register(this method.
  2. For manual handling, override the onNewIntent() method in all your activities or the base activity and call the clevertapApi.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 StatusActivity in BackstackActivity not in Backstack
Foreground/BackgroundCall 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.
KilledNot 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 StatusActivity in BackstackActivity not in Backstack
Foreground/BackgroundNo Notification Clicks tracked.No Notification Clicks tracked.
KilledNot ApplicableNo 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 the com.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.

  1. For automatic handling, register the activity lifecycle callback using the ActivityLifecycleCallback.register(this) method.
  2. For manual handling, override the onNewIntent() method in all your activities or the base activity and call the clevertapApi.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 StatusActivity in BackstackActivity not in Backstack
Foreground/BackgroundCall 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.
KilledNot 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 StatusActivity in BackstackActivity not in Backstack
Foreground/BackgroundNo Notification Clicks tracked.No Notification Clicks tracked.
KilledNot 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 the onActivityCreated 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:

1080

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:

2736

Set Small App Icon Color

Following is a small icon sample:

Example of a Small Icon

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>
996

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.

  1. Navigate to Settings > Schema > Events.
  2. Search for Push Impressions, then click on the vertical ellipsis.
Push Impressions

Push Impressions

  1. Click on Setup push impressions. A new window displays.
  2. 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:

  1. 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);
  1. Add your service to the AndroidManifest.xml in place of the CleverTap FcmMessageListenerService.
<service
    android:name="com.your.package.MyFcmMessageListenerService"
    android:exported="true">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>
  1. Replace com.your.package with the correct package name which contains the newly created class, MyFcmMessageListenerService.

  2. 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.

KeyTypeDescription
wzrk_pnN/AIf present, this notification is sent from CleverTap.
wzrk_idstringOpen rate tracking ID (can be empty or it might not be present).
wzrk_bpstringIf present, the value will be a URL of an image, that needs to be displayed in the notification.
wzrk_soundboolean or stringIf present, it signifies that the default or custom Android notification sound must be played.
ntstringNotification Title, if absent or empty, fallback to the app name.
nmstringNotification Body, if absent or empty, ignore this notification.
wzrk_dlstringIf present, this is a deeplink that must be followed at the time of notification open.
wzrk_dN/AIf present, ignore this notification.
icostringIf present and not empty, it contains the URL of an image, that must be used as the small notification icon.
wzrk_pivotstringIf present and not empty, it signifies the type of variant in A/B campaigns.
wzrk_rnvstringIf present and not empty, it will raise Push Impressions event
wzrk_nmsstringIf present and not empty, it contains the summary text to be displayed along with the image.
wzrk_ststringIf present and not empty, it contains the subtitle text which is displayed next to the App name.
wzrk_clrstringIf 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

  1. 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)
}
  1. 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
  1. 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)
  1. 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
}