Add handling for media session notifications

Since Android 5.0, media players can have interactive notifications that
reside in the notification area, and offer up to 5 control buttons
(play/pause, next, previous, etc), and information about the currentlu
playing media file.

We use these notifications to get information about the currently
playing media file such as:
- artist
- track (title)
- album
- duration (length of the media file)
- play state (playing, paused, stopped)
- position
- play rate (how fast is the media file being played)

We then send this information up to the device.

On Pebble, the music app will display the title and the artist, as
well as a progress bar showing the current position. The progress bar is
animated when the media file is being played, and if it is being paused,
it displays a pause symbol.

This code will be skipped when GadgetBridge is run on a device with
Android version older than 5.0 (lollipop).
here
Steffen Liebergeld 2016-06-08 20:33:20 +02:00
parent 73fbaf0a54
commit fb71cdf55b
1 changed files with 57 additions and 15 deletions

View File

@ -11,8 +11,14 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.MediaStore;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.v4.app.NotificationCompat;
@ -26,6 +32,7 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
@ -183,10 +190,8 @@ public class NotificationListener extends NotificationListenerService {
String source = sbn.getPackageName();
Notification notification = sbn.getNotification();
if (source.equals("au.com.shiftyjelly.pocketcasts")) {
if (handlePocketCastNotification(notification))
return;
}
if (handleMediaSessionNotification(notification))
return;
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) {
return;
@ -318,32 +323,69 @@ public class NotificationListener extends NotificationListenerService {
}
/**
* Try to handle pocket cast notifications that tell info about the current play state.
* Try to handle media session notifications that tell info about the current play state.
*
* @param notification The notification to handle.
* @return true if notification was handled, false otherwise
*/
public boolean handlePocketCastNotification(Notification notification) {
public boolean handleMediaSessionNotification(Notification notification) {
// this code requires Android 5.0 or newer
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return false;
}
MusicSpec musicSpec = new MusicSpec();
MusicStateSpec stateSpec = new MusicStateSpec();
Bundle extras = notification.extras;
if (extras == null)
return false;
CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
if (title != null)
musicSpec.artist = title.toString();
if (extras.containsKey(Notification.EXTRA_TEXT)) {
CharSequence contentCS = extras.getCharSequence(Notification.EXTRA_TEXT);
if (contentCS != null)
musicSpec.track = contentCS.toString();
if (extras.get(Notification.EXTRA_MEDIA_SESSION) == null)
return false;
MediaController c;
try {
c = new MediaController(getApplicationContext(), (MediaSession.Token) extras.get(Notification.EXTRA_MEDIA_SESSION));
} catch (NullPointerException e) {
return false;
}
musicSpec.album = "unknown";
PlaybackState s = c.getPlaybackState();
stateSpec.position = (int)s.getPosition();
stateSpec.playRate = Math.round(100 * s.getPlaybackSpeed());
stateSpec.repeat = 1;
stateSpec.shuffle = 1;
switch (s.getState()) {
case PlaybackState.STATE_PLAYING:
stateSpec.state = 0x01;
break;
case PlaybackState.STATE_STOPPED:
case PlaybackState.STATE_PAUSED:
stateSpec.state = 0x00;
break;
default:
stateSpec.state = 0x04;
break;
}
LOG.info("handlePocketCastsNotification: artist " + musicSpec.artist + ", track " + musicSpec.track);
MediaMetadata d = c.getMetadata();
if (d == null)
return false;
if (d.containsKey(MediaMetadata.METADATA_KEY_ARTIST))
musicSpec.artist = d.getString(MediaMetadata.METADATA_KEY_ARTIST);
if (d.containsKey(MediaMetadata.METADATA_KEY_ALBUM))
musicSpec.album = d.getString(MediaMetadata.METADATA_KEY_ALBUM);
if (d.containsKey(MediaMetadata.METADATA_KEY_TITLE))
musicSpec.track = d.getString(MediaMetadata.METADATA_KEY_TITLE);
if (d.containsKey(MediaMetadata.METADATA_KEY_DURATION))
musicSpec.duration = (int)d.getLong(MediaMetadata.METADATA_KEY_DURATION) / 1000;
// finally, tell the device about it
GBApplication.deviceService().onSetMusicInfo(musicSpec);
GBApplication.deviceService().onSetMusicState(stateSpec);
return true;
}