Customizing Push Notifications

Step-by-step guide to customizing the appearance of Push Notifications through your app

Custom Push Notification Layouts

WebEngage Android SDK has included two new functional interfaces in v3.10.1 which allows you to modify the default push notification layouts.

🚧

Please ensure you follow the Android notification layout guidelines before creating your own custom layouts. Usually, collapsed view layouts are limited to 64 dp, and expanded view layouts are limited to 256 dp.

1. CustomPushRender: Called for rendering push notifications. This is where you can set your custom layouts and notify push notifications.

public interface CustomPushRender {
    /**
     * @return: Return true if notification is rendered successfully, else return false.
     */
    boolean onRender(Context context, PushNotificationData pushNotificationData);
}

2. CustomPushRerender: Called for re-rendering/updating push notifications. This is where you can update your push notifications.

public interface CustomPushRerender {
    /**
     * @return: Return true if notification is re-rendered successfully, else return false.
     */
    boolean onRerender(Context context, PushNotificationData pushNotificationData, Bundle extras);
}

Custom push render callbacks are only called when you provide we_custom_render: true in the custom key-values while creating push campaign from WebEngage dashboard and CustomPushRender and CustomPushRerender implementations are registered using WebEngage.registerCustomPushRenderCallback and WebEngage.registerCustomPushRerenderCallback APIs.

If onRender or onRerender method returns false, then notification will not be displayed.

Push Notification Data

The instance of PushNotificationData available to you in the onRender and onRerender callbacks contains the details required to construct and show the push notification.

🚧

Rich Text handling

Available from SDK v3.20.0 and needs to be handled properly to avoid showing proper title and message in push notifications.

Handling Rich text

From SDK version 3.20.0 and above you will start getting rich text in PushNotifictaionData hence it needs to be handled accordingly.
Push title, message, summary and action text will start coming in HTML format so to handle them, kindly follow the steps below.

Handling Push Title text

WEHtmlParserInterface weHtmlParserInterface = new WEHtmlParserInterface();
            Spannable title = weHtmlParserInterface.fromHtml(pushNotificationData.getTitle());

...
//Now use the title as your input to remote views.
val weHtmlParserInterface = WEHtmlParserInterface()  
    val title = weHtmlParserInterface.fromHtml(pushNotificationData.title)

...
//Now use the title as your input to remote views.

Handling Push Description text

WEHtmlParserInterface weHtmlParserInterface = new WEHtmlParserInterface();
            Spannable description = weHtmlParserInterface.fromHtml(pushNotificationData.getContentText());

...
//Now use the description as your input to remote views.
val weHtmlParserInterface = WEHtmlParserInterface()      
val description = weHtmlParserInterface.fromHtml(pushNotificationData.contentText)
...
//Now use the description as your input to remote views.

Handling Push Summary text

WEHtmlParserInterface weHtmlParserInterface = new WEHtmlParserInterface();
            Spannable summary = weHtmlParserInterface.fromHtml(pushNotificationData.getContentSummary());

...
//Now use the summary as your input to remote views.
val weHtmlParserInterface = WEHtmlParserInterface()      
val summary = weHtmlParserInterface.fromHtml(pushNotificationData.contentSummary)
...
//Now use the summary as your input to remote views.

❗️

Important changes while getting data from PushNotificationData

Follow the changes mentioned in the table below

Descriptionless than v3.20.0greater equal v3.20.0
Fetching DescriptionpushNotificationData.getSummary()pushNotificationData.getContentText()
Fetching SummaryNot AvailablepushNotificationData.getContentSummary()

Tracking Push Notification Actions

In order to correctly track the push notification actions such as clicks, updates, dismissed, etc. in custom push notifications, it is mandatory to use the PendingIntents constructed through the APIs provided in the WebEngage's PendingIntentFactory class.

PendingIntentFactory includes the APIs for constructing the following PendingIntents which must be set in the Notification for tracking different push notification actions.

1. Click PendingIntent

PendingIntent constructPushClickPendingIntent(Context context, PushNotificationData pushNotificationData, CallToAction cta, boolean autoCancel) method returns a PendingIntent which can be set as PendingIntent for content intent or for any action button in your custom push notification.

boolean autoCancel = true;  // true if notification should be dismissed on click, else false
    PendingIntent contentPendingIntent = PendingIntentFactory.constructPushClickPendingIntent(context, pushNotificationData, pushNotificationData.getPrimeCallToAction(), autoCancel);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(context, MY_CHANNEL_ID)
        ...
        .setContentIntent(contentPendingIntent)
        ...

