65

Prelude

I'm trying to add a chronometer on the notification. The chronometer is a service. Every second this line is called (continue Thread is a the "running" boolean, timeString is the elaborated String showing the time):

NotificationChrono.updateNotification(getApplicationContext(), continueThread, 
NOTIF_ID, timeString, "Chronometer", notificationManager);

This is the NotificationChrono class:

public class NotificationChrono {

    static public void updateNotification(Context context, boolean running,
        int id, String title, String text,
        NotificationManager notificationManager) {

        Intent stopIntent = new Intent("com.corsalini.david.barcalc.STOP");
        PendingIntent stopPendingIntent = PendingIntent.getBroadcast(context,
            0, stopIntent, 0);

    Intent startIntent = new Intent(
            "com.corsalini.david.barcalc.STARTPAUSE");
    PendingIntent startPendingIntent = PendingIntent.getBroadcast(context,
            0, startIntent, 0);

    NotificationCompat.Builder builder = new NotificationCompat.Builder(
            context)

            .setContentText(context.getString(R.string.notif_text))

            .setContentTitle(title)

            .setSmallIcon(R.drawable.ic_action_alarm_2)

            .setAutoCancel(false)

            .setOngoing(running)

            .setOnlyAlertOnce(true)

            .setContentIntent(
                    PendingIntent.getActivity(context, 10, new Intent(
                            context, FrontActivity.class)
                            .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), 0))
            .addAction(
                    running ? R.drawable.ic_action_pause
                            : R.drawable.ic_action_play,
                    running ? context.getString(R.string.pause) : context
                            .getString(R.string.start), startPendingIntent)
            .addAction(R.drawable.ic_action_stop,
                    context.getString(R.string.stop), stopPendingIntent);

    notificationManager.notify(id, builder.build());
}
}

Problem

Every second the notification is deleted and recreated, visually it means that every second the notification disappears and reappears in the notification list.

What I would want is to just update the TITLE text, not recreating the notification entirely every second. Is it possible?

Brigham
  • 14,395
  • 3
  • 38
  • 48
David Corsalini
  • 7,958
  • 8
  • 41
  • 66
  • If my answer did the trick for you, please accept is as the answer :) – PieterAelse Nov 26 '13 at 08:51
  • I didn't use your answer, I actually don't remember exactly what I did (I think the trick was with setWhen). But reading your answer, it seems a better solution, I'll accept that! – David Corsalini Nov 26 '13 at 12:31

3 Answers3

135

Be sure to use the same NotificationCompat.Builder builder each time for creating the Notification!

Although the first time you have to set everything, the second time using the Builder you only have to set the value(s) you want to update. After that it's calling notificationManager.notify(id, builder.build()) just like you did. If you use the same ID then the notification gets updated (important!).

Example:

//First time
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
            .setContentText(context.getString(R.string.notif_text))
            .setContentTitle(title)
            .setSmallIcon(R.drawable.ic_action_alarm_2)
            .setAutoCancel(false)
            .setOngoing(running)
            .setOnlyAlertOnce(true)
            .setContentIntent(
                    PendingIntent.getActivity(context, 10, 
                            new Intent(context, FrontActivity.class)                                 
                            .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP),
                    0)
            )
            .addAction(running ? R.drawable.ic_action_pause 
                               : R.drawable.ic_action_play,
                       running ? context.getString(R.string.pause)
                               : context.getString(R.string.start),
                       startPendingIntent)
            .addAction(R.drawable.ic_action_stop, context.getString(R.string.stop),
                    stopPendingIntent);

notificationManager.notify(id, builder.build());

//Second time
builder.setContentTitle(title);
notificationManager.notify(id, builder.build());

But you can also use the setUsesChronometer method of the NotificationCompat class. This automatically displays a chronometer using the given timestamp (able to set with setWhen).

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
PieterAelse
  • 3,578
  • 2
  • 23
  • 33
  • 1
    where does notificationManager come from? – Rena Apr 27 '20 at 19:04
  • 3
    @Rena You can get the NotificationManager in Android by doing: `NotificationManager notificationManager = getSystemService(NotificationManager.class);` in Java or `val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager` in Kotlin. – PieterAelse Apr 28 '20 at 07:54
  • In this way after some update it shows `Package has already posted or enqueued XXX notifications. Not showing more.` but it is always the same I want update... so is it not possible? – Perry Jul 13 '21 at 12:56
  • 1
    DISREGARD this line "the second time using the Builder you only have to set the value(s) you want to update". Set everything every time or what you didn't set will be missing when the system updates the notification. You can also get "IllegalArgumentException: Invalid notification (no valid small icon)". Tested on Android 11 – Osagui Aghedo Jan 13 '22 at 06:23
  • For those wondering how to get a notification manager in Java -> notificationManager = NotificationManagerCompat.from(context); – Osagui Aghedo Jan 13 '22 at 06:23
  • Be sure to use the same NotificationCompat.Builder each time for creating the Notification! This is wrong! you're free to use a new NotificationCompat.Builder instance to update as long the same notification ID and channel ID as the original notification. – Cyber Avater Jul 26 '23 at 10:08
7

You need to mark the notification as "only Arert Once"

Builder#setOnlyAlertOnce(true)
GaRRaPeTa
  • 5,459
  • 4
  • 37
  • 61
5

Thanks for the idea PieterAelse!

In my case I had to go with this (build a new notification) for the second and following times:

        builder.setContentText(getConvertedTime(millisUntilFinished));
        Notification notification = builder.getNotification();
        notification.flags = Notification.FLAG_ONGOING_EVENT;            
        nm.notify(R.string.app_name,notification);

Hope it helps.

daros
  • 87
  • 1
  • 3