Skip to main content

Android Notification Terminology

Notification Channel​

Read more about notification channels here.

Notification Channel Group​

Read more about notification channel groups here.

Notification Delegate​

Read more about notification delegates here.

Notification Category​

It is possible to assign a category for your notification using setCategory() method. Category may be used by the system for ranking and filtering.

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Ready for Upside Down Cake?")
.setContentText("Tap here to learn more about Android 14")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_RECOMMENDATION)
val notification = builder.build()

There are a lot of categories you can choose based on your needs:

CategoryDescription
CATEGORY_ALARMAlarm or timer
CATEGORY_CALLIncoming call (voice or video) or similar synchronous communication request
CATEGORY_EMAILAsynchronous bulk message (email)
CATEGORY_ERRORError in background operation or authentication status
CATEGORY_EVENTCalendar event
CATEGORY_LOCATION_SHARINGTemporarily sharing location
CATEGORY_MESSAGEIncoming direct message (SMS, instant message, etc.)
CATEGORY_MISSED_CALLMissed call
CATEGORY_NAVIGATIONMap turn-by-turn navigation
CATEGORY_PROGRESSProgress of a long-running background operation
CATEGORY_PROMOPromotion or advertisement
CATEGORY_RECOMMENDATIONA specific, timely recommendation for a single thing. For example, a news app might want to recommend a news story it believes the user will want to read next.
CATEGORY_REMINDERUser-scheduled reminder
CATEGORY_SERVICEIndication of running background service
CATEGORY_SOCIALSocial network or sharing update
CATEGORY_STATUSOngoing information about device or contextual status
CATEGORY_STOPWATCHRunning stopwatch
CATEGORY_SYSTEMSystem or device status update. Reserved for system use.
CATEGORY_TRANSPORTMedia transport control for playback
CATEGORY_WORKOUTTracking a user's workout

Notification Actions​

You can add action buttons for your notifications by using addAction() method:

import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.app.NotificationCompat

val customIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://android-notebook.hanmajid.com/docs/android-14"),
)
val pendingIntent = PendingIntent.getActivity(
context,
1,
customIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
)
val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Ready for Upside Down Cake?")
.setContentText("Let's start your journey on Android 14!")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.addAction(
NotificationCompat.Action.Builder(
R.drawable.baseline_rocket_launch_24,
"Learn more",
pendingIntent,
).build()
)
val notification = builder.build()

If you post the notification above, you would get this display:

You can add up to 3 actions for your notification.

Notification Lockscreen Visibility​

You can customize your notification behavior related to lockscreen by using setVisibility() method:

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Ready for Upside Down Cake?")
.setContentText("Tap here to learn more about Android 14")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setVisibility(NotificationCompat.VISIBILITY_SECRET)
val notification = builder.build()

There are 3 kinds of lockscreen visibility you can choose based on your needs:

Lockscreen VisibilityDescriptionScreenshot
VISIBILITY_PRIVATE (Default)Show this notification on all lockscreens, but conceal sensitive or private information on secure lockscreens.
VISIBILITY_PUBLICShow this notification in its entirety on all lockscreens.
VISIBILITY_SECRETDo not reveal any part of this notification on a secure lockscreen.

Showing Public Version of Notification​

If you set your notification's lockscreen visibility as VISIBILITY_PRIVATE (default value), you can supply a different notification to be shown in the lockscreen by using setPublicVersion() method:

import android.content.Context
import androidx.core.app.NotificationCompat

val channelId = "my-channel-id"
// This is the public version of the notification.
val publicVersion = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("This is public version")
.setContentText("Testing public...")
.build()