2. Delete PendingIntent

PendingIntent constructPushDeletePendingIntent(Context context, PushNotificationData pushNotificationData) method returns a PendingIntent that must be set as delete intent in your notification, which will help WebEngage SDK to track push notification dismisses.

PendingIntent deletePendingIntent = PendingIntentFactory.constructPushDeletePendingIntent(context, pushNotificationData);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(context, MY_CHANNEL_ID)
        ...
        .setDeleteIntent(deletePendingIntent)
        ...

3. Rerender PendingIntent

PendingIntent constructRerenderPendingIntent(Context context, PushNotificationData pushNotificationData, String requestCodePrefix, Bundle extraData) method returns a PendingIntent which will trigger the onRerender callback on click. This callback can be used to update/rerender your notification.

param String requestCodePrefix: The request code of the returned PendingIntent is generated using the following code:

...
    int requestCode = (requestCodePrefix + pushNotificationData.getVariationId()).hashCode();
    PendingIntent pendingIntent = PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    return pendingIntent;

param Bundle extraData: This bundle must include additional data required for rerendering the notification. This bundle will be passed in boolean onRerender(Context context, PushNotificationData pushNotificationData, Bundle extraData) method as extraData for updating the notification.

For eg. You might want to update the push notification content when a user selects a star in rating push notification. In order to mark the star(s) as selected, you might need an additional information that tells you which star was clicked. Such additional data can be passed in the extraData bundle.

Bundle rateClickExtraData = new Bundle();
    rateClickExtraData.putInt("current", i);
    ...

    PendingIntent rateClickPendingIntent = PendingIntentFactory.constructRerenderPendingIntent(context, pushNotificationData, "rate_click_" + i, rateClickExtraData);
    ratingView.setOnClickPendingIntent(R.id.selected_star, rateClickPendingIntent);

4. Carousel Browse PendingIntent

PendingIntent constructCarouselBrowsePendingIntent(Context context, PushNotificationData pushNotificationData, int newIndex, String navigation, String requestCodePrefix, Bundle extraData) method returns a PendingIntent which will automatically track the carousel browse event on click of left/right arrows in the carousel push notification. It also triggers the onRerender callback which can be used to update the current carousel item.

param int newIndex: This is the newly calculated index of the carousel item to be shown.

param String navigation: This indicates the direction in which the carousel is browsed.

int currIndex = 0;
    PendingIntent leftPendingIntent = PendingIntentFactory.constructCarouselBrowsePendingIntent(context, pushNotificationData, currIndex, "left", "carousel_left", browseExtraData);
    PendingIntent rightPendingIntent = PendingIntentFactory.constructCarouselBrowsePendingIntent(context, pushNotificationData, currIndex, "right", "carousel_right", browseExtraData);
    ...
    carouselView.setOnClickPendingIntent(R.id.left_arraow, leftPendingIntent);
    carouselView.setOnClickPendingIntent(R.id.right_arrow, rightPendingIntent);
    ...

5. Rating Submit PendingIntent

PendingIntent constructPushRatingSubmitPendingIntent(Context context, PushNotificationData pushNotificationData, int rateValue) method takes an integer for rating as parameter which will track rating submit event on click of submit button in rating notification.

param int rateValue: This is the last rating value selected by the user.

int currIndex = extraData.getInt("current");  // current index can be obtained from extraData bundle provided while setting Click PendingIntent
    PendingIntent rateSubmitPendingIntent = PendingIntentFactory.constructPushRatingSubmitPendingIntent(context, pushNotificationData, currIndex);
    ratingView.setOnClickPendingIntent(R.id.rating_submit_button, rateSubmitPendingIntent);

Note: Any PendingIntents used in the push notification which is not provided by the above specified APIs, will not be tracked by the WebEngage Android SDK and hence will not be seen on the campaign stats page in your WebEngage dashboard.

Sample usage of these PendingIntents can be found in the code snippets of sample custom layouts given below.

Sample Custom Layouts

Prerequisites:

  • Create implementations of CustomPushRender and CustomPushRerender interfaces as shown below.
public class MyPushRenderer implements CustomPushRender, CustomPushRerender {
    @Override
    public boolean onRender(Context context, PushNotificationData pushNotificationData) {
        // render your notification here
        return true;
    }

    @Override
    public boolean onRerender(Context context, PushNotificationData pushNotificationData, Bundle bundle) {
        // re-render your notification here
        return true;
    }
}
  • Make sure to register for custom push render callbacks in your Application class as shown below.
