# Advanced Customizations

Customize notification appearance, behavior, and interactions with Android-specific features.

## Notification channels

Starting with Android O, each notification must assign a valid notification channel or the notification will not display. The SDK will automatically assign a default channel with the name "Notifications". The default channel can be overridden either in [AirshipConfigOptions](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship/-airship-config-options/-builder/set-notification-channel.html)
 or the notification channel can also be set per message by specifying the channel's ID in the [Push API](https://www.airship.com/docs/developer/rest-api/ua/schemas/platform-overrides/#androidoverrideobject).

### Compat notification channels

Notification channel compat allows you to define notification channels for all Android versions. For pre-O Android devices, the Airship SDK will apply the notification channel settings on the notification before posting. This allows an app to define channels and use them across all devices.


#### Kotlin


```kotlin
override fun onAirshipReady(context: Context) {
    // ...

    val channelCompat = NotificationChannelCompat(
        "customChannel",
        "Breaking News!",
        NotificationManagerCompat.IMPORTANCE_DEFAULT
    )

    Airship.push
            .notificationChannelRegistry
            .createNotificationChannel(channelCompat)
}
```



#### Java


```java
@Override
public void onAirshipReady(@NonNull Context context) {
    // ...

    NotificationChannelCompat channelCompat = new NotificationChannelCompat(
        "customChannel", "Breaking News!", NotificationManagerCompat.IMPORTANCE_DEFAULT);

    Airship.getPush()
            .getNotificationChannelRegistry()
            .createNotificationChannel(channelCompat);
}
```




> **Note:** The Airship SDK will fall back to the default notification channel if you set a notification channel ID that doesn't exist, so make sure that you created one with the same ID.


## Clearing notifications

