Use MediaStyle Notification on Lollipop

fixes  #543
This commit is contained in:
daniel oeh 2015-01-02 00:01:53 +01:00
parent c33081b909
commit d697fab7eb
2 changed files with 135 additions and 5 deletions

View File

@ -749,8 +749,8 @@ public class PlaybackService extends Service {
final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()); final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
if (!isCancelled() && if (!isCancelled() &&
started == true && started &&
info.playable != null) { info.playable != null) {
String contentText = info.playable.getFeedTitle(); String contentText = info.playable.getFeedTitle();
String contentTitle = info.playable.getEpisodeTitle(); String contentTitle = info.playable.getEpisodeTitle();
Notification notification = null; Notification notification = null;
@ -791,7 +791,7 @@ public class PlaybackService extends Service {
.setLargeIcon(icon) .setLargeIcon(icon)
.setSmallIcon(smallIcon) .setSmallIcon(smallIcon)
.setPriority(UserPreferences.getNotifyPriority()); // set notification priority .setPriority(UserPreferences.getNotifyPriority()); // set notification priority
if(newInfo.playerStatus == PlayerStatus.PLAYING){ if (newInfo.playerStatus == PlayerStatus.PLAYING) {
notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action
getString(R.string.pause_label), getString(R.string.pause_label),
pauseButtonPendingIntent); pauseButtonPendingIntent);
@ -800,11 +800,20 @@ public class PlaybackService extends Service {
getString(R.string.play_label), getString(R.string.play_label),
playButtonPendingIntent); playButtonPendingIntent);
} }
if(UserPreferences.isPersistNotify()) { if (UserPreferences.isPersistNotify()) {
notificationBuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel, // stop action notificationBuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel, // stop action
getString(R.string.stop_label), getString(R.string.stop_label),
stopButtonPendingIntent); stopButtonPendingIntent);
} }
if (Build.VERSION.SDK_INT >= 21) {
notificationBuilder.setStyle(new Notification.MediaStyle()
.setMediaSession((android.media.session.MediaSession.Token) mediaPlayer.getSessionToken().getToken())
.setShowActionsInCompactView(0))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setColor(Notification.COLOR_DEFAULT);
}
notification = notificationBuilder.build(); notification = notificationBuilder.build();
} else { } else {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder( NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
@ -813,7 +822,7 @@ public class PlaybackService extends Service {
.setContentText(contentText).setOngoing(true) .setContentText(contentText).setOngoing(true)
.setContentIntent(pIntent).setLargeIcon(icon) .setContentIntent(pIntent).setLargeIcon(icon)
.setSmallIcon(smallIcon); .setSmallIcon(smallIcon);
notification = notificationBuilder.getNotification(); notification = notificationBuilder.build();
} }
startForeground(NOTIFICATION_ID, notification); startForeground(NOTIFICATION_ID, notification);
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)

View File

