Customize Push Notifications for Android

The NotificationCustomizationOptions class of the Salesforce Engagement SDK facilitates push notification customization through a NotificationChannelIdProvider, enabling the assignment of a specific Notification Channel for each NotificationMessage.

Additionally, NotificationLaunchIntentProvider defines the action to take when a user taps a NotificationMessage, alongside your application’s notification icon.

When you target apps that run Android 12 or later, specify the mutability of your Pending Intent. See Android Developer Documentation: Intents and Intent Filters.

Note

1SFMCSdk.configure(applicationContext as Application, SFMCSdkModuleConfig.build {
2  this.pushFeatureModuleConfig = PushFeatureConfig.builder().apply {
3    // Other configuration values
4    setNotificationCustomizationOptions(
5      NotificationCustomizationOptions.create(R.drawable.ic_notification_icon,
6        NotificationManager.NotificationLaunchIntentProvider { context, notificationMessage ->
7          val requestCode = Random().nextInt()
8          val url = notificationMessage.url
9          when {
10              url.isNullOrEmpty() ->
11                  PendingIntent.getActivity(
12                    context,
13                    requestCode,
14                    Intent(context, MainActivity::class.java),
15                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
16                  )
17              else ->
18                  PendingIntent.getActivity(
19                    context,
20                    requestCode,
21                    Intent(Intent.ACTION_VIEW, url.toUri()),
22                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
23                  )
24          }
25        },
26        NotificationManager.NotificationChannelIdProvider { context, notificationMessage ->
27          if (TextUtils.isEmpty(notificationMessage.url)) {
28            NotificationManager.createDefaultNotificationChannel(context)
29          } else {
30            "UrlNotification"
31          }
32        }
33      )
34    )
35  }.build()
36  })
37{
38  // Handle initialization status
39}
8.x or higher
1SFMCSdk.configure(applicationContext as Application, SFMCSdkModuleConfig.build {
2  pushModuleConfig = MarketingCloudConfig.builder().apply {
3    // Other configuration values
4    setNotificationCustomizationOptions(
5      NotificationCustomizationOptions.create(R.drawable.ic_notification_icon,
6        NotificationManager.NotificationLaunchIntentProvider { context, notificationMessage ->
7          val requestCode = Random().nextInt()
8          val url = notificationMessage.url
9          when {
10            url.isNullOrEmpty() ->
11              PendingIntent.getActivity(
12                context,
13                requestCode,
14                Intent(context, MainActivity::class.java),
15                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
16              )
17            else ->
18              PendingIntent.getActivity(
19                context,
20                requestCode,
21                Intent(Intent.ACTION_VIEW, Uri.parse(url)),
22                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
23              )
24          }
25        },
26        NotificationManager.NotificationChannelIdProvider { context, notificationMessage ->
27          if (TextUtils.isEmpty(notificationMessage.url)) {
28            NotificationManager.createDefaultNotificationChannel(context)
29          } else {
30            "UrlNotification"
31          }
32        }
33      )
34    )
35  }.build(applicationContext)
36}) {
37  // TODO handle initialization status
38}
7.x
1MarketingCloudSdk.init(applicationContext as Application, MarketingCloudConfig.builder().apply {
2  // Other configuration values
3  setNotificationCustomizationOptions(
4    NotificationCustomizationOptions.create(R.drawable.ic_notification_icon,
5      NotificationManager.NotificationLaunchIntentProvider { context, notificationMessage ->
6        val requestCode = Random().nextInt()
7        val url = notificationMessage.url()
8        when {
9          url.isNullOrEmpty() ->
10            PendingIntent.getActivity(
11              context,
12              requestCode,
13              Intent(context, MainActivity::class.java),
14              PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
15            )
16          else ->
17            PendingIntent.getActivity(
18              context,
19              requestCode,
20              Intent(Intent.ACTION_VIEW, Uri.parse(url)),
21              PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
22            )
23        }
24      },
25      NotificationManager.NotificationChannelIdProvider { context, notificationMessage ->
26        if (TextUtils.isEmpty(notificationMessage.url())) {
27          NotificationManager.createDefaultNotificationChannel(context)
28        } else {
29          "UrlNotification"
30        }
31      }
32    )
33  )
34}.build(applicationContext)) {
35  // TODO handle initialization status
36}

