# Customizing notifications

### Setting up custom push icons

#### Overview

Android lets you customise the way your notifications look:

1. [**Small icon**](#small-icon): *Required* - App icon displayed in your notifications. The small icon must be opaque white, otherwise Android will display a white square.
2. [**Accent color**](#accent-color): *Optional* - Color is used by to color your small icon / app name. Android will use a grey accent icolor if you don't set one.
3. [**Large icon**](#large-icon): *Optional* - Use it to add contextual information to your message. You shouldn't use it to display your app icon.

![Android push anatomy](/files/P58eY4yzauiJgId8XAVb)

#### Small icon

The small icon allows users to recognize your app when they receive a push. It should be **opaque white**, using only the alpha channel and **24x24dp**. You can easily create one using the [Android Assets Studio](https://romannurik.github.io/AndroidAssetStudio/icons-notification#source.space.trim=1\&source.space.pad=0\&name=ic_stat_example).\
\\

<figure><img src="/files/8V5vyR1wAGdJTadnegBN" alt=""><figcaption><p>Small icon</p></figcaption></figure>

If you use a colored small icon, Android will display it as a white square:\
\\

<figure><img src="/files/lOPyIXUPqot7vEe7ZhVN" alt=""><figcaption><p>Small icon with issue</p></figcaption></figure>

Here's how to set it to Batch push:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MyKotlinApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        Batch.start("MY_API_KEY")
        registerActivityLifecycleCallbacks(BatchActivityLifecycleHelper())

        Batch.Push.setSmallIconResourceId(R.drawable.push_icon)
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Batch.start("MY_API_KEY");
        registerActivityLifecycleCallbacks(new BatchActivityLifecycleHelper());
        
        Batch.Push.setSmallIconResourceId(R.drawable.push_icon);
    }
}
```

{% endtab %}
{% endtabs %}

It can also be configured using a manifest `meta-data` entry:

{% code title="AndroidManifest.xml" %}

```xml
<meta-data
            android:name="com.batch.android.push.smallicon"
            android:resource="@drawable/push_icon" />
```

{% endcode %}

#### Accent color

Google allows you to set an accent color for your push notifications. If you don't set a custom accent color, Android will use a grey accent color.

![Accent color](/files/AK7Iz8jA6i6Mu4SDtVM8)

Here is how to customise that color:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MyKotlinApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        Batch.start("MY_API_KEY")
        registerActivityLifecycleCallbacks(BatchActivityLifecycleHelper())

        Batch.Push.setNotificationsColor(0xFF00FF00) // You should pass an aRGB integer
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Batch.start("MY_API_KEY");
        registerActivityLifecycleCallbacks(new BatchActivityLifecycleHelper());

        Batch.Push.setNotificationsColor(0xFF00FF00); // You should pass an aRGB integer
    }
}
```

{% endtab %}
{% endtabs %}

#### Large icon

The large icon is optional. You can use it to add contextual information to your notification *(e.g. an avatar, etc)*. You shouldn't use the large icon to display the icon of the app. Here is how it looks:\
\\

<figure><img src="/files/RenEJStlnKhjQzo7L2AJ" alt=""><figcaption><p>Large icon</p></figcaption></figure>

Here's how to set a custom large icon to Batch push:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MyKotlinApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        Batch.start("MY_API_KEY")
        registerActivityLifecycleCallbacks(BatchActivityLifecycleHelper())

        Batch.Push.setSmallIconResourceId(R.drawable.push_icon)
        Batch.Push.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.large_push_icon))
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Batch.start("MY_API_KEY");
        registerActivityLifecycleCallbacks(new BatchActivityLifecycleHelper());

        Batch.Push.setSmallIconResourceId(R.drawable.push_icon);
        Batch.Push.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.large_push_icon));
    }
}
```

{% endtab %}
{% endtabs %}

### Sending silent notifications

Silent push notifications allow you wake up the app to run actions for a limited amount of time without notifying the user.

Here is the key you need to add to your payload to send silent notifications:

```json
{"msg": null}
```

Please note that this will work out of the box with Batch’s default receiver. In case you are using a custom receiver, ensure you don’t display any notification when a `null` value is received for the `“msg”` parameter.

### Setting a custom sound

See our article about [notification sounds on Android](/developer/technical-guides/how-to-guides/mobile/android-specific/how-to-use-custom-notification-sounds-on-android.md).

### Managing notification display

Using `Batch.Push.setShowNotifications`, it is possible to toggle whether Batch will show notifications or skips them. This allows you to implement a "Notifications ON/OFF" switch in your app.

#### Disabling notifications

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
Batch.Push.setShowNotifications(false)
```

