21

I have a Media service that uses startForeground() to show a notification when playback starts. It has pause/stop buttons when playing, play/stop buttons while paused.

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
// setup...

Notification n = mBuilder.build();

if (state == State.Playing) {
    startForeground(mId, n);
}
else {
    stopForeground(false);
    mNotificationManager.notify(mId, n);        
}

The problem here is when I show/update the notification in it's paused state, you should be allowed to remove it. mBuilder.setOngoing(false) appears to have no effect as the previous startForeground overrides it.

Calling stopForeground(true); with the same code works as expected, but the Notification flashes as it is destroyed and recreated. Is there a way to "update" the notification created from startForeground to allow it to be removed after calling stop?

Edit: As requested, here's the full code creating the notification. createNotification is called whenever the service is played or paused.

private void createNotification() {
    NotificationCompat.Builder mBuilder = 
            new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.ic_launcher)
    .setContentTitle("No Agenda")
    .setContentText("Live stream");

    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
    {
        if (state == State.Playing) {
            Intent pauseIntent = new Intent(this, MusicService.class);
            pauseIntent.setAction(ACTION_PAUSE);
            PendingIntent pausePendingIntent =     PendingIntent.getService(MusicService.this, 0, pauseIntent, 0);              

            mBuilder.addAction(R.drawable.pause, "Pause", pausePendingIntent);
            //mBuilder.setOngoing(true);
        }
        else if (state == State.Paused) {
            Intent pauseIntent = new Intent(this, MusicService.class);
            pauseIntent.setAction(ACTION_PAUSE);
            PendingIntent pausePendingIntent =  PendingIntent.getService(MusicService.this, 0, pauseIntent, 0);

            mBuilder.addAction(R.drawable.play, "Play", pausePendingIntent);
            mBuilder.setOngoing(false);
        }

        Intent stopIntent = new Intent(this, MusicService.class);
        stopIntent.setAction(ACTION_STOP);
        PendingIntent stopPendingIntent = PendingIntent.getService(MusicService.this, 0, stopIntent, 0);

        setNotificationPendingIntent(mBuilder);
        mBuilder.addAction(R.drawable.stop, "Stop", stopPendingIntent);
    }
    else
    {
        Intent resultIntent = new Intent(this, MainActivity.class);
        PendingIntent intent = PendingIntent.getActivity(this, 0, resultIntent, 0);

        mBuilder.setContentIntent(intent);
    }

    Notification n = mBuilder.build();

    if (state == State.Playing) {
        startForeground(mId, n);
    }
    else {
        stopForeground(true);
        mNotificationManager.notify(mId, n);
    }
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void setNotificationPendingIntent(NotificationCompat.Builder mBuilder) {
    Intent resultIntent = new Intent(this, MainActivity.class);

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    stackBuilder.addParentStack(MainActivity.class);
    stackBuilder.addNextIntent(resultIntent);

    PendingIntent resultPendingIntent =
            stackBuilder.getPendingIntent(
                0,
                PendingIntent.FLAG_UPDATE_CURRENT
            );

    mBuilder.setContentIntent(resultPendingIntent);
}

Follow-up Edit:

One of the comments below mentioned that the answer may be "fragile", and as of the release of Android 4.3, the behavior behind startForeground has changed. startForeground will force your application to show a notification while in the foreground, and the method should just be called with the notification to show. I haven't tested, but the accepted answer may no longer work as intended.

In terms of stopping the flashing when calling stopForeground, I don't think it's worth fighting the framework for.

There's some additional information on the Android 4.3 notification change here.

4

4 に答える 4

6

You may consider using a different approach.
Since you should use foreground service for such a task (media playing) I suggest you keep on doing start foreground(), but instead of passing a notification to it just set id 0 and notification null like this startForeground(0, null);.

This way the foreground service will not display any notification.

Now, for your purposes, you can use regular notifications and update their states (ongoing, layout, text, etc...), this way you are not dependant on the foreground service's notification behaviour.

Hope this helps.

于 2013-04-02T13:41:39.943 に答える
6

Instead of stopService(true), calling stopService(false) will retain the notification as it is (without ongoing state) unless it is dismissed by user/removed programmatically or if the service stops. Hence just call stopService(false) and update notification to show paused state and now notification can be dismissed by user. This also prevents flashing since we are not recreating the notification.

于 2016-03-05T17:17:25.367 に答える
3

According to docs this behaviour is by design before Lollipop

Applications targeting this or a later release will get these new changes in behavior:

...

  • Calling Service.stopForeground with removeNotification false will modify the still posted notification so that it is no longer forced to be ongoing.
于 2015-01-20T00:51:29.760 に答える
1

Before N, the notification has to reappear when stopForeground and startForeground called in a sequence. You need to show the notification once more and don't make it a foreground notification after you paused. I recommend you not to add work around or hack to fix this.(There are work arounds, but may introduce other bugs).

On N+, there is a flag added, ServiceCompat.STOP_FOREGROUND_DETACH. This will be passed to stopForeground and let you keep the current notification in the notification tray.

Also don't forget to kill the service(stopSelf() or whatever). Since if the user swipes away the app, you app main process may stay.

于 2020-09-24T21:20:23.280 に答える