public class MainApplication extends Application {
    private static final String TAG = MainApplication.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();

        ...

        // Register for custom push render callbacks
        MyPushRenderer myPushRenderer = new MyPushRenderer();
        WebEngage.registerCustomPushRenderCallback(myPushRenderer);
        WebEngage.registerCustomPushRerenderCallback(myPushRenderer);
    }
}

Please refer to the following sample code snippets which might help you to build your own custom push notification layouts - Text Layout, Banner Layout, Carousel Layout (Landscape), Carousel Layout (Portrait), Rating Layout.

For the following sections, you can customize your push messages by implementing push message callbacks in your application.

Overriding Push Title, Message and Image

You can override the title, message or image of the push notification that you had set on WebEngage dashboard while creating the campaign.

@Override
    public PushNotificationData onPushNotificationReceived(Context context, PushNotificationData notificationData) {
        notificationData.setTitle("YOUR_TITLE"); // Add or change title
        notificationData.setContentText("YOUR_MESSAGE"); // Change message
        WebEngageConstant.STYLE style = notificationData.getStyle();
        if (style != null) {
            switch (style) {
                case BIG_PICTURE:
                    // Change image
                    notificationData.getBigPictureStyleData().setBigPictureUrl("http://www.example.com");

                    //Add title which will be shown when notification is expanded
                    notificationData.getBigPictureStyleData().setBigContentTitle("YOUR_EXPANDED_TITLE");
                    // Add summary which will be shown when notification is expanded.
                    notificationData.getBigPictureStyleData().setSummary("YOUR_SUMMARY");
                    break;

                case BIG_TEXT:
                    // Add or change title which will be shown when notification is expanded.
                    notificationData.getBigTextStyleData().setBigContentTitle("YOUR_EXPANDED_TITLE");
                    // Change message which will be shown when notification is expanded.
                    notificationData.getBigTextStyleData().setBigText("YOUR_EXPANDED_MESSAGE");
                    break;
            }
        }
        return notificationData;
    }

Campaign-level Customization

While creating a push message campaign on WebEngage dashboard, you can define key-value pairs. This data is a part of the push message payload, and can be accessed using push callbacks. This allows you to customize your push messages at a campaign level from within your app. This is illustrated below using onPushNotificationDismissed. You can use any of the push message callbacks depending on where you want to customize the user experience.

@Override
public void onPushNotificationDismissed(Context context, PushNotificationData notificationData) {
  	Bundle customData = notificationData.getCustomData();
  	if (customData != null) {
            // Your customization logic goes here
    }
}

📘

If you have multiple apps using the same WebEngage license code, you can target them selectively using push notifications by adding key-value pairs in the campaign body and using them as identifiers in your apps as illustrated above.

Call to Action

Calls to action are the deep links or URIs where a user lands on clicking the push message. You can add CTAs while creating your push campaign on WebEngage dashboard. They are divided into two categories in WebEngage Android SDK - Primary CTA and Non-primary CTA.

Primary CTA

A push message can have at most one primary CTA which performs action (directs the user to a particular URI or a deep link) when the user taps on the content of the push notification. If no URI or deep link is configured for primary CTA then the user will be redirected to your application's launcher activity upon clicking the push notification.

2024

Click to enlarge

Non-primary CTAs

A push notification can have at most 2 non-primary CTAs which perform action when the user clicks on their respective action buttons.

Sometimes you may desire to take control of the click action of a push notification for performing tasks such as creating your own back navigation. When the user enters your app with a deep link that starts the activity in its own task, it's necessary for you to synthesize a new back stack because the activity is running in a new task without any back stack at all. This is illustrated below using push message callbacks.

@Override
public boolean onPushNotificationClicked(Context context, PushNotificationData notificationData) {
        return buildNavigation(context, notificationData.getPrimeCallToAction());
    }

