How do I update the notification text for a foreground service in Android?
Asked Answered
G

5

173

I have a foreground service setup in Android. I would like to update the notification text. I am creating the service as shown below.

How can I update the notification text that is setup within this foreground service? What is the best practise for updating the notification? Any sample code would be appreciated.

public class NotificationService extends Service {

    private static final int ONGOING_NOTIFICATION = 1;

    private Notification notification;

    @Override
    public void onCreate() {
        super.onCreate();

        this.notification = new Notification(R.drawable.statusbar, getText(R.string.app_name), System.currentTimeMillis());
        Intent notificationIntent = new Intent(this, AbList.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
        this.notification.setLatestEventInfo(this, getText(R.string.app_name), "Update This Text", pendingIntent);

        startForeground(ONGOING_NOTIFICATION, this.notification);

    }

I am creating the service in my main activity as shown below:

    // Start Notification Service
    Intent serviceIntent = new Intent(this, NotificationService.class);
    startService(serviceIntent);
Godship answered 3/4, 2011 at 7:41 Comment(0)
O
67

I would think that calling startForeground() again with the same unique ID and a Notification with the new information would work, though I have not tried this scenario.

Update: Based on the comments, you should use NotifcationManager to update the notification and your service continues to stay in the foreground mode. Take a look at the answer below.

Obla answered 3/4, 2011 at 11:52 Comment(13)
Could you please give me an example of how I would call that from my Activity though? I haven't been able to find a good sample on how to call methods in my foreground service.Godship
@Luke: Oh, you can't call it from an activity. You would need to tell your service what to update and have it update the Notification. You can try to use NotificationManager and update the Notification directly from the activity, using the same unique ID. However, I worry that this would "undo" your foreground status.Obla
Yes, I was going to add a method to the service for updating the notification but I can't find any samples or examples on how to access my service instance and call my updateNotification method in my foreground service. I tried making my service a singleton class but that doesn't seem to work.Godship
@Luke: There are any number of patterns for using a service, and I have no idea what yours is following. If you are calling startService() to pass a command to the service, then just call startService() again to tell it to update its text. Or, if you are calling bindService(), add a method to your API to have the service update its text. Or, consider whether the service itself should be the one making the decision whether or not to update the text. Or, perhaps the text is a SharedPeference that the service has a listener on. It's impossible to give you accurate advice in the abstract.Obla
@Obla Have you ever discovered whether using NotificationManager would undo the foreground status?Doublespace
@Doublespace In Google's Random Music Player sample app, they use NotificationManager to update the notification of a foreground service, so chances are pretty good that it retains the foreground status.Ballottement
@LorneLaliberte Looks like a good answer for #8290427Doublespace
@Obla in my experience that does work (at least on 2.3.x and below), with one notable exception being that you can't make the ticker text appear more than once for a given notification, even if you change the ticker text (at least not if you're using Notification.FLAG_ONGOING_EVENT). To show ticker text for an existing notification, you need to cancel the notification and then restart it (and do so using startForeground if you want your service to have foreground status).Ballottement
to clarify further: you can't cancel() a Notification set by startForeground(). You have to remove the foreground status of the service itself (using stopForeground() if you want to make ticker text appear again. I lost hours because these answers led me to believe it was in fact possible.Witchcraft
I have downvoted this answer as it is clearly plain wrong: developer.android.com/training/notify-user/managing.html Please @Obla consider removing this answer, as your high reputation score makes this answer the "holy truth" for the casual browser. Thanks.Dilworth
Didn’t work for me (though I recall using this same method in a previous project). Using NotificationManager worked as I expected.Whiten
@Obla would you please have a look of this relevant question too => https://mcmap.net/q/144696/-detect-when-app-is-killed-from-recent-apps-list-in-android-o-and-p/2304819Easel
Calling startForeground() again was the only thing working for me actually.Erdman
S
285

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);
}

The documentation states

To set up a notification so it can be updated, issue it with a notification ID by calling NotificationManager.notify(). To update this notification after you've issued it, 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. If the previous notification has been dismissed, a new notification is created instead.