Notifications can be cleared manually by using standard Android APIs on the [NotificationManager](https://developer.android.com/reference/android/app/NotificationManager.html) or [NotificationManagerCompat](https://developer.android.com/reference/android/support/v4/app/NotificationManagerCompat.html) classes.


#### Kotlin


```kotlin
NotificationManagerCompat.from(context).cancelAll()
```



#### Java


```java
NotificationManagerCompat.from(context).cancelAll();
```




## Control foreground notification display

By default, notifications are displayed even when the app is in the foreground. To override that per message, set `foregroundNotificationDisplayPredicate` on `Airship.push`. The predicate runs for every incoming push; return `true` to present the notification or `false` to suppress it. Set the predicate to `null` to restore the default behavior.


#### Kotlin


```kotlin
Airship.push.foregroundNotificationDisplayPredicate = Predicate<PushMessage> { message ->
    // Example: only show when getExtra("display_in_foreground") == "true"
    message.getExtra("display_in_foreground") == "true"
}
```



#### Java


```java
Airship.getPush().setForegroundNotificationDisplayPredicate(message -> {
    // Example: only show when getExtra("display_in_foreground") == "true"
    return "true".equals(message.getExtra("display_in_foreground"));
});
```




## Custom notification provider

The `AirshipNotificationProvider` is the recommended factory as it provides full support for all of the [Android push features](https://www.airship.com/docs/developer/rest-api/ua/schemas/platform-overrides/#androidoverrideobject).

All incoming push notifications are built using a class that implements the [NotificationProvider](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push.notifications/-notification-provider/index.html)
.

The Airship SDK uses the [AirshipNotificationProvider](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push.notifications/-airship-notification-provider/index.html)
. With this provider, the standard Android Notification layout will use the application's title and icon. A default big text style will be applied for all notifications.

![Advanced Customizations](https://www.airship.com/docs/images/default-factory-notification_hu_828cc11f66780a38.webp)

## Custom notification factory

Custom notification factories are supported, but may cause some [Android Push features](https://www.airship.com/docs/developer/rest-api/ua/schemas/platform-overrides/#androidoverrideobject) to no longer work. Only features that the custom factory implements will be available.


#### Kotlin


```kotlin
class CustomNotificationFactory : NotificationProvider {
    @WorkerThread
    override fun onCreateNotificationArguments(context: Context, message: PushMessage): NotificationArguments {
        val channel = requireNotNull(message.getNotificationChannel("defaultChannel"))
        return NotificationArguments.newBuilder(message)
                .setNotificationChannelId(channel)
                .setNotificationId(message.notificationTag, NotificationIdGenerator.nextID())
                .build()
    }

    @WorkerThread
    override fun onCreateNotification(context: Context, arguments: NotificationArguments): NotificationResult {
        val message = arguments.message

        // do not display a notification if there is not an alert
        if (message.alert.isNullOrEmpty()) {
            return NotificationResult.cancel()
        }
        // Build the notification
        val builder = NotificationCompat.Builder(context)
                .setContentTitle("Notification title")
                .setContentText(message.alert)
                .setAutoCancel(true)
                .setSmallIcon(R.drawable.notification_icon)
        // Notification action buttons
        builder.extend(ActionsNotificationExtender(context, message, notificationId))
        return NotificationResult.notification(builder.build())
    }

    @WorkerThread
    override fun onNotificationCreated(context: Context, notification: Notification, arguments: NotificationArguments) {
        // Called after the notification is built, right before posting the notifications. Apply any global
        // defaults to the notification
    }
}
```



#### Java


```java
public class CustomNotificationFactory implements NotificationProvider {
    @WorkerThread
    @NonNull
    @Override
    public NotificationArguments onCreateNotificationArguments(@NonNull Context context, @NonNull PushMessage message) {
        String channel = message.getNotificationChannel("defaultChannel");
        return NotificationArguments.newBuilder(message)
            .setNotificationChannelId(channel)
            .setNotificationId(message.getNotificationTag(), NotificationIdGenerator.nextID())
            .build();
    }

    @WorkerThread
    @NonNull
    @Override
    public NotificationResult onCreateNotification(@NonNull Context context, @NonNull NotificationArguments arguments) {
        PushMessage message = arguments.getMessage();

        // do not display a notification if there is not an alert
        if (UAStringUtil.isEmpty(message.getAlert())) {
            return NotificationResult.cancel();
        }

        // Build the notification
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                .setContentTitle("Notification title")
                .setContentText(message.getAlert())
                .setAutoCancel(true)
                // Make sure that your icon follows Google's Guidelines : a white icon with transparent background
                .setSmallIcon(R.drawable.notification_icon);

        // Notification action buttons
        builder.extend(new ActionsNotificationExtender(context, message, notificationId));

        return NotificationResult.notification(builder.build);
    }

    @WorkerThread
    public void onNotificationCreated(@NonNull Context context, @NonNull Notification notification, @NonNull NotificationArguments arguments) {
        // Called after the notification is built, right before posting the notifications. Apply any global
        // defaults to the notification
    }
}
```




For simple modifications, it is recommended to extend the `AirshipNotificationProvider` instead to ensure all Airship push features continue to work.


#### Kotlin


```kotlin
class CustomNotificationFactory(context: Context, configOptions: AirshipConfigOptions) : AirshipNotificationProvider(context, configOptions) {
    @WorkerThread
    override fun onExtendBuilder(
        context: Context,
        builder: NotificationCompat.Builder,
        arguments: NotificationArguments
    ): NotificationCompat.Builder {
        val newBuilder = super.onExtendBuilder(context, builder, arguments)

        // Apply any defaults to the builder

        return newBuilder
    }
}
```



#### Java


```java
public class CustomNotificationFactory extends AirshipNotificationProvider {

    public CustomNotificationFactory(
        @NonNull Context context,
        @NonNull AirshipConfigOptions configOptions
    ) {
        super(context, configOptions);
    }

    @WorkerThread
    @NonNull
    @Override
    protected NotificationCompat.Builder onExtendBuilder(
        @NonNull Context context,
        @NonNull NotificationCompat.Builder builder,
        @NonNull NotificationArguments arguments
    ) {
        builder = super.onExtendBuilder(context, builder, arguments);

        // Apply any defaults to the builder

        return builder;
    }
}
```





#### Kotlin


```kotlin
override fun onAirshipReady(context: Context) {
    Airship.push.notificationProvider = CustomDefaultNotificationProvider()
}
```



#### Java


```java
@Override
public void onAirshipReady(Context context) {
    Airship.getPush()
           .setNotificationProvider(new CustomDefaultNotificationProvider());
}
```




## Available notification extenders

The SDK also provides several notification builder extenders to help support [Android Push features](https://www.airship.com/docs/developer/rest-api/ua/schemas/platform-overrides/#androidoverrideobject).

[ActionsNotificationExtender](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push.notifications/-actions-notification-extender/index.html)

: Supports Android Notification Action Button API features.

[PublicNotificationExtender](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push.notifications/-public-notification-extender/index.html)

: Supports Public Notification API features.

[StyleNotificationExtender](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push.notifications/-style-notification-extender/index.html)

: Supports Android style API features.

[WearableNotificationExtender](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push.notifications/-wearable-notification-extender/index.html)

: Supports Android Wear API features.

## Interactive notifications

You can add action buttons to notifications to allow users to interact without opening the app.

### Standard interactive notifications

Airship provides a set of standard Interactive Notification types (See: [Built-In Interactive Notification Types](https://www.airship.com/docs/reference/messages/built-in-interactive-notifications/)). It is the *type* that determines which buttons and corresponding labels will be available when you send a push. See the next section for where to specify that in the push payload. You control what happens when you send the push separately, by tying each button ID to a specific action.

### Custom interactive notification types (notification action button groups)

If you want to define a custom Interactive Notification type, you must register a new notification action button group.

> **Note:** Airship reserves category IDs prefixed with `ua_`. Any custom groups with that prefix will be dropped.


Custom [NotificationActionButtonGroups](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push.notifications/-notification-action-button-group/index.html)
 are supported by registering the groups with the [PushManager](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push/-push-manager/index.html)
 right after [UAirship.takeOff](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship/-airship/take-off.html)
 using the [PushManager.addNotificationActionButtonGroup](https://www.airship.com/docs/reference/libraries/android-kotlin/latest/urbanairship-core/com.urbanairship.push/-push-manager/add-notification-action-button-group.html)
 method.


#### Kotlin


```kotlin
// Define actions for the group
val hiButtonAction: NotificationActionButton = NotificationActionButton.Builder("hi")
        .setLabel(R.string.hi)
        .setIcon(R.drawable.your_icon_file)
        .setPerformsInForeground(true)
        .build()

val byeButtonAction: NotificationActionButton = NotificationActionButton.Builder("bye")
        .setLabel(R.string.bye)
        .setIcon(R.drawable.your_icon_file)
        .setPerformsInForeground(true)
        .build()

// Define the group
val buttonGroup = NotificationActionButtonGroup.Builder()
        .addNotificationActionButton(hiButtonAction)
        .addNotificationActionButton(byeButtonAction)
        .build()

// Add the custom group
Airship.push.pushManager.addNotificationActionButtonGroup("custom group", buttonGroup)
```



#### Java


```java
// Define actions for the group
NotificationActionButton hiButtonAction = new NotificationActionButton.Builder("hi")
        .setLabel(R.string.hi)
        .setIcon(R.drawable.your_icon_file)
        .setPerformsInForeground(true)
        .build();

NotificationActionButton byeButtonAction = new NotificationActionButton.Builder("bye")
        .setLabel(R.string.bye)
        .setIcon(R.drawable.your_icon_file)
        .setPerformsInForeground(true)
        .build();

// Define the group
NotificationActionButtonGroup buttonGroup = new NotificationActionButtonGroup.Builder()
        .addNotificationActionButton(hiButtonAction)
        .addNotificationActionButton(byeButtonAction)
        .build();

// Add the custom group
Airship.getPushManager().addNotificationActionButtonGroup("custom group", buttonGroup);
```




