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:
Category | Description |
---|---|
CATEGORY_ALARM | Alarm or timer |
CATEGORY_CALL | Incoming call (voice or video) or similar synchronous communication request |
CATEGORY_EMAIL | Asynchronous bulk message (email) |
CATEGORY_ERROR | Error in background operation or authentication status |
CATEGORY_EVENT | Calendar event |
CATEGORY_LOCATION_SHARING | Temporarily sharing location |
CATEGORY_MESSAGE | Incoming direct message (SMS, instant message, etc.) |
CATEGORY_MISSED_CALL | Missed call |
CATEGORY_NAVIGATION | Map turn-by-turn navigation |
CATEGORY_PROGRESS | Progress of a long-running background operation |
CATEGORY_PROMO | Promotion or advertisement |
CATEGORY_RECOMMENDATION | A 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_REMINDER | User-scheduled reminder |
CATEGORY_SERVICE | Indication of running background service |
CATEGORY_SOCIAL | Social network or sharing update |
CATEGORY_STATUS | Ongoing information about device or contextual status |
CATEGORY_STOPWATCH | Running stopwatch |
CATEGORY_SYSTEM | System or device status update. Reserved for system use. |
CATEGORY_TRANSPORT | Media transport control for playback |
CATEGORY_WORKOUT | Tracking 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 Visibility | Description | Screenshot |
---|---|---|
VISIBILITY_PRIVATE (Default) | Show this notification on all lockscreens, but conceal sensitive or private information on secure lockscreens. | |
VISIBILITY_PUBLIC | Show this notification in its entirety on all lockscreens. | |
VISIBILITY_SECRET | Do 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:
Location | Screenshot |
---|---|
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:
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​
- Changes to how users experience non-dismissible notifications | Android Developers
- Notification Behavior | Material Design
ACTION_APP_BLOCK_STATE_CHANGED
| Android DevelopersFLAG_ONGOING_EVENT
| Android DevelopersNotificationCompat
| Android DeveloperssetCategory()
| Android DeveloperssetOngoing()
| Android DeveloperssetPublicVersion()
| Android DeveloperssetProgress()
| Android DeveloperssetShowWhen()
| Android DeveloperssetSubText()
| Android DeveloperssetUsesChronometer()
| Android DeveloperssetVisibility()
| Android DeveloperssetWhen()
| Android Developers