@Override
public boolean onPushNotificationActionClicked(Context context, PushNotificationData notificationData, String buttonID) {
        return buildNavigation(context, notificationData.getCallToActionById(buttonID));
    }

    private boolean buildNavigation(Context context, CallToAction callToAction) {
        if (callToAction != null) {
            if (callToAction.isPrimeAction()) {
                //User clicked on notification content
                String action = callToAction.getAction();
                if (action == null) {
                    // Primary CTA was not configured with a URI or deep link.
                    // Return false if you want WebEngage SDK to open launcher activity with default intent flags.
                    // Return true and write your own code to open desired activity with custom intent flags.


                } else {
                    CallToAction.TYPE type = callToAction.getType();
                    if (type != null) {
                        switch (type) {
                            case LINK:
                                //This CTA was configured with a link while creating the push notification campaign.
                                // This link can be either an http url or your custom deep link.
                                // Return false if you want WebEngage SDK to open this link with default flags or return true
                                // and write your own code to open this link.

                                break;


                            case LAUNCH_ACTIVITY:
                                //This CTA was configured with an activity path while creating the push notification.
                                // Return false if you want WebEngage SDK to open this activity with default flags or return true 
                                // and write your own code to open this activity.

                                break;
                        }
                    }
                }
                //NOTE: To get action string use callToAction.getAction()
            } else {
                // User clicked on one of the action buttons.
                // Handle this CTA in the same way above primary CTA is handled, if only desired.
            }
        }
        return false;
    }

🚧

Please Note

Read operation on PushNotificationData fields can be done from any push notification callback function.

Any changes done to PushNotificationData fields will be reflected only if they are called from onPushNotificationReceived callback.

For example : notificationData.getPrimeCallToAction().setAction("http://www.example.com", CallToAction.TYPE.LINK,context) will be effective only if it is called from onPushNotificationReceived.

Render Timer Push notifications

Timer push notifications are custom push notification layouts wherein clients can configure a timer within push notifications for Android.

WebEngage offers clients 2 layout options for timer push notifications: Countdown timer and Timer with progress bar.

🚧

Supported via custom push

  • Timer notification layouts are not offered out of the box on the dashboard currently. We have provided this capability using custom layouts.

  • These pre-defined timer template support is added for text layouts only. But it can always be extended to other layouts as these are powered through custom push.

Steps to configure timer push notifications in your app

  • Add custom push render support within your app as per the steps mentioned here
  • Integrate our timer push template from Github.
  • Add below line to the application class of your app or edit your callbacks for including the callbacks provided in the module.
WebEngage.registerCustomPushRenderCallback(CustomCallback())
WebEngage.registerCustomPushRerenderCallback(CustomCallback())

Once the above steps are completed, your App will be able to support these timer notifications.

To render Countdown Timer push notifications

345

Countdown timer Push Notification

Once your app is set up to support timer notifications, you can run countdown timer push notification by passing the below 'Key-values' in the 'Message' section of the push campaigns on the dashboard.

KeyValueDescription
we_custom_rendertrueRequired for allowing apps to render custom push notifications.
template_typetimerCountdown timer template.
future_timeepoch time in millisecondsAdd the time till when the notification is required to be displayed.
Example: If you are sending a notification at 12PM and want the notification to expire by 9 PM then add the value as epoch time (in milliseconds) for 9 PM on that day.
timer_color#000000 {Hex color code}(Optional) Hex code for the color in which timer is required to be displayed.
Note: If no value added then default hex values passed:
Light mode: #000000
Dark mode: #ffffff
If Notification background color added: #ffffff
show_dismiss_ctatrue/false(Optional) Clicking on this CTA will dismiss the notification and log the notification dismiss event.

Once added test the campaigns to make sure notifications are rendering as expected.

To render timer push notifications with Progress Bar

518

Timer Notification with Progress Bar

📘

Progress bar notifications are sticky by default. Its highly recommneded to add a Dismiss CTA using the show_dismiss_cta key mentioned below for giving users the option to dismiss the notification.

Once your app is set up to support timer notifications, you can run progress bar push notification with timer by passing the below 'Key-values' in the 'Message' section of the push campaigns on the dashboard.

KeyValueDescription
we_custom_rendertrueRequired for allowing apps to render custom push notifications.
template_typebarCountdown timer template with progress bar.
future_timeepoch time in millisecondsAdd the time till when the notification is required to be displayed.
Example: If you are sending a notification at 12PM and want the notification to expire by 9 PM then add the value as epoch time (in milliseconds) for 9 PM on that day
timer_color#000000 {Hex color code}(Optional) Hex code for the color in which timer is required to be displayed.
If no value added then default hex values passed:
Light mode: #000000
Dark mode: #ffffff
If Notification background color added: #ffffff
show_dismiss_ctatrue / false(Optional) Clicking on this CTA will dismiss the notification and log the notification dismiss event.

Note: Its highly recommended to keep this true as progress bar notifications are by default sticky.
pb_color#000000 {Hex color code}Hex code for the color in which progress bar is to be displayed.