Stamen answered 22/11, 2013 at 10:31 Comment(11)
THIS IS THE CORRECT ANSWER! The answer above is very wrong and misleading. You don't need to restart your service just so you update a silly notification.Idiomatic
@Idiomatic While I agree that this is the optimal answer (it avoids the slightly longer code-path taken by Commonsware's answer) you are mistaken about what Commonsware's answer does - start/stopForegound do not start/stop the service, they just affect its foregroundness.Idelia
@Idelia Thanks for that Stevie you are probably right. Still I would not mess with that either!Idiomatic
Calling the notify() orstartForeground() both lead to call onStartCommand().Jermaine
The problem with using NotificationManager to update a notification shown with startForeground is that calling stopForeground will no longer remove the notification. Updating it with another call to startForeground obviates that problem.Voorhees
@Voorhees it worked for me. Created a notification with startForeground(), updated it a few times as described in the answer, then called stopForeground()—notification gone.Whiten
This works. Tested with emulator running API 27. No need to re-create the service.Inarch
This is not working for me on target version 29 and with notification channels. Did anyone get the same problem?Erdman
@Erdman Try using NotificationManager. It will not be working if you are using NotificationManagerCompat.Transatlantic
I reached this thread with a not working solution using channels and targeting version 31 in a behaviour which I believe is similar to the one described by @Bolling. In my case, the issue was related with a call to notificationManagerCompat.createNotificationChannel(channel) performed before the update. The solution was to create this channel at play and just use the channelId at pause to be able to have play/pause actions updated in a MediaBrowserServiceCompat notification. My solution was codded using Compat APIs.Tenishatenn
I have had a very specific issue, using CallStyle notifications. If for whatever reason the foreground service was not running, updating the notification with NotificationManager.notify would cause a crash (CallStyle notifications must either be for a foreground Service or use a fullScreenIntent.). In my case, it was safer to just use the other answer (calling startForeground with same ID).Sprang
O
67

I would think that calling startForeground() again with the same unique ID and a Notification with the new information would work, though I have not tried this scenario.

Update: Based on the comments, you should use NotifcationManager to update the notification and your service continues to stay in the foreground mode. Take a look at the answer below.

Obla answered 3/4, 2011 at 11:52 Comment(13)
Could you please give me an example of how I would call that from my Activity though? I haven't been able to find a good sample on how to call methods in my foreground service.Godship
@Luke: Oh, you can't call it from an activity. You would need to tell your service what to update and have it update the Notification. You can try to use NotificationManager and update the Notification directly from the activity, using the same unique ID. However, I worry that this would "undo" your foreground status.Obla
Yes, I was going to add a method to the service for updating the notification but I can't find any samples or examples on how to access my service instance and call my updateNotification method in my foreground service. I tried making my service a singleton class but that doesn't seem to work.Godship
@Luke: There are any number of patterns for using a service, and I have no idea what yours is following. If you are calling startService() to pass a command to the service, then just call startService() again to tell it to update its text. Or, if you are calling bindService(), add a method to your API to have the service update its text. Or, consider whether the service itself should be the one making the decision whether or not to update the text. Or, perhaps the text is a SharedPeference that the service has a listener on. It's impossible to give you accurate advice in the abstract.Obla
@Obla Have you ever discovered whether using NotificationManager would undo the foreground status?Doublespace
@Doublespace In Google's Random Music Player sample app, they use NotificationManager to update the notification of a foreground service, so chances are pretty good that it retains the foreground status.Ballottement
@LorneLaliberte Looks like a good answer for #8290427Doublespace
@Obla in my experience that does work (at least on 2.3.x and below), with one notable exception being that you can't make the ticker text appear more than once for a given notification, even if you change the ticker text (at least not if you're using Notification.FLAG_ONGOING_EVENT). To show ticker text for an existing notification, you need to cancel the notification and then restart it (and do so using startForeground if you want your service to have foreground status).Ballottement
to clarify further: you can't cancel() a Notification set by startForeground(). You have to remove the foreground status of the service itself (using stopForeground() if you want to make ticker text appear again. I lost hours because these answers led me to believe it was in fact possible.Witchcraft
I have downvoted this answer as it is clearly plain wrong: developer.android.com/training/notify-user/managing.html Please @Obla consider removing this answer, as your high reputation score makes this answer the "holy truth" for the casual browser. Thanks.Dilworth
Didn’t work for me (though I recall using this same method in a previous project). Using NotificationManager worked as I expected.Whiten
@Obla would you please have a look of this relevant question too => https://mcmap.net/q/144696/-detect-when-app-is-killed-from-recent-apps-list-in-android-o-and-p/2304819Easel
Calling startForeground() again was the only thing working for me actually.Erdman
I
33

