Start of Media Service.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".service.PlayerService"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user