Icons 

The SDK supports icons in various notification elements, including small icons (in the status bar and notification header), large icons (in the notification body) and rich button icons.

You can use icons by placing icon files in the PNG format inside drawables-**hdpi folders within your client app’s resources. The SDK then references these icons by their resource names (for example, ic_notification_icon).

Here’s how you can configure and set icons:

  • Small icons: Provide the resource ID within NotificationCustomizationOptions when using MarketingCloudConfigBuilder. Alternatively, you can specify the icon’s resource name directly in the Engagement UI.
  • Large icons: Provide the resource name and optional public URL for remote retrieval in the Engagement UI.
  • Rich button icons: Provide the resource names in the Engagement UI.

For example, to use an icon named ic_notification_icon.png, place it in the appropriate drawables-**hdpi folder. Then, provide ic_notification_icon as the resource name on the Engagement UI. If its a small icon, you can also specify the resource ID in NotificationCustomizationOptions when using MarketingCloudConfigBuilder.

Sound 

You can customize notification channels by setting priorities, colors, and sounds. To use a custom sound in notifications, follow these steps:

  1. Add the sound file to the app’s res/raw/ folder.

  2. Create a notification channel for the audio file and configure it in the code using NotificationChannelIdProvider. This sample code creates a notification channel named myaudio, enables badges and lights, and sets a default importance level.

    1/*
    2Create a notification channel for the audio file
    3*/
    4if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    5  androidNotificationManager.createNotificationChannel(
    6    NotificationChannel(
    7      "myaudio", "myaudio",
    8      AndroidNotificationManager.IMPORTANCE_DEFAULT
    9    ).apply {
    10      setShowBadge(true)
    11      enableLights(true)
    12      setSound(
    13        Uri.parse("android.resource://${context.packageName}/${context.resources.getIdentifier("custom","raw", context.packageName)}"),
    14        AudioAttributes.Builder().setUsage(
    15          AudioAttributes.USAGE_NOTIFICATION_EVENT
    16        ).build()
    17      )
    18    })
    19  }
  3. In the Marketing Cloud Engagement UI, set the sound file name and extension in the Custom section of Sound settings.

    Custom sounds can use these file formats: .mp3, .wav, .aac, .m4a

This sample code configures the SDK for an Android application. It sets up notification customization options, including the small icon, launch intent, and notification channel ID.

1SFMCSdk.configure(applicationContext as Application, SFMCSdkModuleConfig.build {
2  pushModuleConfig = MarketingCloudConfig.builder().apply {
3    // Other configuration values
4    setNotificationCustomizationOptions(
5    // Set small icon via NotificationCustomizationOptions
6      NotificationCustomizationOptions.create(R.drawable.ic_notification_icon,
7        NotificationManager.NotificationLaunchIntentProvider { context, notificationMessage ->
8          val requestCode = Random().nextInt()
9          val url = notificationMessage.url
10          when {
11            url.isNullOrEmpty() ->
12              PendingIntent.getActivity(
13                context,
14                requestCode,
15                Intent(context, MainActivity::class.java),
16                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
17              )
18            else ->
19              PendingIntent.getActivity(
20                context,
21                requestCode,
22                Intent(Intent.ACTION_VIEW, Uri.parse(url)),
23                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
24              )
25          }
26        },
27        NotificationManager.NotificationChannelIdProvider { context, notificationMessage ->
28          if (TextUtils.isEmpty(notificationMessage.url)) {
29            NotificationManager.createDefaultNotificationChannel(context)
30          } else if(notificationMessage.sound == NotificationMessage.Sound.CUSTOM) {
31            notificationMessage.soundName  // This will be myaudio if sound sent with push is myaudio.mp3
32          } else {
33            "UrlNotification"
34          }
35        }
36      )
37    )
38  }.build(applicationContext)
39}) {
40  // TODO handle initialization status
41}