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