{% endtab %}

{% tab title="Java" %}

```java
Batch.Push.setShowNotifications(false)
```

{% endtab %}
{% endtabs %}

#### Enabling notifications

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
Batch.Push.setShowNotifications(true)
```

{% endtab %}

{% tab title="Java" %}

```java
Batch.Push.setShowNotifications(true)
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Batch will remember this value, even if your Application reboots. You can ask to Batch whether notifications are disabled or not  with : `Batch.Push.shouldShowNotifications()` .
{% endhint %}

### Reading a push's payload from your activity

A push's custom payload can be read from multiple places:

* A Notification Interceptor, to be able to enhance the notification using custom payload values
* Your own BroadcastReceiver implementation, to be able to act on a push and take full control of what happens when you get one
* An Activity, allowing your code to act upon the payload without having to make your own PendingIntent

Most of the time, reading it from the activity is what you're looking for.

If integrated correctly, Batch will attach the full payload to the extras of triggered intent. Depending on your Activity's flags and the current application state, you might get the intent in `onCreate()` or `onNewIntent()`

Here's an example of how to read the following custom payload: `{"foo": "bar"}`

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MyActivity : Activity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    handleBatchCustomPayload(getIntent())
  }

  override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    Batch.onNewIntent(this, intent)
    handleBatchCustomPayload(intent)
  }

  fun handleBatchCustomPayload(intent: Intent) {
    val pushPayload = intent.getBundleExtra(Batch.Push.PAYLOAD_KEY)
    if (pushPayload != null) {
      // The activity was opened from a Batch push notification, and may include a custom payload
      // You might wanna mark the intent as consumed so that you don't handle it multiple times.
      // If this extra is missing but you, please check that your integration is correct, and that
      // nothing is missing in your NotificationInterceptor/BroadcastReceiver implementation

      // Custom payload values are available as string extras. No other types are supported by FCM.
      // Values that are of complex types, like a JSON object or Array, will have to be decoded using a json library

      val foo = pushPayload.getString("foo") // foo = "bar"
    }
  }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class MyActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    [...]
    handleBatchCustomPayload(getIntent());
  }

  @Override
  public void onNewIntent(Intent newIntent) {
    super.onNewIntent(newIntent);
    Batch.onNewIntent(this, newIntent);
    handleBatchCustomPayload(newIntent);
  }

  public void handleBatchCustomPayload(Intent intent) {
    Bundle pushPayload = intent.getBundleExtra(Batch.Push.PAYLOAD_KEY);
    if (pushPayload != null) {
      // The activity was opened from a Batch push notification, and may include a custom payload
      // You might wanna mark the intent as consumed so that you don't handle it multiple times.
      // If this extra is missing but you, please check that your integration is correct, and that
      // nothing is missing in your NotificationInterceptor/BroadcastReceiver implementation

      // Custom payload values are available as string extras. No other types are supported by FCM.
      // Values that are of complex types, like a JSON object or Array, will have to be decoded using a json library

      String foo = pushPayload.getString("foo"); // foo = "bar"
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Advanced notification customization

Sometimes, you might want to alter and/or enhance the way Batch builds the notification, or override other fields based on the notification payload.\
Starting with Batch 1.9.2, Batch includes a notification interception system so you don't have to make your own notification from scratch.

First, you will need to extend the `BatchNotificationInterceptor` class. It has two methods you can override to tweak notifications:

* `getPushNotificationCompatBuilder` gives you full access the the `NotificationCompat.Builder`. This method is called after Batch is done setting everything up in the builder you get in a parameter. You can add your own customizations to it, or return a totally different builder.
* `getPushNotificationId` lets you change the Notification ID. This is handy to turn the ID into a non unique one, allowing notifications to be updated (such as a sports game score update, for example).

Then you can either use this interceptor in a `Batch.Push.displayNotification()` call in a custom receiver, or set it globally using `Batch.Push.setNotificationInterceptor()` in your Application's `onCreate()`.

{% hint style="info" %}
Like other setup methods, it is VERY important that you set the global interceptor in the Application's `onCreate()` and not the Activity's.
{% endhint %}

Example interceptor implementation:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class NotificationInterceptor : BatchNotificationInterceptor() {
  @Nullable
  override fun getPushNotificationCompatBuilder(
    context: Context,
    defaultBuilder: NotificationCompat.Builder,
    pushIntentExtras: Bundle,
    notificationId: Int
  ): NotificationCompat.Builder {
    defaultBuilder.setOngoing(true)
    return defaultBuilder
  }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class NotificationInterceptor extends BatchNotificationInterceptor {
  @Nullable
  @Override
  public NotificationCompat.Builder getPushNotificationCompatBuilder(@NonNull Context context,
                                                                     @NonNull NotificationCompat.Builder defaultBuilder,
                                                                     @NonNull Bundle pushIntentExtras,
                                                                     int notificationId) {
    defaultBuilder.setOngoing(true);
    return defaultBuilder;
  }
}
```

{% endtab %}
{% endtabs %}

All that is left to do, is to globally register the interceptor:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MyKotlinApplication : Application() {

    override fun onCreate() {
        super.onCreate()

      Batch.Push.setNotificationInterceptor(NotificationInterceptor())

      // Other Batch configuration ...
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();

    Batch.Push.setNotificationInterceptor(new NotificationInterceptor());

    // Other Batch configuration ...
  }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Batch sets some default values on the Notification builder that cannot be overridden later. If you run into such a case, please contact us, so we can take this into account for future SDK improvements.
{% endhint %}

#### Adding actions to notifications

By combining [Batch Actions](/developer/sdk/android/advanced/custom-actions.md) and `BatchNotificationInterceptor`, you can easily add action buttons to notifications based on your custom payload: `BatchNotificationAction` comes with an helper method allowing you to generate `NotificationCompat.Action` easily without needing to worry about PendingIntents or services.

Here's an example of `BatchNotificationInterceptor` implementation that adds a "Call" button for the following custom payload: `{"phoneCTA": "123456789"}`

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class NotificationInterceptor : BatchNotificationInterceptor() {
  @Nullable
  override fun getPushNotificationCompatBuilder(context: Context,
                                                defaultBuilder: NotificationCompat.Builder,
                                                pushIntentExtras: Bundle,
                                                notificationId: Int): NotificationCompat.Builder? {
    val phoneNumber = pushIntentExtras.getString("phoneCTA")
    if (phoneNumber != null) {
      try {
        val jsonArgs = JSONObject()
        jsonArgs.put("number", phoneNumber)
        val notificationAction = BatchNotificationAction()
        notificationAction.shouldDismissNotification = false
        notificationAction.actionIdentifier = "CALL"
        notificationAction.label = "Call"
        notificationAction.hasUserInterface = true
        notificationAction.actionArguments = jsonArgs
        val notificationActions: MutableList<BatchNotificationAction> = ArrayList(1)
        notificationActions.add(notificationAction)
        val compatActions = BatchNotificationAction.getSupportActions(
          context,
          notificationActions,
          BatchPushPayload.payloadFromReceiverExtras(pushIntentExtras),
          notificationId)
        for (compatAction in compatActions) {
          defaultBuilder.addAction(compatAction)
        }
      } catch (e: JSONException) {
        e.printStackTrace()
      } catch (e: MissingDependencyException) {
        e.printStackTrace()
      } catch (e: BatchPushPayload.ParsingException) {
        e.printStackTrace()
      }
    }
    return defaultBuilder
  }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class NotificationInterceptor extends BatchNotificationInterceptor {
  @Nullable
  @Override
  public NotificationCompat.Builder getPushNotificationCompatBuilder(@NonNull Context context,
                                                                     @NonNull NotificationCompat.Builder defaultBuilder,
                                                                     @NonNull Bundle pushIntentExtras,
                                                                     int notificationId) {
    final String phoneNumber = pushIntentExtras.getString("phoneCTA");
    if (phoneNumber != null) {
      try {
        JSONObject jsonArgs = new JSONObject();
        jsonArgs.put("number", phoneNumber);

        BatchNotificationAction notificationAction = new BatchNotificationAction();
        notificationAction.shouldDismissNotification = false;
        notificationAction.actionIdentifier = "CALL";
        notificationAction.label = "Call";
        notificationAction.hasUserInterface = true;
        notificationAction.actionArguments = jsonArgs;

        List<BatchNotificationAction> notificationActions = new ArrayList<>(1);
        notificationActions.add(notificationAction);

        List<NotificationCompat.Action> compatActions = BatchNotificationAction.getSupportActions(
          context,
          notificationActions,
          BatchPushPayload.payloadFromReceiverExtras(pushIntentExtras),
          notificationId);

        for (NotificationCompat.Action compatAction : compatActions) {
          defaultBuilder.addAction(compatAction);
        }
      } catch (JSONException | MissingDependencyException | BatchPushPayload.ParsingException e) {
        e.printStackTrace();
      }

    }
    return defaultBuilder;
  }
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Since Android 12 and the new notification trampoline restrictions, your app cannot call `startActivity()` inside of a service or broadcast receiver. So you have to precise if your action imply showing any UI or will it act in the background by using `notificationAction.hasUserInterface = true;`
{% endhint %}

This interceptor assumes that you've registered the `CALL` action to Batch earlier:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MyApplication : Application() {
  fun onCreate() {
    super.onCreate()
    Batch.Push.setNotificationInterceptor(NotificationInterceptor())
    Batch.Actions.register(UserAction("CALL", object : UserActionRunnable() {
      fun performAction(@Nullable context: Context?,
                        @NonNull identifier: String?,
                        @NonNull args: JSONObject,
                        @Nullable source: UserActionSource?) {
        try {
          val callIntent: Intent = Intent(Intent.ACTION_DIAL)
          callIntent.setData(Uri.parse("tel:" + Uri.encode(args.getString("number"))))
          callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
          startActivity(callIntent)
        } catch (e: JSONException) {
          e.printStackTrace()
          Toast.makeText(context, "Could not execute CALL action", Toast.LENGTH_SHORT).show()
        }
      }
    }))
    // Other Batch configuration ...
  }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    Batch.Push.setNotificationInterceptor(new NotificationInterceptor());
    Batch.Actions.register(new UserAction("CALL", new UserActionRunnable() {
      @Override
      public void performAction(@Nullable Context context,
                                @NonNull String identifier,
                                @NonNull JSONObject args,
                                @Nullable UserActionSource source) {
        try {
          Intent callIntent = new Intent(Intent.ACTION_DIAL);
          callIntent.setData(Uri.parse("tel:" + Uri.encode(args.getString("number"))));
          callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          startActivity(callIntent);
        } catch (JSONException e) {
          e.printStackTrace();
          Toast.makeText(context, "Could not execute CALL action", Toast.LENGTH_SHORT).show();
        }
      }
    }));
    // Other Batch configuration ...
  }
}
```

{% endtab %}
{% endtabs %}

#### Changing the notification's launch intent

You might also want the notification to launch another intent that the one picked by Batch. Starting with Batch 1.10.2, you can easily generate a valid PendingIntent containing Batch's required extras, or append Batch's data to your own pending intent:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class NotificationInterceptor : BatchNotificationInterceptor() {
  override fun getPushNotificationCompatBuilder(context: Context,
                                                defaultBuilder: NotificationCompat.Builder,
                                                pushIntentExtras: Bundle,
                                                notificationId: Int): NotificationCompat.Builder {
    // Automatically make a PendingIntent from a given intent
    defaultBuilder.setContentIntent(Batch.Push.makePendingIntent(context, Intent(context, MySecondaryActivity::class.java), pushIntentExtras))

    // Get a PendingIntent for an URL
    defaultBuilder.setContentIntent(Batch.Push.makePendingIntentForDeeplink(context, "https://batch.com", pushIntentExtras))

    // Or, bring your own PendingIntent, and add Batch's data
    val i = Intent(context, MySecondaryActivity::class.java)
    Batch.Push.appendBatchData(pushIntentExtras, i)
    val pi = PendingIntent.getActivity([...]); // Generate your own PendingIntent
    defaultBuilder.setContentIntent(pi);
    return defaultBuilder
  }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class NotificationInterceptor extends BatchNotificationInterceptor {
  @Nullable
  @Override
  public NotificationCompat.Builder getPushNotificationCompatBuilder(@NonNull Context context,
                                                                     @NonNull NotificationCompat.Builder defaultBuilder,
                                                                     @NonNull Bundle pushIntentExtras,
                                                                     int notificationId) {
    // Automatically make a PendingIntent from a given intent
    defaultBuilder.setContentIntent(Batch.Push.makePendingIntent(context, new Intent(context, MySecondaryActivity.class), pushIntentExtras));

    // Get a PendingIntent for an URL
    defaultBuilder.setContentIntent(Batch.Push.makePendingIntentForDeeplink(context, "https://batch.com", pushIntentExtras));

    // Or, bring your own PendingIntent, and add Batch's data
    Intent i = new Intent(context, MySecondaryActivity.class);
    Batch.Push.appendBatchData(pushIntentExtras, i);
    PendingIntent pi = PendingIntent.getActivity([...]); // Generate your own PendingIntent
    defaultBuilder.setContentIntent(pi);

    return defaultBuilder;
  }
}
```

{% endtab %}
{% endtabs %}

#### Filtering notifications

A notification interceptor can also be used to filter notifications, by returning `null` instead of a Builder instance:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
 // This sample class represents how your app would store and provide user settings
object UserConfig {
  fun areNotificationsEnabled(): Boolean {
    return false
  }
}
class NotificationInterceptor : BatchNotificationInterceptor() {
  fun getPushNotificationCompatBuilder(@NonNull context: Context?,
                                       @NonNull defaultBuilder: NotificationCompat.Builder?,
                                       @NonNull pushIntentExtras: Bundle?,
                                       notificationId: Int): NotificationCompat.Builder? {
    if (!UserConfig.areNotificationsEnabled()) {
      return null
    }

    return defaultBuilder
  }
}
```

{% endtab %}

{% tab title="Java" %}

```java
// This sample class represents how your app would store and provide user settings
public class UserConfig {
  public static boolean areNotificationsEnabled() { return false; }
}

public class NotificationInterceptor extends BatchNotificationInterceptor {
  @Nullable
  @Override
  public NotificationCompat.Builder getPushNotificationCompatBuilder(@NonNull Context context,
                                                                     @NonNull NotificationCompat.Builder defaultBuilder,
                                                                     @NonNull Bundle pushIntentExtras,
                                                                     int notificationId) {
    if (!UserConfig.areNotificationsEnabled()) {
      return null;
    }

    return defaultBuilder;
  }
}
```

{% endtab %}
{% endtabs %}

This can be used to implement conditional silent notifications, but [Batch supports this feature out of the box (click here for more info)](/developer/technical-guides/how-to-guides/backend/how-to-send-a-silent-push-notification.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.batch.com/developer/sdk/android/advanced/customizing-notifications.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
