Private
Public Access
1
0

Start of Media Service.

This commit is contained in:
Kevin Whitaker
2015-12-08 00:23:07 -05:00
parent 52031ac62b
commit 14ae1a478b
3 changed files with 376 additions and 0 deletions

View File

@@ -19,6 +19,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".service.PlayerService"/>
</application>
</manifest>

View File

@@ -0,0 +1,296 @@
package com.magnatune.eyecreate.companionformagnatune.service;
import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.support.v7.app.NotificationCompat;
import android.util.Log;
import com.magnatune.eyecreate.companionformagnatune.model.Album;
import com.magnatune.eyecreate.companionformagnatune.model.MagnatuneDBManager;
import com.magnatune.eyecreate.companionformagnatune.model.Track;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.io.IOException;
import io.realm.Realm;
public class PlayerService extends Service implements MediaPlayer.OnPreparedListener,MediaPlayer.OnCompletionListener,MediaPlayer.OnErrorListener,AudioManager.OnAudioFocusChangeListener{
public static String ACTION_TRACK_PLAY = "maganatune.playtrack";
public static String ACTION_TRACK_PAUSE = "magnatune.pausetrack";
public static String ACTION_TRACK_NEXT = "magnatune.next";
public static String ACTION_TRACK_PREV = "magnatune.previous";
public static String ACTION_TRACK_LIST = "magantune.gettracks";
public static String ACTION_TRACK_QUEUE = "magnatune.queuetrack";
public static String ACTION_TRACK_DEQUEUE = "magnatune.dequeuetrack";
public static String ACTION_PLAYLIST_CLEAR = "magnatune.clear";
public static String EXTRA_TRACK_ID = "magnatune.trackid";
public static String EXTRA_TRACK_PLAYLIST_POSITION = "magnatune.playlistposition";
public static String EXTRA_PLAYLIST = "magnatune.playlist";
public static String ACTION_PLAYING_STARTED = "magnatune.startedplaying";
private static int notificationId = 58769423;
MediaPlayer player;
boolean mediaPrepared = false;
Playlist currentPlaylist;
Notification displayedPlayData;
Realm db;
MediaSessionCompat session;
MediaSessionCompat.Callback mediaEventHandler = new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
super.onPlay();
if(mediaPrepared) {
player.start();
}
}
@Override
public void onPause() {
super.onPause();
if(mediaPrepared && player.isPlaying()) {
player.pause();
}
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
playNextTrack();
}
@Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
playPreviousTrack();
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
db = MagnatuneDBManager.getAlbumDB();
player = new MediaPlayer();
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
currentPlaylist = new Playlist();
session = new MediaSessionCompat(this,"magnatune");
session.setCallback(mediaEventHandler);
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.requestAudioFocus(this,AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN);
//TODO:work on possible media notification
// Compat.MediaStyle().setMediaSession(session.getSessionToken())).build();
//NotificationManagerCompat.from(this).notify(notificationId,displayedPlayData);
}
@Override
public void onDestroy() {
super.onDestroy();
currentPlaylist.clearPlaylist();
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_PLAYLIST_CLEAR));
NotificationManagerCompat.from(this).cancel(notificationId);
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.abandonAudioFocus(this);
session.release();
player.release();
db.close();
}
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPrepared = true;
mediaPlayer.start();
session.setPlaybackState(new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING, player.getCurrentPosition(), 0)
.setActions(PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
.build());
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_PLAYING_STARTED));
}
private void playNextTrack() {
//If last track, trigger player stop
if(currentPlaylist.getPlaylistIds().size()==0) {
//Do nothing as there is nothing to play.
} else if(currentPlaylist.getPlaylistIds().indexOf(currentPlaylist.getCurrentlyPlayingId())+1>=currentPlaylist.getPlaylistIds().size()) {
stopSelf();
} else {
Track nextTrack = db.where(Track.class).equalTo("isrc", currentPlaylist.getPlaylistIds().get(currentPlaylist.getPlaylistIds().indexOf(currentPlaylist.getCurrentlyPlayingId()) + 1)).findFirst();
playThisTrack(nextTrack);
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_TRACK_NEXT));
}
}
private void playPreviousTrack() {
//If first track, just seek back to start of track.
if(currentPlaylist.getPlaylistIds().size()==0) {
//Do nothing as there is nothing to play.
} else if(currentPlaylist.getPlaylistIds().indexOf(currentPlaylist.getCurrentlyPlayingId())==0 && mediaPrepared) {
player.seekTo(0);
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_TRACK_PREV));
} else {
Track nextTrack = db.where(Track.class).equalTo("isrc", currentPlaylist.getPlaylistIds().get(currentPlaylist.getPlaylistIds().indexOf(currentPlaylist.getCurrentlyPlayingId()) - 1)).findFirst();
playThisTrack(nextTrack);
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_TRACK_PREV));
}
}
private void playThisTrack(final Track trackToPlay) {
player.reset();
mediaPrepared = false;
try {
player.setDataSource(trackToPlay.getUrl());
player.prepareAsync();
currentPlaylist.setCurrentlyPlayingId(trackToPlay.getIsrc());
//Load resource and set upp media metadata
Picasso.with(this).load(db.where(Album.class).equalTo("albumname",trackToPlay.getAlbumname()).equalTo("artist.artistname",trackToPlay.getArtist()).findFirst().getCover_small()).into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
session.setMetadata(new MediaMetadataCompat.Builder()
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART,bitmap)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM,trackToPlay.getAlbumname())
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST,trackToPlay.getArtist())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE,trackToPlay.getTrackname())
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION,trackToPlay.getSeconds()*1000)
.build());
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent.getAction().equals(ACTION_TRACK_PLAY)) {
//Check if new track requested
if(intent.hasExtra(EXTRA_TRACK_PLAYLIST_POSITION) && currentPlaylist.getPlaylistIds().size()>intent.getIntExtra(EXTRA_TRACK_PLAYLIST_POSITION,-1)) {
Track nextTrack = db.where(Track.class).equalTo("isrc", currentPlaylist.getPlaylistIds().get(currentPlaylist.getPlaylistIds().indexOf(intent.getIntExtra(EXTRA_TRACK_PLAYLIST_POSITION, -1)))).findFirst();
playThisTrack(nextTrack);
session.setPlaybackState(new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING,player.getCurrentPosition(),0)
.setActions(PlaybackStateCompat.ACTION_PAUSE|PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
.build());
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_TRACK_PLAY));
} else {
if (!mediaPrepared) {
//Do nothing as currently loading or stopped.
} else {
player.start();
session.setPlaybackState(new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING,player.getCurrentPosition(),0)
.setActions(PlaybackStateCompat.ACTION_PAUSE|PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
.build());
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_TRACK_PLAY));
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_PLAYING_STARTED));
}
}
} else if(intent.getAction().equals(ACTION_TRACK_PAUSE)) {
if(player.isPlaying()) {
player.pause();
session.setPlaybackState(new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PAUSED,player.getCurrentPosition(),0)
.setActions(PlaybackStateCompat.ACTION_PLAY|PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
.build());
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_TRACK_PAUSE));
}
} else if(intent.getAction().equals(ACTION_TRACK_NEXT)) {
playNextTrack();
} else if(intent.getAction().equals(ACTION_TRACK_PREV)) {
playPreviousTrack();
} else if(intent.getAction().equals(ACTION_TRACK_QUEUE)) {
if(intent.hasExtra(EXTRA_TRACK_ID)) {
currentPlaylist.addToPlaylist(intent.getStringExtra(EXTRA_TRACK_ID));
Intent queue = new Intent(ACTION_TRACK_QUEUE);
queue.putExtra(EXTRA_TRACK_ID,intent.getStringExtra(EXTRA_TRACK_ID));
LocalBroadcastManager.getInstance(this).sendBroadcast(queue);
}
} else if(intent.getAction().equals(ACTION_TRACK_DEQUEUE)) {
if(intent.hasExtra(EXTRA_TRACK_PLAYLIST_POSITION)) {
currentPlaylist.removeFromPlaylist(intent.getIntExtra(EXTRA_TRACK_PLAYLIST_POSITION, -1));
Intent dequeue = new Intent(ACTION_TRACK_DEQUEUE);
dequeue.putExtra(EXTRA_TRACK_PLAYLIST_POSITION,intent.getIntExtra(EXTRA_TRACK_PLAYLIST_POSITION,-1));
LocalBroadcastManager.getInstance(this).sendBroadcast(dequeue);
}
} else if(intent.getAction().equals(ACTION_PLAYLIST_CLEAR)) {
player.reset();
mediaPrepared = false;
currentPlaylist.clearPlaylist();
currentPlaylist.setCurrentPositionMill(0);
currentPlaylist.setCurrentlyPlayingId(null);
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_PLAYLIST_CLEAR));
} else if(intent.getAction().equals(ACTION_TRACK_LIST)) {
currentPlaylist.setCurrentPositionMill(player.getCurrentPosition());
Intent trackData = new Intent(ACTION_TRACK_LIST);
trackData.putExtra(EXTRA_PLAYLIST,currentPlaylist);
LocalBroadcastManager.getInstance(this).sendBroadcast(trackData);
}
return START_STICKY;
}
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
playNextTrack();
}
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
player.reset();
mediaPrepared = false;
session.setPlaybackState(new PlaybackStateCompat.Builder().setState(PlaybackStateCompat.STATE_ERROR,player.getCurrentPosition(),0).setErrorMessage("Media error occured").build());
Log.e("Magnatune","Media error occurred");
return false;
}
@Override
public void onAudioFocusChange(int i) {
switch (i) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
if(player.isPlaying()) player.setVolume(0.1f,0.1f);
break;
case AudioManager.AUDIOFOCUS_GAIN:
if(!player.isPlaying() && mediaPrepared) player.start();
player.setVolume(1.0f,1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
stopSelf(); //TODO:might not want to outright kill
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
if(player.isPlaying()) player.pause();
break;
}
}
//TODO: look at implementing AUDIO_BECOMING_NOISY
}

