I am building a messaging application that notifies users when a new message comes in.
Because this could happen several times a day (or several times an hour), I don't want to continually throw new notifications. Instead, if the user has not dismissed a notification, I would like to update it with the number of new messages pending (following the "Stacking" design guideline).
In the Android documentation, there is an example of updating a notification with a number:
mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Sets an ID for the notification, so it can be updated
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle("New Message")
.setContentText("You've received new messages.")
.setSmallIcon(R.drawable.ic_notify_status)
numMessages = 0;
// Start of a loop that processes data and then notifies the user
...
mNotifyBuilder.setContentText(currentText)
.setNumber(++numMessages);
// Because the ID remains unchanged, the existing notification is
// updated.
mNotificationManager.notify(
notifyID,
mNotifyBuilder.build());
...
HOWEVER, this seems to assume that you are maintaining this number within your application and outside of the notification manager / builder. For a host of reasons, this is very inconvenient (and brittle) in the context of my application.
I would like to know - is there any way to read the current number assigned to a message (the equivalent of mNotifyBuilder.getNumber()) ?
FOLLOW-ON QUESTION: If reading the current number is not possible, is there a way to know from a running service if a notification has been cancelled or manually dismissed by the user ?
You notification is assigned with an ID. You can use this ID to cancel or update the same notification if it hasn't been cancelled by the user.
Straight from the documentation: "If a notification with the same id
has already been posted by your application and has not yet been
canceled, it will be replaced by the updated information."
See this post and that one.
Related
I have an app where the user can receive multiple notifications for things they need to do. The user has a choice of making some of these notifications persistent (which I achieve by calling NotificationCompat.Builder.setOngoing). At least on my version of Android which is Nougat, when more than three notifications are posted by my app they get bundled together into one notification, which makes all of them dismissible to the user in one swipe. This makes the previously persistent notifications no longer persistent. Is there a way to programmatically instruct Android not to bundle my notifications?
This is the code I use to build the notification and display it:
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(eventName + " " + notificationText)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setContentIntent(eventListPendingIntent);
if (goalInfo.goal.persistNotification) {
builder.setOngoing(true);
} else {
builder.setAutoCancel(true);
}
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(eventType.value(), builder.build());
Thanks, Nir
As per Google docs, notifications from the same app would be bundled automatically -
Note: If the same app sends four or more notifications and does not
specify a grouping, the system automatically groups them together.
So in your case, what you can do is , instead of system applying the default grouping, you can separate your notifications into two groups using a separate group key for the persistent notifications and one for the non-persistent ones.
Check the Google docs. The method Builder.setGroup() on NotificationBuilderCompat takes a string parameter which is the key.
There is a related method Builder.setGroupSummary which you should call on your Builder
Hope this is clear.
I want to group notifications into a summary.
I achieve this by having a single ID for all notifications. This way android will not create new notifications but update the existing one (reduced code):
Notification summaryNotification = new NotificationCompat.Builder(this)
.setGroupSummary(true)
.setDefaults(Notification.DEFAULT_ALL)
.setStyle(new NotificationCompat.InboxStyle()
.addLine(msg)
.setBigContentTitle("My App")
.setSummaryText("FooBar"))
.build();
mNotificationManager.notify(uuid, summaryNotification);
UUID is always the same so that the notification should be updated. However when a new notification arrives, setStyle seems to be overwritten.
This cause the old addLine(msg) to disappear. However I want the new message to be added without having some kind of notification manager server side.
Any ideas or suggestions?
I think you are misinterpreting the notification builder.
The NotificationCompat.Builder builds the complete notification, with all the content.
Reusing the same id simply tells the notification manager to replace an existing notification with the same id with the new one: (Source)
[...] update or create a NotificationCompat.Builder object, build a Notification object from it, and issue the Notification with the same ID you used previously. If the previous notification is still visible, the system updates it from the contents of the Notification object.
Thus addLine is not an operation that is performed on an existing notification, but on the new builder you created (which is empty at that time).
If you want to add a line to an existing notification with the inbox style, you will need to either
keep the original builder object, add lines as needed and resend the notification with the same id
create a new builder and add the old lines first, then the new one. You will need to store or retrieve the old lines from somewhere (depending on your application).
I'm developing a messaging app that potentially creates multiple notifications. I want it such that these notifications can be updated and deleted when stuff happens.
For example, if I receive a message from A, the notification should say "You have 1 message from A". If another message arrives, it should say "You have 2 messages from A".
Then, if I receive a message from B, it should be a consolidated notification that simply says "You have messages" rather than a separate notification from each sender.
Also, when I click on the notification, it should cancel, and if I click on the user A chat window, any notification from user A should also cancel.
Right now I've implemented the code below for generating notifications:
PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder noti = new NotificationCompat.Builder(
context).setContentTitle(context.getResources().getString(R.string.app_name))
.setContentText(notificationMessage)
.setSmallIcon(R.drawable.ic_stat_notify)
setContentIntent(pendingIntent)
setAutoCancel(true)
setWhen(System.currentTimeMillis())
setDefaults(Notification.DEFAULT_ALL);
notificationManager.notify(notifyId, noti.build());
This creates the notification just fine, and cancels it when the user clicks on it. However, I'm unsure how to update notifications, consolidate them, and cancel the appropriate ones. Does anyone know?
Thanks.
You need to use the same Notification ID all the time
public void notify(int id, Notification notification)
id - Post a notification to be shown in the status bar. If a notification with the same id has already been posted by your application and has not yet been canceled, it will be replaced by the updated information.
Just post a new notification with the same ID as the old one, that will replace the old notification with your new one.
I'm looking at this code in the Android documentation for stacking notifications:
mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Sets an ID for the notification, so it can be updated
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle("New Message")
.setContentText("You've received new messages.")
.setSmallIcon(R.drawable.ic_notify_status)
numMessages = 0;
// Start of a loop that processes data and then notifies the user
...
mNotifyBuilder.setContentText(currentText)
.setNumber(++numMessages);
// Because the ID remains unchanged, the existing notification is
// updated.
mNotificationManager.notify(
notifyID,
mNotifyBuilder.build());
...
but I don't understand how you can keep track of the numMessages parameter across multiple receipts from GCM or how you can start a "loop that processes data" as they say in comments. I would think you would need to retrieve any current notifications and then append the new data to them. Any help appreciated.
edit: I also don't understand what the point of iterating over the loop would be if each notification overwrites the last one, why not just send a notification for the last iteration of this loop?
Try it:
new Notification.Builder().setAutoCancel(false).setOngoing(false);
Good luke!
mBuilder.setAutoCancel(true);//automatically canceled when the user clicks it
mBuilder.setOngoing(true);/*if true:
Ongoing notifications differ from regular notifications in the following ways:
Ongoing notifications are sorted above the regular notifications in the notification panel.
Ongoing notifications do not have an 'X' close button, and are not affected by the "Clear all" button. */
I have an IntentService that uploads a file. Everything works fine, but I'm a little confused about how to handle the notifications. When I start the notification I use startForeground() because the files can be rather large and I don't want the upload to get killed unless absolutely necessary. When I use startForeground() it displays two notifications in the notification area (one under Ongoing and one under Notifications):
I've read through a number of different Stack Overflow posts and web articles, but none of them answer the question I have...hopefully I haven't missed one that talks about ths.
It's my understanding that the ongoing notification in the image above (the one without the progress bar) is put there since this is running in the foreground (Services documentation). That's all well and good if you ask me, and I understand why. However, I don't need two notifications displayed and I'm pretty sure most people wouldn't want two notifications cluttering up the notification area either. I would like to know how to properly handle the notification so that only one displays and doesn't clutter up the notification area.
The only way I've been able to get around this is if I set the integer ID for startForeground (int id, Notification notification) (ONGOING_NOTIFICATION_ID in my code below) to zero. However, the documentation I quote above says:
Caution: The integer ID you give to startForeground() must not be 0
Setting it to 0 disables the Ongoing notification and then just shows the regular notification with the progress bar. I figure I could kind of "fix" this by setting .setOngoing(true) until it's done uploading the file and then setting .setOngoing(false) once it's finished, so it can be dismissed. I'm not sure exactly how "Cautious" one has to be with setting the integer ID to 0. To me it kind of seems like a lazy way to be able to get around the issue I'm having, but I don't know if there are other consequences for setting it to 0. Also, this only works if I only have one notification that I'm dealing with. If I have multiple, different notifications, then I'll need different IDs for each one and this won't work. Update: It looks like setting the ID to 0 won't work in Android 4.3, so now I'm back to square one.
What is a valid way to get around displaying both notifications?
Update/Solution: Duh, taking some time off and then coming back to this and double-checking what I had done based on #dsandler 's recommendation helped me figure out what I was doing wrong. I wasn't setting the correct ID when I was doing the progress update, so that's why it was creating two notifications and one wasn't getting updated. Using the same ID (ONGOING_NOTIFICATION_ID) for all the notifications solved the issue for me. See the code below for the additional pieces I hadn't included before and where I had made the mistake.
Relevant code from UploadFile.java:
public class UploadFile extends IntentService {
private static final int ONGOING_NOTIFICATION_ID = 1;
#Override
protected void onHandleIntent(Intent intent) {
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle(getText(R.string.upload))
.setContentText("0% " + getText(R.string.upload_in_progress))
.setSmallIcon(android.R.drawable.stat_sys_upload);
startForeground(ONGOING_NOTIFICATION_ID, mBuilder.build());
....
if (progress > 0){
percent = (Long.valueOf(progress) * 100) / totalSize;
mBuilder.setProgress(100, percent.intValue(), false);
mBuilder.setContentText(percent.intValue() + "% " + getText(R.string.upload_in_progress));
mNotifyManager.notify(ONGOING_NOTIFICATION_ID, mBuilder.build()); // <-- My problem was that I had set the ID here to 0 and did not make it the same as the ID I set above
}
....
}
First off, judging by your description, you might be able to get away with a regular service, recognizing that the out-of-memory killer won't come calling unless things are getting dire on the device and your service has really been running a very long time.
That said, if you must make the service foreground, the typical strategy here is to show your progress using the Notification you used when you put your service into the foreground state. Using NotificationManager.notify you can post as many updates to that notification as you like, including adjustments to progress bars via Notification.Builder.setProgress, for example.
In this way you only show one notification to the user, and it's the one required by startForeground.
When you want to update a Notification set by startForeground(), simply build a new notication and then use NotificationManager to notify it.
The KEY point is to use the same notification id.
I didn't test the scenario of repeatedly calling startForeground() to update the Notification, but I think that using NotificationManager.notify would be better.
Updating the Notification will NOT remove the Service from the foreground status (this can be done only by calling stopForground );
Example:
private static final int notif_id=1;
#Override
public void onCreate (){
this.startForeground();
}
private void startForeground() {
startForeground(notif_id, getMyActivityNotification(""));
}
private Notification getMyActivityNotification(String text){
// The PendingIntent to launch our activity if the user selects
// this notification
CharSequence title = getText(R.string.title_activity);
PendingIntent contentIntent = PendingIntent.getActivity(this,
0, new Intent(this, MyActivity.class), 0);
return new Notification.Builder(this)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(R.drawable.ic_launcher_b3)
.setContentIntent(contentIntent).getNotification();
}
/**
this is the method that can be called to update the Notification
*/
private void updateNotification() {
String text = "Some text that will update the notification";
Notification notification = getMyActivityNotification(text);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(notif_id, notification);
}