Android Notification - txgz999/Mobile GitHub Wiki
- https://developer.android.com/guide/topics/ui/notifiers/notifications#Templates
- https://developer.android.com/training/notify-user/build-notification
- https://developer.android.com/training/notify-user/custom-notification
A notification is a message that Android displays outside your app's UI to provide the user with reminders, communication from other people, or other timely information from your app.
Notification can come from a local app on the device, which is called a local notification, or from a remote sender, which is called a remote notification or a push notification. The most famous type of push notifications is the Firebase Cloud Message (FCM). Not every FCM message is a push notification. Only when the FCM message contains a notification section, it becomes a push notification if the target app is in the background or is not running. If the target app is in the foreground or the FCM message does not contain a notification section, the message is not visible to user and only the target app gets the message. In the following, we concentrate on local notification.
We can have the following method in an Activity class. When called, creates a local notification:
private void sendMessage() {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
String channelId = "txgz999_test";
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World")
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}We also need to create a notification channel, which can be called from the OnCreate method of the activity, or be part of the method above
String channelId = "txgz999_test";
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Channel human readable title", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}Notice that there is no harm to try to create a channel if it already exists, but the new settings would not be applied. So if we want to change the channel settings (e.g. importance, show/hide badge), it is better to uninstall the app first, then run the app again. It happens more that once that I changed the importance from default to high, and get puzzled of why the heads-up box still does not appear.
There are many options we can set when creating the notification channel and notification, which affect how the notification behaves. We will discuss some of them below. Some of these options are not used anymore on newer versions of Android, including:
- ticker: replaced by heads-up, see https://www.androidpit.com/how-to-get-ticker-text-notifications-on-lollipop
- priority: it used to determine if a notification is heads-up one, now that is determined at channel level
- when: although still working, this setting does not control the time the notification is sent, it just set the timestamp displayed in the notification drawer
When a notification arrives, a notification icon (specified as the small icon in the notification creation) would appear on the status bar. User can swipe down on the status bar to open the notification drawer and view the notification detail there. The notification entry in the notification drawer normally contains the following content:
- first line contains small notification icon, app name, optionally a sub text, a when time stamp
- the time stamp represents the time that notification arrives, but may be display in different format, e.g. now, 2m
- second line contains content title
- third line contains content text
- title and text each takes one line only, and would be truncated if too long
- optionally fourth line contains a list of actions
- optionally a large icon at the right side, within the second and third lines
All these elements can be set as follows:
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World")
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_test1))
.setSubText("Test")
.setContentIntent(pendingIntent);Android provides standard layout for notification message, but also allow developers to provide custom layout. Those custom layout are not limited to display the standard contents (small icon, app name, sub text, when, content title, content text, actions and large icon), they can display any custom contents. We will discuss that later.
Normally when a notification arrives, only a notification icon would appear on the status bar, and user needs to swipe down on the status bar to see the notification detail in the notification drawer. But we can make the notification to appear as a popup box on the screen when it come. This type of notification is called heads-up notification. In early versions of Android, a notification is a heads-up notification if it’s priority is set to high. In newer versions since version 26, every notification needs to belong to a channel, and the notification is a heads-up notification if it’s channel has importance set to high or maximum.
NotificationChannel channel = new NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_HIGH);There is another way to create heads-up notification, which is to create full-screen activity.
I saw in some articles, the heads-up message box looks just like the notification entry in the notification drawer. But on my Android SDK version 28 emulator, the first line of the notification entry (small icon, app name, sub text and when) does not appear in the heads-up box. Confirmed in https://stackoverflow.com/questions/61135069/android-9-device-misses-icon-on-application-in-heads-up-notification. This popup box can be customized as well.
Normally a heads-up notification would disappear from screen after a few seconds. But if it is created with full-screen intent, it will stay on screen forever until user clicks it.
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World")
.setFullContentIntent(pendingIntent, false);A notification goes to the notification drawer when it comes. When user clicks that notification entry in the notification drawer, the notification drawer will be closed, and the target activity of that notification would come to foreground. A question arises: is that notification still in the notification drawer, or dismissed from there? This is determined by the auto-cancel option of the notification. By default, that value is false, which means the notification remains in the notification drawer. But if that value is set to true, then the notification is dismissed once user clicks it in the notification drawer.
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World")
.setAutoCancel(true)
.setContentIntent(pendingIntent);Normally a notification can be dismissed from the notification drawer when user swipes it left or right, or clicks the ClearAll link at the bottom of the list. This behavior is controlled by the onGoing option, which is false by default. But if this value is set to true, then user can not dismiss it anymore, and the ClearAll link is not there anymore. Only the app itself can dismiss the notification.
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World")
.setOnGoing(true)
.setContentIntent(pendingIntent);There is a feature called app icon badge or notification dot, which puts a little number or dot on the top right corner of the app icon on the home screen when that app has notifications. This feature is provided by the home screen launcher. There are many such launchers in the market, and the one installed on my emulator is the Pixel Launcher provided by Google. Pixel Launcher supports notification dot only, it does not support number badge.
For the badge or dot to appear, all of the following condition need to be met:
- Grant the home screen launcher to have access to all notifications on the device: Settings->Apps & notifications->Default Apps->Home app, There we can find the current home screen launcher (home app) being used currently. Clicking the gear icon next to the home screen launcher name, the Home Setting screen appears. the first item there is Notification dots. Verify it says On. If there is a triangle exclamation icon next to it, and with the word Notification Access needed, clicking the icon would lead to the steps to enable the home screen launcher to have access to all notifications on the device. If we already the app that serves as home screen launcher currently, then we can also get to the same place from Settings->Apps & notifications->Special app access->Notification access, the home screen launcher should appear in the list there. Enable notification access for that launcher there. Since this setting is not enabled by default, this is a must done step
- Enable notification dots for all apps or one particular app. Go to Settings->Apps & notifications->Notifications->Allow notification dots, make sure it is enabled. Or if you don't want to enable it for all apps, then find the app under consideration, and enable notification dots for that app. By default both are enabled
- At the notification channel level, there is an option to show badge or not. The method name is
setShowBadge. Notice that the default value is true. So unless you want to hide badge (dot or number), there is no need to call it. This setting may be ignored by some home screen launcher, see https://stackoverflow.com/questions/58815789/what-can-be-the-reason-behind-the-failure-of-setshowbadgefalse
There are other settings that prevent the badge to appear, such as battery optimization and Do Not Disturb mode. For details, see https://milagromobilemarketing.com/blog/easy-tricks-to-fix-android-oreo-notification-dots-not-working/.
When long pressing the app icon, notification information appears as a context menu below the normal App Info link. This menu shows the number of active notifications, as well as the detail of the last notification: title and text, without their html formatting, as well as the large notification icon. We can also use the small notification icon instead, with the icon color if set, as follows (https://developer.android.com/training/notify-user/badges)
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_large))
.setContentTitle("Greeting")
.setContentText("Hello World")
.setColor(0x0000FF)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
.setContentIntent(pendingIntent);There is also an option to not displaying icon, but on my emulator the large icon still shows up when I use it. Clicking the menu would take user to the app, but that does not dismiss any notification. Thus the dot will remain there. The dot would disappear only if all notifications of that app have been dismissed.
Confirmed from https://support.google.com/pixelphone/thread/8745516 that the Pixel Launcher supports only dots, but we can download other launchers that support number badge from the Google Play Store. Nova Launcher supports badge number only in the premium version. Microsoft Launcher is free and supports badge number from (Microsoft) Launcher Settings Icon->Home screen->Notification badges->Show notification badges, Badge style. The problem I have with this launcher is the app icon has a different context menu, and the new notifications don’t appear, so there seems no way to take user to the new notifications from the app icon. There is another setting at the same place: Open apps to reset notification badges. If that is enabled, then when user opens an app, say by clicking the app icon, the badge disappear from the app icon, although the notifications stays in the notification drawer. I guess the idea here for may notifications, we can just open the app to see the notification details.
We can set the color of notification small icon as well as the app name that appear in the notification drawer as follows:
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World")
.setColor(0x0000FF)
.setContentIntent(pendingIntent);But on my Android SDK version 28 emulator, only the small icon changes color, not the app name. confirmed in https://stackoverflow.com/questions/56914743/change-color-of-notification-title-android.
- https://stackoverflow.com/questions/30101824/android-wear-notification-with-different-text-styles
- https://commonsware.com/blog/Android/2010/05/26/html-tags-supported-by-textview.html
- https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML
We can add styles to the content title and text using certain html tags, most of them are those for paragraph formatting.
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle(Html.fromHtml("<font color='red'>Greeting</font>"))
.setContentText(Html.fromHtml("Hello <b>World</b>"))
.setContentIntent(pendingIntent);Normally the notification entry in the notification drawer truncates the content text to one line, that layout is called the standard notification template. But we can set the notification to use a different template called the big text style, then the content text can be displayed in multiple lines:
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World ...") // imagine this is a long text
.setStyle(new NotificationCompat.BigTextStyle())
.setContentIntent(pendingIntent);Now the notification entry has an arrow at the end of the first line of text to expand the content text into multiple lines and shrink it back. In this way, the notification has a collapsed content view and expanded content view. When the notification appears on the notification drawer the first time, it is displayed in the expanded view. Later when we go back to the notification drawer, that notification would be in the collapsed view. Notice the heads-up box shows one line of the content text and is consistent with the collapsed view.
In the code above, we specify content text only in setContentText, thus the text displayed in the collapsed view is exactly the first line of the text in the expanded view. But it does not have to be that way. We can specify explicitly what needs to be the content in the expanded view:
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Greeting")
.setContentText("Hello World ...") // imagine this is a long text
.setStyle(new NotificationCompat.BigTextStyle().bigText("Hello World 2..."))
.setContentIntent(pendingIntent);Just like the content text, the big text, i.e. the content text in the expanded view, supports certain html format tab as well. Most of these tags are those to help format paragraphs. Image tag can be added but no with or height can be specified, so images added there is only one line tall. Hyperlink can be added, but the link cannot be clicked.
Besides setting content text in the big text style, we can also set big content title and summary text there. The summary text only appears if the sub text is not set. If that is the case, then the summary text appears in the place of sub text, in both collapsed and expanded views.
Heads-up box shows the big content title, if set, as its title. It shows the big content text as its text if that text takes one line. When the big content text takes only one line, the big text style arrow does not appear.
Besides the big text style, Android provides several expandable notification templates that support expanded views. Another one is called the big picture style, where a picture can appear under the one-line content text. We can set when the notification entry is displayed in the expanded view, the content title, summary text. The large icon would still appear in the expanded view, unless we set in the big picture style a different large icon or set it to null.
Other expandable notification templates provided by Android includes the inbox style, the messaging style, and the media style.
- https://medium.com/hootsuite-engineering/custom-notifications-for-android-ac10ca67a08c
- https://stackoverflow.com/questions/41888161/how-to-create-a-custom-notification-layout-in-android
We may want to assign a unique id for each notification, because if an app creates two notifications with the same ID, then the new one would replace the old one in the notification drawer. See https://stackoverflow.com/questions/25713157/):
notificationManager.notify((int) System.currentTimeMillis(),
notificationBuilder.build());When we call the NotificationManager.notify method, the notification is sent immediately. There is no feature built in the notification manager to delay the sending of notification. If we need to send a notification in a future time once, or even recursively, we need to rely on the alarm manager. The steps are
- create a broadcasting intent that creates and carries a notification, then create an alarm manager that fires up the broadcast ping intent at a specified time, in the proper place, say inside the button click event handler
Intent notificationIntent = new Intent(this, AlarmReceiver.class); notificationIntent.putExtra("Notification", getNotification()); PendingIntent broadcastingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+ 10000, broadcastingIntent);
- create and a broadcast receiver. When the
OnReceivemethod of this class is called, find the notification passed through the intent, then fires up that notificationpublic class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Notification n = (Notification) intent.getParcelableExtra("Notification"); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify((int) System.currentTimeMillis(), n); } }
- register a broadcast receiver in AndroidManifest.xml:
<receiver android:name=".AlarmReceiver"></receiver>
Therefore scheduling notification can be easily be implemented using the alarm manager, and in NativeScript local notification plugin, this becomes a feature provided by the notification function there. Why do we need to use the alarm manager? Can we just set a timer to run the task? The alarm manager provides access to the system alarm service. Thus it can run the task at certain time even if the app is not running at that time.