View File

@@ -0,0 +1,79 @@
package com.magnatune.eyecreate.companionformagnatune.service;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
public class Playlist implements Parcelable {
private ArrayList<String> playlistIds;
private String currentlyPlayingId;
private int currentPositionMill;
public Playlist() {
playlistIds = new ArrayList<>();
}
protected Playlist(Parcel in) {
in.readStringList(getPlaylistIds());
setCurrentlyPlayingId(in.readString());
setCurrentPositionMill(in.readInt());
}
public static final Creator<Playlist> CREATOR = new Creator<Playlist>() {
@Override
public Playlist createFromParcel(Parcel in) {
return new Playlist(in);
}
@Override
public Playlist[] newArray(int size) {
return new Playlist[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeStringList(getPlaylistIds());
parcel.writeString(getCurrentlyPlayingId());
parcel.writeInt(getCurrentPositionMill());
}
public ArrayList<String> getPlaylistIds() {
return playlistIds;
}
public void addToPlaylist(String id) {
this.playlistIds.add(id);
}
public void removeFromPlaylist(int itemPosition) {
this.playlistIds.remove(itemPosition);
}
public void clearPlaylist() {
this.playlistIds.clear();
}
public String getCurrentlyPlayingId() {
return currentlyPlayingId;
}
public void setCurrentlyPlayingId(String currentlyPlayingId) {
this.currentlyPlayingId = currentlyPlayingId;
}
public int getCurrentPositionMill() {
return currentPositionMill;
}
public void setCurrentPositionMill(int currentPositionMill) {
this.currentPositionMill = currentPositionMill;
}
}