@ -6,6 +6,9 @@ import android.media.AudioManager;
import android.media.RemoteControlClient; import android.media.RemoteControlClient;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@ -48,6 +51,10 @@ public class PlaybackServiceMediaPlayer {
private volatile PlayerStatus statusBeforeSeeking; private volatile PlayerStatus statusBeforeSeeking;
private volatile IPlayer mediaPlayer; private volatile IPlayer mediaPlayer;
private volatile Playable media; private volatile Playable media;
/**
* Only used for Lollipop notifications.
*/
private final MediaSessionCompat mediaSession;
private volatile boolean stream; private volatile boolean stream;
private volatile MediaType mediaType; private volatile MediaType mediaType;
@ -89,6 +96,10 @@ public class PlaybackServiceMediaPlayer {
} }
); );
mediaSession = new MediaSessionCompat(context, TAG);
mediaSession.setCallback(sessionCallback);
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaPlayer = null; mediaPlayer = null;
statusBeforeSeeking = null; statusBeforeSeeking = null;
pausedBecauseOfTransientAudiofocusLoss = false; pausedBecauseOfTransientAudiofocusLoss = false;
@ -181,6 +192,7 @@ public class PlaybackServiceMediaPlayer {
setPlayerStatus(PlayerStatus.INITIALIZING, media); setPlayerStatus(PlayerStatus.INITIALIZING, media);
try { try {
media.loadMetadata(); media.loadMetadata();
mediaSession.setMetadata(getMediaSessionMetadata(media));
if (stream) { if (stream) {
mediaPlayer.setDataSource(media.getStreamUrl()); mediaPlayer.setDataSource(media.getStreamUrl());
} else { } else {
@ -211,6 +223,13 @@ public class PlaybackServiceMediaPlayer {
} }
} }
private MediaMetadataCompat getMediaSessionMetadata(Playable p) {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, p.getEpisodeTitle());
builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, p.getFeedTitle());
return builder.build();
}
/** /**
* Resumes playback if the PSMP object is in PREPARED or PAUSED state. If the PSMP object is in an invalid state. * Resumes playback if the PSMP object is in PREPARED or PAUSED state. If the PSMP object is in an invalid state.
@ -603,6 +622,9 @@ public class PlaybackServiceMediaPlayer {
if (mediaPlayer != null) { if (mediaPlayer != null) {
mediaPlayer.release(); mediaPlayer.release();
} }
if (mediaSession != null) {
mediaSession.release();
}
releaseWifiLockIfNecessary(); releaseWifiLockIfNecessary();
} }
@ -666,6 +688,16 @@ public class PlaybackServiceMediaPlayer {
return new PSMPInfo(playerStatus, media); return new PSMPInfo(playerStatus, media);
} }
/**
* Returns a token to this object's MediaSession. The MediaSession should only be used for notifications
* at the moment.
*
* @return The MediaSessionCompat.Token object.
*/
public MediaSessionCompat.Token getSessionToken() {
return mediaSession.getSessionToken();
}
/** /**
* Sets the player status of the PSMP object. PlayerStatus and media attributes have to be set at the same time * Sets the player status of the PSMP object. PlayerStatus and media attributes have to be set at the same time
* so that getPSMPInfo can't return an invalid state (e.g. status is PLAYING, but media is null). * so that getPSMPInfo can't return an invalid state (e.g. status is PLAYING, but media is null).
@ -683,6 +715,45 @@ public class PlaybackServiceMediaPlayer {
this.playerStatus = newStatus; this.playerStatus = newStatus;
this.media = newMedia; this.media = newMedia;
PlaybackStateCompat.Builder sessionState = new PlaybackStateCompat.Builder();
int state;
if (playerStatus != null) {
switch (playerStatus) {
case PLAYING:
state = PlaybackStateCompat.STATE_PLAYING;
break;
case PREPARED:
case PAUSED:
state = PlaybackStateCompat.STATE_PAUSED;
break;
case STOPPED:
state = PlaybackStateCompat.STATE_STOPPED;
break;
case SEEKING:
state = PlaybackStateCompat.STATE_FAST_FORWARDING;
break;
case PREPARING:
case INITIALIZING:
state = PlaybackStateCompat.STATE_CONNECTING;
break;
case INITIALIZED:
case INDETERMINATE:
state = PlaybackStateCompat.STATE_NONE;
break;
case ERROR:
state = PlaybackStateCompat.STATE_ERROR;
break;
default:
state = PlaybackStateCompat.STATE_NONE;
break;
}
} else {
state = PlaybackStateCompat.STATE_NONE;
}
sessionState.setState(state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, getPlaybackSpeed());
callback.statusChanged(new PSMPInfo(playerStatus, media)); callback.statusChanged(new PSMPInfo(playerStatus, media));
} }
@ -980,4 +1051,54 @@ public class PlaybackServiceMediaPlayer {
} }
}); });
} }
private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
resume();
} else if (playerStatus == PlayerStatus.INITIALIZED) {
setStartWhenPrepared(true);
prepare();
}
}
@Override
public void onPause() {
super.onPause();
if (playerStatus == PlayerStatus.PLAYING) {
pause(false, true);
}
if (UserPreferences.isPersistNotify()) {
pause(false, true);
} else {
pause(true, true);
}
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
endPlayback();
}
@Override
public void onFastForward() {
super.onFastForward();
seekDelta(UserPreferences.getSeekDeltaMs());
}
@Override
public void onRewind() {
super.onRewind();
seekDelta(-UserPreferences.getSeekDeltaMs());
}
@Override
public void onSeekTo(long pos) {
super.onSeekTo(pos);
seekTo((int) pos);
}
};
} }