Note:
Supported only for Android 12 and above.
If no value added then default hex values passed:
Light mode: #4CAF50
Dark mode: #2196F3
pb_bg_color#000000 {Hex color code}Hex code for the background color in which progress bar is to be displayed.

Note:
Supported only for Android 12 and above.
If no value added then default hex values passed:
Light mode: #808080
Dark mode: #dddddd

Once added test the campaigns to make sure notifications are rendering as expected.

Understanding timer notification logic and layout

Countdown timer notification

The template logic for the countdown notification renderer is located at path com.webengage.pushtemplates.templates.CountDownRenderer

704

The layout for the notification is located at layout_timer_template.xml . The view ids and layouts will be used by the NotificationConfigurator to show the notification contents.

  • View id : @id/we_notification_container .
    The background colour of the notification will set to this view.

  • View Id: @id/we_notification_timer .
    This is the chronometer view which will act as a countdown timer for your notifications.

  • <include layout="@layout/pushbase" />
    This is used for adding notification header details (like app name, summary, time in devices below Android 12)

  • <include layout="@layout/title" />
    This is used for adding notification title to the notification.

  • <include layout="@layout/description" />
    This is used for adding notification description to the notification.

  • <include layout="@layout/push_actions" />
    This is used to add the CTA button lists to the notifications.

Timer notification with progress bar

The template logic for the countdown notification renderer is located at path com.webengage.pushtemplates.templates.ProgressBarRenderer

The template uses a foreground service to update the progress-bar. The service is located at path com.webengage.pushtemplates.services.NotificationService.
The notification will be updated after every second with the new values for the progress bar.

686

The layout for the notification is located at layout_progressbar_template.xml . The view ids and layouts will be used by the NotificationConfigurator to show the notification contents.

  • View id : @id/we_notification_container .
    The background colour of the notification will set to this view.

  • View Id: @id/we_notification_timer
    This is the chronometer view which will act as a countdown timer for your notifications.

  • View Id: @id/we_notification_progressBar
    This is the progress-bar view for your notifications.

  • <include layout="@layout/pushbase" />
    This is used for adding notification header details (like app name, summary, time in devices below Android 12)

  • <include layout="@layout/title" />
    This is used for adding notification title to the notification.

  • <include layout="@layout/description" />
    This is used for adding notification description to the notification.

  • <include layout="@layout/push_actions" />
    This is used to add the CTA button lists to the notifications.

Customizing layouts and elements for timer notifications

All the elements added for timer notifications can be customized as per the desired use cases.

Customizing Max title and description length

The max lines of the title and description have been set to 2 by default.
To change the limit, go to your Template File and provide the required value in maxLines.

NotificationConfigurator().setTitleMaxLines(remoteView,maxLinesValue)
NotificationConfigurator().setDescriptionMaxLines(remoteView,maxLinesValue)
Configure the remoteView

Configure the remoteView using below method. This will set the notification background colour and header details like app name, time, summary.

whenTimeInEpoch is the time at which the notification was received to be be shown in the notification

NotificationConfigurator().configureRemoteView(
context,remoteView, pushNotificationData, whenTimeInEpoch)
Configure the title
NotificationConfigurator().setNotificationTitle(
context,pushNotification,remoteView)
Configure the description
NotificationConfigurator().setNotificationDescription(
context,pushNotification,remoteView)
Adding the deep-link intent for notification clicks (Applicable only for Countdown timer notification)
NotificationConfigurator().setClickIntent(
context,remoteView,pushNotification)
Adding the dismiss intent for notification dismiss (Applicable only for Countdown timer notification)
NotificationConfigurator().setDismissIntent(
context,notificationBuilder,pushNotificationData)
Set the notification action intents

Configure the showDismissCTA boolean value based on whether to show the dismiss CTA button at end of the action list.

NotificationConfigurator().setCTAList(
context,
remoteView,
pushNotification,
showDismissCTABoolean)
Set the notification timer color
NotificationConfigurator().setChronometerViewColor(
context,remoteView,pushNotificationData,timerColorHexValue)
Set the progress bar colour
NotificationConfigurator().setProgressBarColor(
remoteView, progressBarColorValue, progressBarBackgroundColorValue)
Get the pending intent for removing the notification from notification panel after clicking on it. (Applicable only for timer notification with progress bar)

Set this pending intent on the remoteView of your notification.

NotificationConfigurator().getClickAndDismissPendingIntent(
context,PushNotificationData,ctaID)

Please feel free to drop in a few lines at [email protected] in case you have any further queries. We're always just an email away!


So, what's next?