Skip to main content

Android - Implementing Remote Notifications

In this page you'll learn how notifications are handled in your app and what are all the options at your disposal to create a great messaging experience for your users.

caution

Notificare supports several types of interactive and actionable notifications that will be handled for you without any extra development. If you are going to prevent this default behaviour, please note that you will have to either handle all the functionality yourself (metrics logging, presenting UI or collect replies) or if you don't, you understand that some features will not work as advertised.

Requesting Permission

Since Android 13, the notification permission is not granted by default and should be requested. We recommended targeting Android 13 to have more control over the request.

When running Android 13 and targeting Android 12 or lower, users will be prompted for the permission when the notification channel is created. Typically, when the application starts.

Although permission requests must follow a recommended standard, controlling the overall experience is something unique to each application. However, you can use the following code as inspiration for your implementation.

class MainActivity : AppCompatActivity() {

private val notificationsPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (!granted) {
// User denied notifications permissions
return@registerForActivityResult
}

// You can enable remote notifications

lifecycleScope.launch {
try {
Notificare.push().enableRemoteNotifications()
} catch (e: Exception) {
// ...
}
}
}

private fun onEnableRemoteNotificationsClicked() {
// Ensure we have sufficient permissions
if (!ensureNotificationsPermission()) return

// We have sufficient permissions to enable remote notifications

lifecycleScope.launch {
try {
Notificare.push().enableRemoteNotifications()
} catch (e: Exception) {
// ...
}
}
}

private fun ensureNotificationsPermission(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return true

val permission = android.Manifest.permission.POST_NOTIFICATIONS
val granted = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
if (granted) return true

if (shouldShowRequestPermissionRationale(permission)) {
AlertDialog.Builder(this)
.setTitle(R.string.app_name)
.setMessage(R.string.notifications_permission_rationale)
.setPositiveButton(android.R.string.ok) { _, _ ->
notificationsPermissionLauncher.launch(permission)
}
.show()

return false
}

notificationsPermissionLauncher.launch(permission)
return false
}
}

Enabling Notifications

In order to enable the device to receive notifications, all that you need to do is invoke a single method.

// Check if the user has previously enabled remote notifications.
Notificare.push().hasRemoteNotificationsEnabled

Typically, the step above is done during some form of user onboarding. When the user already went through that flow, we automatically enable notifications when Notificare launches.

You can also check whether the user enrolled on remote notifications.

// Check if the user has previously enabled remote notifications.
Notificare.push().hasRemoteNotificationsEnabled

Additionally, you can check if the user has disabled notifications in the System Settings.

Notificare.push().allowedUI

If you want to also give access to your app's settings view from the notification settings in the device's settings, you can listen to the NOTIFICATION_PREFERENCES intent. This will add a link to your activity inside the device's settings.

<activity android:name=".SettingsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.NOTIFICATION_PREFERENCES" />
</intent-filter>
</activity>

alt text

Disabling remote notifications

Disabling remote notifications can be achieved in the same fashion as enabling them.

Notificare.push().disableRemoteNotifications()

When this method is called, we will automatically register your device to never receive remote notifications, although you will still maintain the same user profile, inbox messages and enjoy all the other services your plan supports. You can at anytime request a re-register for push notifications if you wish to.

Receiving Notifications

Due to the changes in Android 12 when it comes to handling notifications, your Activity needs to become the trampoline. Assuming you will let your MainActivity receive the notification intents, you need to declare the following intent-filter it in your AndroidManifest.xml.

<activity android:name=".MainActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="re.notifica.intent.action.RemoteMessageOpened" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

You should configure the Activity receiving these intents with android:launchMode="singleTask" to prevent recreating it several times when processing the series of intents fired from opening a notification from the notifications drawer. You can also use android:launchMode="singleTop", but be aware the OS will recreate it when processing deep links triggered from the NotificationActivity. For more information on launch modes, refer to the Android documentation.

Additionally, in order to process those intents, you need to take care of them in your MainActivity as shown below.

