# Push Notifications Comprehensive guides for implementing push notifications, including setup, notification channels, and advanced customizations. # Push Notifications > How to configure your application to receive and respond to notifications. ## Push Provider Setup Configure a push provider to enable push notifications on Android devices. ### FCM 1. Follow [FCM Android Setup](https://firebase.google.com/docs/android/setup) to configure your Android application to connect to Firebase. 1. Add the `urbanairship-fcm` dependency to your application's build.gradle file: #### Gradle Kotlin ```kotlin implementation("com.urbanairship.android:urbanairship-fcm:$airshipVersion") ``` #### Gradle Groovy ```groovy implementation "com.urbanairship.android:urbanairship-fcm:$airshipVersion" ``` ### ADM 1. Follow [Amazon's documentation](https://developer.amazon.com/docs/adm/integrate-your-app.html#store-your-api-key-as-an-asset) to store your API key as an asset. 1. Add the `urbanairship-adm` dependency to your application's build.gradle file: #### Gradle Kotlin ```kotlin implementation("com.urbanairship.android:urbanairship-adm:$airshipVersion") ``` #### Gradle Groovy ```groovy implementation "com.urbanairship.android:urbanairship-adm:$airshipVersion" ``` ### HMS 1. Follow [Huawei's documentation](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-integrating-sdk-0000001050040084) to set up the HMS SDK. > **Note:** Airship requires HMS Core Push SDK 6.3.0.304 or newer. 1. Add the `urbanairship-hms` dependency to your application's build.gradle file: #### Gradle Kotlin ```kotlin implementation("com.urbanairship.android:urbanairship-hms:$airshipVersion") ``` #### Gradle Groovy ```groovy implementation "com.urbanairship.android:urbanairship-hms:$airshipVersion" ``` ## Enable User Notifications Enabling `userNotificationsEnabled` will prompt the user for permission to send notifications. To increase the likelihood that the user will accept, you should avoid prompting the user for permission immediately, and instead wait for a more appropriate time in the app. The Airship SDK makes a distinction between `user notifications`, which can be seen by the user, and other forms of push that allow you to send data to your app silently, or in the background. Enabling or disabling user notifications is a preference often best left up to the user, so by default, user notifications are disabled. #### Kotlin ```kotlin Airship.push.userNotificationsEnabled = true ``` #### Java ```java Airship.getPush().setUserNotificationsEnabled(true); ``` > **Note:** For apps that target Android 13 (API 33) and above, enabling user notifications will display a runtime permission prompt to allow notifications to be sent. > > To increase the likelihood that the user will accept, you should avoid prompting the user for permission immediately on app startup, and instead wait for a more appropriate time to prompt for notification permission. ## Handle Notification Events The Airship SDK provides several callbacks for when a push is received or a notification is interacted with. Apps can use these callbacks to do custom push processing. Registering for a callback is optional, the SDK will automatically launch the application without the need to set a callback. #### Kotlin ```kotlin Airship.push.addPushListener { message: PushMessage, notificationPosted: Boolean -> // Called when a message is received } Airship.push.notificationListener = object : NotificationListener { override fun onNotificationPosted(notificationInfo: NotificationInfo) { // Called when a notification is posted } override fun onNotificationOpened(notificationInfo: NotificationInfo): Boolean { // Called when a notification is tapped. // Return false here to allow Airship to auto launch the launcher activity return false } override fun onNotificationForegroundAction( notificationInfo: NotificationInfo, actionButtonInfo: NotificationActionButtonInfo ): Boolean { // Called when a notification action button is tapped. // Return false here to allow Airship to auto launch the launcher activity return false } override fun onNotificationBackgroundAction( notificationInfo: NotificationInfo, actionButtonInfo: NotificationActionButtonInfo ) { // Called when a background notification action button is tapped. } override fun onNotificationDismissed(notificationInfo: NotificationInfo) { // Called when a notification is dismissed } } ``` #### Java ```java Airship.getPush().addPushListener((message, notificationPosted) -> { // Called when any push is received }); Airship.getPush().setNotificationListener(new NotificationListener() { @Override public void onNotificationPosted(@NonNull NotificationInfo notificationInfo) { // Called when a notification is posted } @Override public boolean onNotificationOpened(@NonNull NotificationInfo notificationInfo) { // Called when a notification is tapped. // Return false here to allow Airship to auto launch the launcher activity return false; } @Override public boolean onNotificationForegroundAction(@NonNull NotificationInfo notificationInfo, @NonNull NotificationActionButtonInfo actionButtonInfo) { // Called when a notification action button is tapped. // Return false here to allow Airship to auto launch the launcher activity return false; } @Override public void onNotificationBackgroundAction(@NonNull NotificationInfo notificationInfo, @NonNull NotificationActionButtonInfo actionButtonInfo) { // Called when a background notification action button is tapped. } @Override public void onNotificationDismissed(@NonNull NotificationInfo notificationInfo) { // Called when a notification is dismissed } }); ``` ## Silent Notifications Silent notifications are push messages that do not present a notification to the user. These are typically used to briefly wake the app from a background state to perform processing tasks or fetch remote content. > **Important:** We recommend that you thoroughly test your implementation to confirm that silent notifications do not generate any device notifications. For Android, all push messages are delivered in the background, but default Airship will treat messages without an `alert` as silent. > **Note:** Pushes sent without an `alert` property do not have guaranteed delivery. Factors affecting delivery include battery life, whether the device is connected to WiFi, and the number of silent pushes sent within a recent time period. These metrics are determined solely by Android and FCM. Therefore, this feature is best used for supplementing the regular behavior of the app rather than providing critical functionality. For instance, an app could use a silent push to pre-fetch new data ahead of time in order to reduce load times when the app is later launched by the user. ## Next Steps - [Notification Channels](https://www.airship.com/docs/developer/sdk-integration/android/push-notifications/advanced-customizations/#notification-channels) - Create custom notification channels for Android - [Custom Notification Provider](https://www.airship.com/docs/developer/sdk-integration/android/push-notifications/advanced-customizations/#custom-notification-provider) - Customize how notifications are displayed - [Interactive Notifications](https://www.airship.com/docs/developer/sdk-integration/android/push-notifications/advanced-customizations/#interactive-notifications) - Add action buttons to notifications If push notifications aren't working as expected, see [Troubleshooting Push Notifications](https://www.airship.com/docs/developer/sdk-integration/android/troubleshooting/push-notifications/) to check notification status and fix common issues. # 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 { 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. ![Default notification layout using AirshipNotificationProvider](https://www.airship.com/docs/images/default-factory-notification_hu_828cc11f66780a38.webp) *Default notification layout using AirshipNotificationProvider* ## 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); ```