Okay. I had a question regaurding keeping Media Player Services in Android Oreo. Based on the discussion here:
Android Oreo: Keep started background service alive without setting it foreground (but with a notification)?
It seems the proper way to handle Media player services in Android Oreo is to store state information when the media player is paused so if it is destroyed, pressing play will create a new media player and start where it left off.
My question is how do I create a notification that won't get destroyed when the service that started it is destroyed. I am not running any code to dismiss the notification but it is still being dismissed automatically when the service is destoryed. As you can see in my code, I can rebuild the notification onDestroy, but I don't like the way this looks as the user can see it dismissing and rebuilding again.
Here is my notification code:
private void buildNotification() {
Cat.d("building notification");
final MediaPlayerService context = this;
RequestBuilder<Bitmap> requestBuilder = Glide.with(this).asBitmap()
.load(R.mipmap.ic_launcher);
NotificationCompat.Action action;
if (mediaPlayer != null && mediaPlayer.isPlaying())
action = generateAction(R.drawable.ic_pause, "Pause");
else
action = generateAction(R.drawable.ic_play_arrow, "Play");
int[] actionsInCompact;
builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_navbooks_icon_black)
//.setOnlyAlertOnce(true)
.setContentTitle(book.getTitle())
.setContentText(book.getAuthor())
.setAutoCancel(false)
.setContentIntent(PendingIntentHelper.getOpenMainActivityIntent(context, book.getId()))
.setDeleteIntent(PendingIntentHelper.getStopServiceIntent(context));
if (SettingsUtil.GetNotificationSkip(context)) {
builder.addAction(R.drawable.ic_skip_backward, "Skip Previous",
PendingIntentHelper.getSkipBackwardIntent(context))
.addAction(R.drawable.ic_backward, "Rewind",
PendingIntentHelper.getSeekBackwardIntent(context))
.addAction(action)
.addAction(R.drawable.ic_forward, "Fast Forward",
PendingIntentHelper.getSeekForwardIntent(context))
.addAction(R.drawable.ic_skip_forward, "Skip Next",
PendingIntentHelper.getSkipForwardIntent(context));
actionsInCompact = new int[]{0, 1, 2, 3, 4};
} else {
builder.addAction(R.drawable.ic_backward, "Rewind",
PendingIntentHelper.getSeekBackwardIntent(context))
.addAction(action)
.addAction(R.drawable.ic_forward, "Fast Forward",
PendingIntentHelper.getSeekForwardIntent(context));
actionsInCompact = new int[]{0, 1, 2};
}
builder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.getSessionToken())
.setShowActionsInCompactView(actionsInCompact));
// Load a cover image if there isn't one loaded yet or the cover has changed
if(cover == null || !book.getCover().equals(lastCover)) {
lastCover = book.getCover();
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, book.getTitle())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, book.getAuthor())
.build());
Glide.with(this)
.asBitmap()
.error(requestBuilder)
.load(book.getCover())
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap largeIcon, Transition transition) {
Cat.d("Finished loading");
cover = largeIcon;
// initBuilder
builder.setLargeIcon(largeIcon);
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, largeIcon)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, book.getTitle())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, book.getAuthor())
.build());
startNotification();
}
});
} else {
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, cover)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, book.getTitle())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, book.getAuthor())
.build());
if(cover != null)
builder.setLargeIcon(cover);
}
startNotification();
}
private NotificationCompat.Action generateAction(int icon, String title) {
return new NotificationCompat.Action.Builder( icon, title, PendingIntentHelper.getPlayPauseIntent(this)).build();
}
private void startNotification() {
mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
.setActions(MEDIA_SESSION_ACTIONS)
.setState(mediaPlayer.isPlaying() ? PlaybackStateCompat.STATE_PLAYING :
PlaybackStateCompat.STATE_PAUSED, book.getLastPosition(), 1)
.build());
if(mediaPlayer.isPlaying())
startForeground(NOTIFICATION_ID, builder.build());
else
{
NotificationManager notificationManager =
(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
if(notificationManager != null) {
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
stopForeground(false);
}
}
And here is what happens when the service is destroyed:
@Override
public void onDestroy() {
//super.onDestroy();
handleDestroy();
}
private void handleDestroy() {
if(book != null)
sendProgressUpdate();
else
book = new BookDBHelper(this).getBook(SettingsUtil.GetLastPlayedBook(this));
if(mediaPlayer != null) {
book.setLastPosition(this, getPosition());
SyncHelper.SendProgressUpdate(this, book);
}
if(mediaSession != null)
mediaSession.release();
if(noisyRegistered) {
unregisterReceiver(becomingNoisyReceiver);
unregisterReceiver(shakeReceiver);
}
buildNotification();
}
Okay. I found out that the stopForeground function has an optional parameter of a flag instead of a boolean. It can be given a flag STOP_FOREGROUND_DETACH which is necessary to detach the notification from the service even after calling stopForeground so that when the service is destroyed, the notification won't be dismissed and a new notification doesn't have to be built. Here is my updated code for starting the notification:
private fun updateNotification(preparing: Boolean = false) {
if(getPlaying()) {
startService(Intent(applicationContext, [email protected]))
startForeground(NOTIFICATION_ID, getNotification())
}else {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
stopForeground(Service.STOP_FOREGROUND_DETACH)
else
stopForeground(false)
notificationManager.notify(NOTIFICATION_ID, getNotification())
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With