Improving on Luca Manzo answer in android 8.0+ when updating the notification it will make sound and show as Heads-up.
to prevent that you need to add setOnlyAlertOnce(true)

so the code is:

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){
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
        ((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(
        NotificationChannel("timer_notification","Timer Notification",NotificationManager.IMPORTANCE_HIGH))
}

        // The PendingIntent to launch our activity if the user selects
        // this notification
        PendingIntent contentIntent=PendingIntent.getActivity(this,
        0,new Intent(this,MyActivity.class),0);

        return new NotificationCompat.Builder(this,"my_channel_01")
        .setContentTitle("some title")
        .setContentText(text)
        .setOnlyAlertOnce(true) // so when data is updated don't make sound and alert in android 8.0+
        .setOngoing(true)
        .setSmallIcon(R.drawable.ic_launcher_b3)
        .setContentIntent(contentIntent)
        .build();
}

/**
 * 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);
}
Ichthyo answered 12/3, 2018 at 14:48 Comment(3)
Missing keyword, there should be new NotificationChannelTehuantepec
so we can pass any number to notification_id ?other than 0?Kellda
@Kellda yes, notification_id has to be an integer.Buckner
P
6

here's the code to do so in your service. Create a new notification, but ask notification manager to notify the same notification id you used in startForeground.

Notification notify = createNotification();
final NotificationManager notificationManager = (NotificationManager) getApplicationContext()
    .getSystemService(getApplicationContext().NOTIFICATION_SERVICE);

notificationManager.notify(ONGOING_NOTIFICATION, notify);

for full sample codes, you can check here:

https://github.com/plateaukao/AutoScreenOnOff/blob/master/src/com/danielkao/autoscreenonoff/SensorMonitorService.java

Proconsul answered 6/6, 2013 at 16:53 Comment(6)
I'm not sure this will maintain the foreground status of startService.Lanctot
@Daniel Kao Your solution does not start a foreground serviceSwound
Correct me if I'm wrong but could the people down voting this answer please be more descriptive as to what's wrong with it? The question doesn't ask how to start a Foreground service, but how to update a notification of a foreground service. This is effectively the same answer as Luca which people agree works and maintains the foreground status.Asteroid
@Asteroid It Does not work. The notification's status become not foreground for foreground created message.Brayer
This would result in a duplicate notification, because startForeground() was already called.Swound
what if I had two?Sterol
J
6

It seems none of the existing answers show how to handle the full case - to startForeground if it's the first call but update the notification for subsequent calls.

You can use the following pattern to detect the right case:

private void notify(@NonNull String action) {
    boolean isForegroundNotificationVisible = false;
    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
    for (StatusBarNotification notification : notifications) {
        if (notification.getId() == FOREGROUND_NOTE_ID) {
            isForegroundNotificationVisible = true;
            break;
        }
    }
    Log.v(getClass().getSimpleName(), "Is foreground visible: " + isForegroundNotificationVisible);
    if (isForegroundNotificationVisible){
        notificationManager.notify(FOREGROUND_NOTE_ID, buildForegroundNotification(action));
    } else {
        startForeground(FOREGROUND_NOTE_ID, buildForegroundNotification(action));
    }
}

Additionally you need to build the notification and channel as in other answers:

private Notification buildForegroundNotification(@NonNull String action) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        createNotificationChannel();
    }
    //Do any customization you want here
    String title;
    if (ACTION_STOP.equals(action)) {
        title = getString(R.string.fg_notitifcation_title_stopping);
    } else {
        title = getString(R.string.fg_notitifcation_title_starting);
    }
    //then build the notification
    return new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle(title)
            .setOngoing(true)
            .build();
}

@RequiresApi(Build.VERSION_CODES.O)
private void createNotificationChannel(){
    NotificationChannel chan = new NotificationChannel(CHANNEL_ID, getString(R.string.fg_notification_channel), NotificationManager.IMPORTANCE_DEFAULT);
    chan.setLightColor(Color.RED);
    chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
    NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    assert manager != null;
    manager.createNotificationChannel(chan);
}
Jamarjamb answered 21/2, 2020 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.