override fun onCreate(savedInstanceState: Bundle?) {
// more code ...

if (intent != null) handleIntent(intent)
}

override fun onNewIntent(intent: Intent?) {
// more code ...

if (intent != null) handleIntent(intent)
}

private fun handleIntent(intent: Intent) {
if (Notificare.push().handleTrampolineIntent(intent)) {
Log.d(TAG, "Trampoline intent handled.")
return
}

// more code ...
}

Listening to Received Notifications

Once you're receiving notifications in your app, we can dive deeper and fully understand how they are handled. Our library takes care of placing a notification in the Notification Center so developers will not need to take any action in order to display notifications. If you want to be notified incoming notifications in your own Intent Receiver, for example to add a badge to your application launcher icon, you can leverage the NotificarePushIntentReceiver.

Start by creating a subclass of that Intent Receiver and overriding the onNotificationReceived() method.

class CustomPushIntentReceiver: NotificarePushIntentReceiver() {
override fun onNotificationReceived(
context: Context,
notification: NotificareNotification,
deliveryMechanism: NotificareNotificationDeliveryMechanism
) {
// more code ...
}
}

In order to receive those intents in your Intent Receiver you need to let Notificare know about your class.

Notificare.push().intentReceiver = CustomPushIntentReceiver::class.java

Lastly, declare your custom intent receiver in your AndroidManifest.xml.

<receiver
android:name=".CustomPushIntentReceiver"
android:exported="false" />

Presenting notifications

A notification can be opened by either tapping the actual notification or by tapping an action inside the notification. We will emit an intent in each case to an Activity that declares the appropriate intent-filters. To receive those intents, add the following to your MainActivity in the AndroidManifest.xml.

<activity android:name=".MainActivity">
<intent-filter>
<action android:name="re.notifica.intent.action.NotificationOpened" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="re.notifica.intent.action.ActionOpened" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

To handle the intents above, you can take the managed approach and use our NotificarePushUI module which takes care of all the events and UI types as well as actions, or you can fully take over and present them however you prefer. However, be aware if you take the non-managed approach as you will have to deal with all aspects and types of presenting the notifications, including the events needed to show the user's engagement in our Dashboard.

The code below illustrates how this works when using the managed approach.

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// more code ...

if (intent != null) handleIntent(intent)
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// more code ...

if (intent != null) handleIntent(intent)
}

private fun handleIntent(intent: Intent) {
if (NotificarePushCompat.handleTrampolineIntent(intent)) return;

Notificare.push().parseNotificationOpenedIntent(intent)?.also { result ->
Notificare.pushUI().presentNotification(this, result.notification)
return
}

Notificare.push().parseNotificationActionOpenedIntent(intent)?.also { result ->
Notificare.pushUI().presentAction(this, result.notification, result.action)
return
}

// more code ...
}

Additionally, when using the managed approach, you can listen to Notification lifecycle events and perform any additional steps you may require. Let your Activity implement the NotificarePushUI.NotificationLifecycleListener and add following methods as needed.

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// more code ...

Notificare.pushUI().addLifecycleListener(this)
}

override fun onDestroy() {
super.onDestroy()
// more code ...

Notificare.pushUI().removeLifecycleListener(this)
}

override fun onNotificationWillPresent(notification: NotificareNotification) {

}

override fun onNotificationPresented(notification: NotificareNotification) {

}

override fun onNotificationFinishedPresenting(notification: NotificareNotification) {

}

override fun onNotificationFailedToPresent(notification: NotificareNotification) {

}

override fun onNotificationUrlClicked(notification: NotificareNotification, uri: Uri) {

}

override fun onActionWillExecute(notification: NotificareNotification, action: NotificareNotification.Action) {

}

override fun onActionExecuted(notification: NotificareNotification, action: NotificareNotification.Action) {

}

override fun onActionFailedToExecute(
notification: NotificareNotification,
action: NotificareNotification.Action,
error: Exception?
) {

}

override fun onCustomActionReceived(
notification: NotificareNotification,
action: NotificareNotification.Action,
uri: Uri,
) {

}