/*
* Copyright (C) 2014 Fastboot Mobile, LLC.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, see <http://www.gnu.org/licenses>.
*/
package com.fastbootmobile.encore.service;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Build;
import android.util.Log;
import com.echonest.api.v4.EchoNestException;
import com.fastbootmobile.encore.api.echonest.AutoMixBucket;
import com.fastbootmobile.encore.api.echonest.AutoMixManager;
import com.fastbootmobile.encore.model.Album;
import com.fastbootmobile.encore.model.Artist;
import com.fastbootmobile.encore.model.Song;
import com.fastbootmobile.encore.providers.ProviderAggregator;
import com.fastbootmobile.encore.receivers.RemoteControlReceiver;
import com.fastbootmobile.encore.utils.AvrcpUtils;
/**
* Class handling the lockscreen/remote metadata system on Lollipop and above
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class RemoteMetadataManagerv21 extends MediaSession.Callback implements IRemoteMetadataManager {
private static final String TAG = "RemoteMetadataManager";
private static final String SESSION_NAME = "OmniMusic";
private PlaybackService mService;
private MediaSession mMediaSession;
private MediaMetadata.Builder mBuilder;
private PlaybackState.Builder mStateBuilder;
private BitmapDrawable mPreviousAlbumArt;
private Runnable mThumbsUpRunnable = new Runnable() {
@Override
public void run() {
AutoMixBucket bucket = AutoMixManager.getDefault().getCurrentPlayingBucket();
if (bucket != null) {
try {
bucket.notifyLike();
} catch (EchoNestException e) {
Log.e(TAG, "Cannot notify of like event");
}
}
}
};
RemoteMetadataManagerv21(PlaybackService service) {
mService = service;
}
@Override
public void setup() {
mMediaSession = new MediaSession(mService, SESSION_NAME);
mMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
mMediaSession.setCallback(this);
PendingIntent receiverIntent = PendingIntent.getService(mService, 0,
new Intent(mService, RemoteControlReceiver.class), 0);
mMediaSession.setMediaButtonReceiver(receiverIntent);
mBuilder = new MediaMetadata.Builder();
mStateBuilder = new PlaybackState.Builder();
mStateBuilder.setActions(getActionsFlags(false));
}
private long getActionsFlags(boolean hasNext) {
long actions = 0;
actions |= PlaybackState.ACTION_PLAY_PAUSE;
actions |= PlaybackState.ACTION_SET_RATING;
actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
if (hasNext) {
actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
}
return actions;
}
@Override
public void release() {
mMediaSession.release();
mMediaSession = null;
}
@Override
public void setActive(final boolean active) {
mMediaSession.setActive(active);
}
@Override
public void setAlbumArt(final BitmapDrawable bmp) {
if (mPreviousAlbumArt != bmp) {
if (bmp != null) {
mPreviousAlbumArt = bmp;
mBuilder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, mPreviousAlbumArt.getBitmap());
if (mMediaSession != null) {
mMediaSession.setMetadata(mBuilder.build());
}
} else {
mBuilder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null);
}
}
}
@Override
public void setCurrentSong(final Song song, final boolean hasNext) {
final ProviderAggregator aggregator = ProviderAggregator.getDefault();
final Artist artist = aggregator.retrieveArtist(song.getArtist(), song.getProvider());
final Album album = aggregator.retrieveAlbum(song.getAlbum(), song.getProvider());
if (artist != null) {
mBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, artist.getName());
}
if (album != null) {
mBuilder.putString(MediaMetadata.METADATA_KEY_ALBUM, album.getName());
mBuilder.putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, album.getSongsCount());
}
mBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, song.getTitle());
mBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION, song.getDuration());
mStateBuilder.setActions(getActionsFlags(hasNext));
if (mMediaSession != null) {
mMediaSession.setMetadata(mBuilder.build());
mMediaSession.setPlaybackState(mStateBuilder.build());
}
AvrcpUtils.notifyMetaChanged(mService, song.getRef().hashCode(),
artist != null ? artist.getName() : null,
album != null ? album.getName() : null,
song.getTitle(), mService.getQueue().size(),
song.getDuration(), mService.getCurrentTrackPositionImpl());
}
@Override
public void notifyPlaying(final long timeElapsed) {
mStateBuilder.setState(PlaybackState.STATE_PLAYING, timeElapsed, 1.0f);
if (mMediaSession != null) {
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setActive(true);
}
AvrcpUtils.notifyPlayStateChanged(mService, true, timeElapsed);
}
@Override
public void notifyBuffering() {
mStateBuilder.setState(PlaybackState.STATE_BUFFERING, 0, 1.0f);
if (mMediaSession != null) {
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setActive(true);
}
AvrcpUtils.notifyPlayStateChanged(mService, true, 0);
}
@Override
public void notifyPaused(final long timeElapsed) {
mStateBuilder.setState(PlaybackState.STATE_PAUSED, timeElapsed, 1.0f);
if (mMediaSession != null) {
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setActive(true);
}
AvrcpUtils.notifyPlayStateChanged(mService, true, 0);
}
@Override
public void notifyStopped() {
mStateBuilder.setState(PlaybackState.STATE_STOPPED, 0, 1.0f);
if (mMediaSession != null) {
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setActive(false);
}
AvrcpUtils.notifyPlayStateChanged(mService, false, 0);
}
@Override
public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
// Buttons actions can be overridden
return super.onMediaButtonEvent(mediaButtonIntent);
}
@Override
public void onPlay() {
mService.playImpl();
}
@Override
public void onPause() {
mService.pauseImpl();
}
@Override
public void onSkipToNext() {
mService.nextImpl();
}
@Override
public void onSkipToPrevious() {
mService.previousImpl();
}
@Override
public void onFastForward() {
mService.seekImpl(mService.getCurrentTrackPositionImpl() + 5000);
}
@Override
public void onRewind() {
mService.seekImpl(mService.getCurrentTrackPositionImpl() - 5000);
}
@Override
public void onStop() {
mService.stopImpl();
}
@Override
public void onSeekTo(long pos) {
mService.seekImpl(pos);
}
@Override
public void onSetRating(Rating rating) {
if (rating.isThumbUp() || rating.hasHeart()) {
new Thread(mThumbsUpRunnable).start();
}
}
}