Android - Notifications
Notification handling is a frequent request we encounter. Recognizing that not all developers are accustomed to Android's specific way of managing notifications, we have decided to provide additional information and tutorials to assist in this area.
Firebase Cloud Messaging
Android push notifications are delivered through Firebase Cloud Messaging (FCM). When a device enrolls, FCM issues a unique registration token that identifies the app instance. A backend server sends messages to that token, and FCM delivers them to the device.
Q2 provides the FCM integration layer through two core modules that must be present in every app using push:
- Q2PushService (
com.app.q2.modules.push.q2_push_service:q2_push_service) — Q2's implementation of Firebase'sFirebaseMessagingService. It handles device registration, token management, and routes incoming messages to the appropriate module. - PushEnrollment (
com.q2.push:push) — The UI flow that prompts users to opt into push notifications.
Partner modules receive push notifications through the
PushReceiverModule interface. Q2's core broadcasts
each incoming FCM message to every registered PushReceiverModule, allowing each module to
independently decide whether the message belongs to it via willConsumeNotification(). This means
multiple modules can coexist and handle their own notifications without interfering with each other.
Setup
Set the push flag to true
In the settings.json file, ensure the push flag is set to true. This activates push
notification functionality for the app.
"push": true
Enable Q2PushService in mob_modules
In the mob_modules section, ensure the Q2PushService module is included and set to
enabled: true. This module handles core push notification services.
{
"id": 1,
"name": "Q2PushService",
"moduleType": "push",
"classPath": "com.app.q2.modules.push.q2_push_service.Q2PushService",
"include": ":modules:q2_push_service",
"googleServicesRequired": "true",
"data": {},
"enabled": true
}
Enable PushEnrollment in sdk_modules
In the sdk_modules section, enable the PushEnrollment module, which handles the push
enrollment process (i.e., prompting the user to opt-in for push notifications).
{
"name": "PushEnrollment",
"identifier": "pushEnrollment",
"classPath": "com.q2.push.enrollmentprompt.EntryPoint",
"include": ":modules:push",
"data": {},
"enabled": true
}
Full Example
Here is a complete settings.json showing a push-enabled DevApp configuration with a partner
module that uses push:
{
"applicationName": "Dev App",
"package": "com.q2.devapp",
"targetURLBase": "https://stack.q2developer.com/sdk/native/ardent",
"targetPageName": "uux.aspx",
"containerVersion": "26.3.0",
"sdk_modules": [
{
"name": "com.example.notifications_module",
"identifier": "com.example.notifications_module",
"classPath": "com.example.notifications.NotificationsModule",
"enabled": true,
"data": {}
},
{
"name": "PushEnrollment",
"identifier": "pushEnrollment",
"classPath": "com.q2.push.enrollmentprompt.EntryPoint",
"include": ":modules:push",
"data": {},
"enabled": true
}
],
"mob_modules": [
{
"id": 1,
"name": "Q2PushService",
"moduleType": "push",
"classPath": "com.app.q2.modules.push.q2_push_service.Q2PushService",
"include": ":modules:q2_push_service",
"googleServicesRequired": "true",
"data": {},
"enabled": true
}
],
"nativeLogin": true,
"push": true
}
Once these settings are applied, the DevApp will be set up to use push notifications, allowing you to receive notifications and manage the push enrollment flow.
How the Q2 Mobile App Handles Notifications
The handling of notifications varies based on the application's state, which can either be backgrounded or foregrounded.
Backgrounded: If the application is in the background (either minimized or closed),
the operating system takes the initial responsibility of handling the notification. This
is why you see the notification at the top of your device, given that a message and title
are provided. Upon clicking, it launches the Activity marked as the LAUNCHER of the
application. This activity seizes the intent and forwards it to your module via the
LifecycleModule.willHandleNewIntent() method.
Foregrounded: Conversely, when the application is in the foreground, our
FirebaseMessagingService directly receives the message and forwards it to the
PushReceiverModule.willConsumeNotification(). If the notification isn't consumed at this
stage, and no other entity does so, the FirebaseMessagingService will try to display the
notification, provided a title and body exist. If these elements are absent, no
notification will be displayed.
Given that the application's state influences how notifications are consumed, it's
recommended to construct your module using both the LifecycleModule and the
PushReceiverModule. This approach ensures that notifications are handled effectively in
both states.
Notification Channels
As of Android 8.0, Notification Channels are a way to modify the notification displayed to the user.
Mobile SDK allows you to create your own notification channels. This is done by using the
createNotificationChannel() method in the PushReceiverModule. This method provides you
with the NotificationManager.
val channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
channelName,
channelImportance
).apply {
description = channelDescription
}
notificationManager?.createNotificationChannel(channel)
Once the notification channel is created, it can be referred to in the notification
builder. This is achieved by invoking the setChannelId() method in the
NotificationCompat.Builder and passing the channel ID as the argument.
Notification Message
Firebase notifications have a specific structure that must be followed. The following is an example of a standard notification payload which contains a title and body:
{
"message": {
"token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification": {
"title": "My Mobile SDK Test Message",
"body": "Mobile SDK is amazing!"
}
}
}
You can also send data type notifications. These notifications are used to send data to the device without displaying a notification:
{
"message": {
"token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data": {
"myUri": "messages"
}
}
}
You also have the option to consolidate them into a single message. This is beneficial when you aim to display a notification while simultaneously sending data to the device.
Any values transmitted in the data object can be accessed either from the Intent
parameter in LifecycleModule.willHandleNewIntent() or from the RemoteMessage parameter
in PushReceiverModule.willConsumeNotification().
Testing Push Notifications in DevApp
To test push notifications locally, you need your own Firebase project with Cloud Messaging enabled.
Firebase Setup
- Go to console.firebase.google.com and create or open a project.
- Enable Firebase Cloud Messaging for the project.
- Download the
google-services.jsonfile from the console. - Replace the existing
google-services.jsonin the DevApp's root folder with yours.
Once in place, the DevApp's MyFirebaseMessagingService will generate an FCM registration token on startup.
Getting Your FCM Token
With the google-services.json configured correctly, the DevApp writes the FCM token to Logcat
on startup. Look for a log entry from MyFirebaseMessagingService. You can also retrieve the
token programmatically:
SdkUtils.getSdkPushUtils().getRegistrationToken()
Use this token when sending test messages from the Firebase console or your backend.
DevApp Build Dependencies
Push notification modules require two additional dependencies in your DevApp's build.gradle.
These are not included in core and must be declared explicitly:
implementation "com.q2.push:push:$q2Version"
implementation "com.app.q2.modules.push.q2_push_service:q2_push_service:$q2Version"
Without these, the app will crash on startup when push is enabled in your configuration.