package me.barrasso.android.volume.media.compat;
import android.annotation.TargetApi;
import android.service.notification.NotificationListenerService;
import android.content.ComponentName;
import android.content.Context;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Build;
import android.util.Pair;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* {@link me.barrasso.android.volume.media.compat.RemoteControlCompat} that utilizes the
* {@link android.media.session.MediaController} API for Android 5.0 Lollipop.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class RemoteControlLollipop extends RemoteControlCompat
implements MediaSessionManager.OnActiveSessionsChangedListener {
private final ComponentName mControllerService;
private final Map<MediaSession.Token, Pair<MediaController, MediaController.Callback>> mControllers;
private final MediaSessionManager mMediaSessionManager;
private boolean mRegistered;
protected RemoteControlLollipop(Context context, Class<? extends NotificationListenerService> clazz) {
super(context);
mControllerService = new ComponentName(context, clazz);
mControllers = new ConcurrentHashMap<>();
mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
mMediaSessionManager.addOnActiveSessionsChangedListener(this, mControllerService);
mRegistered = true;
}
@Override
public boolean isRegistered() {
return mRegistered;
}
@Override
public void release() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(this);
unregister();
mRegistered = false;
}
@Override
public void onActiveSessionsChanged(List<MediaController> controllers) {
int controllerCount = ((null == controllers) ? 0 : controllers.size());
Set<MediaSession.Token> tokens = new HashSet<>(controllerCount);
for (int i = 0; i < controllerCount; ++i) {
MediaController controller = controllers.get(i);
tokens.add(controller.getSessionToken());
// Only add tokens that we don't already have.
if (!mControllers.containsKey(controller.getSessionToken())) {
MediaController.Callback callback = new Callback(controller);
controller.registerCallback(callback);
Pair<MediaController, MediaController.Callback> pair = Pair.create(controller, callback);
synchronized (mControllers) {
mControllers.put(controller.getSessionToken(), pair);
}
callback.onMetadataChanged(controller.getMetadata());
callback.onPlaybackStateChanged(controller.getPlaybackState());
}
}
// Now remove old sessions that are not longer active.
for (Map.Entry<MediaSession.Token, Pair<MediaController, MediaController.Callback>> entry : mControllers.entrySet()) {
MediaSession.Token token = entry.getKey();
if (!tokens.contains(token)) {
Pair<MediaController, MediaController.Callback> pair = entry.getValue();
pair.first.unregisterCallback(pair.second);
synchronized (mControllers) {
mControllers.remove(token);
}
}
}
}
private void unregister() {
for (Map.Entry<MediaSession.Token, Pair<MediaController, MediaController.Callback>> entry : mControllers.entrySet()) {
Pair<MediaController, MediaController.Callback> pair = entry.getValue();
pair.first.unregisterCallback(pair.second);
}
synchronized (mControllers) {
mControllers.clear();
}
}
// Bridge to handle callbacks from MediaControllers.
protected class Callback extends MediaController.Callback {
private MediaController mController;
public Callback(MediaController controller) {
mController = controller;
}
@Override
public void onMetadataChanged(MediaMetadata metadata) {
if (null == metadata) return;
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(
MediaMetadataCompat.fromMediaMetadata(metadata));
builder.putString(METADATA_KEY_PACKAGE, mController.getPackageName());
metadataChanged(builder.build());
}
@Override
public void onPlaybackStateChanged(PlaybackState state) {
if (null == state) return;
playbackStateChanged(PlaybackStateCompat.fromPlaybackState(state));
}
}
}