val builder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("This is the real version")
.setContentText("Testing real...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setPublicVersion(publicVersion)
val notification = builder.build()

If you post the notification above, you would notice that the content will be different on lockscreen and notification drawer:

LocationScreenshot
Lockscreen
Notification Drawer

Notification Progress​

It is possible to add a linear progress indicator inside your notification by using setProgress() method. You can choose the type of the indicator: indeterminate or not.

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Downloading New Content")
.setContentText("Please wait...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setProgress(
100,
20,
false, // Set this to `true` to make it indeterminate
)
val notification = builder.build()

If you post the notification above, you would get this display:

Notification SubText​

In addition to content title and content text, you can also add subtext to your notification. There are no guarantees where exactly it will be displayed.

As of Android 7.0 (API level 24), subtext is displayed in the notification header area. Before Android 7.0, subtext will be displayed in the third area in the platform notification template, occupying the same place as notification progress.

To add subtext in your notification, simply use setSubText() method:

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Content Title")
.setContentText("Content Text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setSubText("Subtext")
val notification = builder.build()

If you post the notification above (in Android >= 7.0), you would get this display:

Notification Timestamp​

By default, notification will display timestamp in the notification header area:

Notification Timestamp Visibility​

You can hide the timestamp by using setShowWhen() method:

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Content Title")
.setContentText("Content Text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setShowWhen(false)
val notification = builder.build()

If you post the notification above, the timestamp would be hidden:

Custom Notification Timestamp Value​

By default, the timestamp shown in notification will be the time you post the notification. However, we can alter the timestamp value by using setWhen() method:

import android.content.Context
import androidx.core.app.NotificationCompat
import java.util.Calendar

val threeHoursAgo = Calendar.getInstance()
threeHoursAgo.add(Calendar.HOUR, -3)

val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Ready for Upside Down Cake?")
.setContentText("Tap here to learn more about Android 14")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setWhen(threeHoursAgo.timeInMillis)
val notification = builder.build()

In the example above, the notification timestamp will show the timestamp from 3 hours ago.

Notification Chronometer​

It is possible to replace the notification timestamp with chronometer by using setUsesChronometer() method:

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, "my-channel-id")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Content Title")
.setContentText("Content Text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setUsesChronometer(true)
val notification = builder.build()

If you post the notification above, the timestamp would be replaced by chronometer that show the elapsed time since the notification is posted:

You can combine this method with setWhen() method specified above.

Dismissable vs. Non-Dimissable Notifications​

A notification can be dismissed (removed from the notification drawer) by swiping it left or right:

Video source: material.io

By default, notifications we create are dismissable.

Non-Dismissable Notifications​

Non-dismissable (or "ongoing") notifications are notifications that cannot be dismissed by the user. That said, your applications needs to take care of dismissing them. Taken from the documentation:

They are typically used to indicate a background task that the user is actively engaged with (e.g., playing music) or is pending in some way and therefore occupying the device (e.g., a file download, sync operation, active network connection).

To make a notification non-dismissable, there are 2 ways.

The first way is to use setOngoing() method:

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Ready for Upside Down Cake?")
.setContentText("Tap here to learn more about Android 14")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setOngoing(true)

The second way is to set FLAG_ONGOING_EVENT:

import android.app.Notification
import android.content.Context
import androidx.core.app.NotificationManagerCompat

with(NotificationManagerCompat.from(context)) {
// Check for permission here...

val notification = builder.build().apply {
flags = Notification.FLAG_ONGOING_EVENT
}
notify(NOTIFICATION_ID, notification)
}

Android 14 Behavior Change​

In Android 14 there is a behavior change that allows users to dismiss non-dismissable notifications.

However, the notifications are still non-dismissable when:

  • the phone is locked, or
  • the user taps Clear all notification action.

The notifications are also still non-dismissable when:

  • the notifications are set as CallStyle,
  • Device policy controller (DPC) and supporting packages for enterprise,
  • Media notifications, or
  • The default Search Selector package.

Auto-Dismissable Notifications​

We can automatically dismiss notifications after a certain amount of time by using setTimeoutAfter() method:

import android.content.Context
import androidx.core.app.NotificationCompat

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Ready for Upside Down Cake?")
.setContentText("Tap here to learn more about Android 14")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setTimeoutAfter(1000L * 3) // dismiss after 3 seconds

App Notification Block State​

It is possible for user to block the entire notifications from our application with one toggle. The toggle is accessible to user by accessing App Info > Notifications:

By tapping "All Android Notebook notifications" in the image above, all of our app's notifications will be blocked.

Listening to App Notification Block State​

In our apps, we can listen whenever user blocks/unblocks our application's notifications by using the ACTION_APP_BLOCK_STATE_CHANGED intent action:

AppNotificationBlockStateBroadcastReceiver.kt
package com.hanmajid.androidnotebook

import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log

/**
* Simple broadcast receiver that listens to
* [NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED] intent action.
*/
class AppNotificationBlockStateBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val isBlocked = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
intent.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
} else {
null
}
Log.i("TAG", isBlocked.toString())
}
}

Then we can register the broadcast receiver within our context:

import android.app.NotificationManager
import android.content.Context
import android.content.IntentFilter
import android.os.Build
import androidx.core.content.ContextCompat

// Initialize broadcast receiver within your context.
val broadcastReceiver = AppNotificationBlockStateBroadcastReceiver()

// Register broadcast receiver to [context].
ContextCompat.registerReceiver(
context,
broadcastReceiver,
IntentFilter(NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED),
ContextCompat.RECEIVER_NOT_EXPORTED,
)

References​