Clean ups.

This commit is contained in:
Karim Abou Zeid 2016-03-12 12:46:37 +01:00
commit 34f8ffbaf2
3 changed files with 175 additions and 85 deletions

View file

@ -1,11 +1,11 @@
package com.kabouzeid.gramophone.service; package com.kabouzeid.gramophone.service;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.audiofx.AudioEffect; import android.media.audiofx.AudioEffect;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -13,32 +13,30 @@ import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.service.Playback.Playback;
import com.kabouzeid.gramophone.util.PreferenceUtil; import com.kabouzeid.gramophone.util.PreferenceUtil;
import java.lang.ref.WeakReference;
/** /**
* @author Andrew Neal, Karim Abou Zeid (kabouzeid) * @author Andrew Neal, Karim Abou Zeid (kabouzeid)
*/ */
public class MultiPlayer implements MediaPlayer.OnErrorListener, public class MultiPlayer implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
MediaPlayer.OnCompletionListener {
public static final String TAG = MultiPlayer.class.getSimpleName(); public static final String TAG = MultiPlayer.class.getSimpleName();
private final WeakReference<MusicService> mService; private DoubleMediaPlayer mCurrentMediaPlayer = new DoubleMediaPlayer();
private DoubleMediaPlayer mNextMediaPlayer;
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer(); private Context context;
private MediaPlayer mNextMediaPlayer; @Nullable
private Playback.PlaybackCallbacks callbacks;
private Handler mHandler;
private boolean mIsInitialized = false; private boolean mIsInitialized = false;
/** /**
* Constructor of <code>MultiPlayer</code> * Constructor of <code>MultiPlayer</code>
*/ */
public MultiPlayer(final MusicService service) { public MultiPlayer(final Context context) {
mService = new WeakReference<>(service); this.context = context;
mCurrentMediaPlayer.setWakeMode(service, PowerManager.PARTIAL_WAKE_LOCK); mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
} }
/** /**
@ -47,6 +45,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* @return True if the <code>player</code> has been prepared and is * @return True if the <code>player</code> has been prepared and is
* ready to play, false otherwise * ready to play, false otherwise
*/ */
@Override
public boolean setDataSource(@NonNull final String path) { public boolean setDataSource(@NonNull final String path) {
mIsInitialized = false; mIsInitialized = false;
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path); mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
@ -64,15 +63,14 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* ready to play, false otherwise * ready to play, false otherwise
*/ */
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) { private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
MusicService service = mService.get(); if (context == null) {
if (service == null) {
return false; return false;
} }
try { try {
player.reset(); player.reset();
player.setOnPreparedListener(null); player.setOnPreparedListener(null);
if (path.startsWith("content://")) { if (path.startsWith("content://")) {
player.setDataSource(service, Uri.parse(path)); player.setDataSource(context, Uri.parse(path));
} else { } else {
player.setDataSource(path); player.setDataSource(path);
} }
@ -85,9 +83,9 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
player.setOnErrorListener(this); player.setOnErrorListener(this);
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId()); intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, service.getPackageName()); intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC); intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC);
service.sendBroadcast(intent); context.sendBroadcast(intent);
return true; return true;
} }
@ -97,9 +95,9 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* @param path The path of the file, or the http/rtsp URL of the stream * @param path The path of the file, or the http/rtsp URL of the stream
* you want to play * you want to play
*/ */
@Override
public void setNextDataSource(@Nullable final String path) { public void setNextDataSource(@Nullable final String path) {
MusicService service = mService.get(); if (context == null) {
if (service == null) {
return; return;
} }
try { try {
@ -117,9 +115,9 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
if (path == null) { if (path == null) {
return; return;
} }
if (PreferenceUtil.getInstance(mService.get()).gaplessPlayback()) { if (PreferenceUtil.getInstance(context).gaplessPlayback()) {
mNextMediaPlayer = new MediaPlayer(); mNextMediaPlayer = new DoubleMediaPlayer();
mNextMediaPlayer.setWakeMode(mService.get(), PowerManager.PARTIAL_WAKE_LOCK); mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
mNextMediaPlayer.setAudioSessionId(getAudioSessionId()); mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
if (setDataSourceImpl(mNextMediaPlayer, path)) { if (setDataSourceImpl(mNextMediaPlayer, path)) {
try { try {
@ -141,17 +139,19 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
} }
/** /**
* Sets the handler * Sets the callbacks
* *
* @param handler The handler to use * @param callbacks The callbacks to use
*/ */
public void setHandler(final Handler handler) { @Override
mHandler = handler; public void setCallbacks(final Playback.PlaybackCallbacks callbacks) {
this.callbacks = callbacks;
} }
/** /**
* @return True if the player is ready to go, false otherwise * @return True if the player is ready to go, false otherwise
*/ */
@Override
public boolean isInitialized() { public boolean isInitialized() {
return mIsInitialized; return mIsInitialized;
} }
@ -159,6 +159,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
/** /**
* Starts or resumes playback. * Starts or resumes playback.
*/ */
@Override
public boolean start() { public boolean start() {
try { try {
mCurrentMediaPlayer.start(); mCurrentMediaPlayer.start();
@ -171,6 +172,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
/** /**
* Resets the MediaPlayer to its uninitialized state. * Resets the MediaPlayer to its uninitialized state.
*/ */
@Override
public void stop() { public void stop() {
mCurrentMediaPlayer.reset(); mCurrentMediaPlayer.reset();
mIsInitialized = false; mIsInitialized = false;
@ -179,6 +181,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
/** /**
* Releases resources associated with this MediaPlayer object. * Releases resources associated with this MediaPlayer object.
*/ */
@Override
public void release() { public void release() {
stop(); stop();
mCurrentMediaPlayer.release(); mCurrentMediaPlayer.release();
@ -190,6 +193,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
/** /**
* Pauses playback. Call start() to resume. * Pauses playback. Call start() to resume.
*/ */
@Override
public boolean pause() { public boolean pause() {
try { try {
mCurrentMediaPlayer.pause(); mCurrentMediaPlayer.pause();
@ -202,6 +206,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
/** /**
* Checks whether the MultiPlayer is playing. * Checks whether the MultiPlayer is playing.
*/ */
@Override
public boolean isPlaying() { public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying(); return mIsInitialized && mCurrentMediaPlayer.isPlaying();
} }
@ -211,6 +216,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* *
* @return The duration in milliseconds * @return The duration in milliseconds
*/ */
@Override
public int duration() { public int duration() {
if (!mIsInitialized) { if (!mIsInitialized) {
return -1; return -1;
@ -227,6 +233,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* *
* @return The current position in milliseconds * @return The current position in milliseconds
*/ */
@Override
public int position() { public int position() {
if (!mIsInitialized) { if (!mIsInitialized) {
return -1; return -1;
@ -244,6 +251,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* @param whereto The offset in milliseconds from the start to seek to * @param whereto The offset in milliseconds from the start to seek to
* @return The offset in milliseconds from the start to seek to * @return The offset in milliseconds from the start to seek to
*/ */
@Override
public int seek(final int whereto) { public int seek(final int whereto) {
try { try {
mCurrentMediaPlayer.seekTo(whereto); mCurrentMediaPlayer.seekTo(whereto);
@ -253,6 +261,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
} }
} }
@Override
public boolean setVolume(final float vol) { public boolean setVolume(final float vol) {
try { try {
mCurrentMediaPlayer.setVolume(vol, vol); mCurrentMediaPlayer.setVolume(vol, vol);
@ -267,6 +276,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* *
* @param sessionId The audio session ID * @param sessionId The audio session ID
*/ */
@Override
public boolean setAudioSessionId(final int sessionId) { public boolean setAudioSessionId(final int sessionId) {
try { try {
mCurrentMediaPlayer.setAudioSessionId(sessionId); mCurrentMediaPlayer.setAudioSessionId(sessionId);
@ -281,6 +291,7 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
* *
* @return The current audio session ID. * @return The current audio session ID.
*/ */
@Override
public int getAudioSessionId() { public int getAudioSessionId() {
return mCurrentMediaPlayer.getAudioSessionId(); return mCurrentMediaPlayer.getAudioSessionId();
} }
@ -292,9 +303,11 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
public boolean onError(final MediaPlayer mp, final int what, final int extra) { public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false; mIsInitialized = false;
mCurrentMediaPlayer.release(); mCurrentMediaPlayer.release();
mCurrentMediaPlayer = new MediaPlayer(); mCurrentMediaPlayer = new DoubleMediaPlayer();
mCurrentMediaPlayer.setWakeMode(mService.get(), PowerManager.PARTIAL_WAKE_LOCK); mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
Toast.makeText(mService.get(), mService.get().getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show(); if (context != null) {
Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
}
return false; return false;
} }
@ -303,21 +316,46 @@ public class MultiPlayer implements MediaPlayer.OnErrorListener,
*/ */
@Override @Override
public void onCompletion(final MediaPlayer mp) { public void onCompletion(final MediaPlayer mp) {
MusicService service = mService.get();
if (service == null) {
return;
}
if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) { if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) {
mIsInitialized = false; mIsInitialized = false;
mCurrentMediaPlayer.release(); mCurrentMediaPlayer.release();
mCurrentMediaPlayer = mNextMediaPlayer; mCurrentMediaPlayer = mNextMediaPlayer;
mIsInitialized = true; mIsInitialized = true;
mNextMediaPlayer = null; mNextMediaPlayer = null;
mHandler.sendEmptyMessage(MusicService.TRACK_WENT_TO_NEXT); if (callbacks != null)
callbacks.onTrackWentToNext();
} else { } else {
service.acquireWakeLock(30000); if (callbacks != null)
mHandler.sendEmptyMessage(MusicService.TRACK_ENDED); callbacks.onTrackEnded();
mHandler.sendEmptyMessage(MusicService.RELEASE_WAKELOCK); }
}
private static final class DoubleMediaPlayer extends MediaPlayer implements MediaPlayer.OnCompletionListener {
private MediaPlayer mNextPlayer;
private OnCompletionListener mCompletion;
public DoubleMediaPlayer() {
super.setOnCompletionListener(this);
}
@Override
public void setNextMediaPlayer(final MediaPlayer next) {
mNextPlayer = next;
}
@Override
public void setOnCompletionListener(final OnCompletionListener listener) {
mCompletion = listener;
}
@Override
public void onCompletion(final MediaPlayer mp) {
if (mNextPlayer != null) {
// SystemClock.sleep(25);
mNextPlayer.start();
}
mCompletion.onCompletion(this);
} }
} }
} }

View file

@ -46,6 +46,7 @@ import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.provider.HistoryStore; import com.kabouzeid.gramophone.provider.HistoryStore;
import com.kabouzeid.gramophone.provider.MusicPlaybackQueueStore; import com.kabouzeid.gramophone.provider.MusicPlaybackQueueStore;
import com.kabouzeid.gramophone.provider.SongPlayCountStore; import com.kabouzeid.gramophone.provider.SongPlayCountStore;
import com.kabouzeid.gramophone.service.Playback.Playback;
import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.PreferenceUtil; import com.kabouzeid.gramophone.util.PreferenceUtil;
import com.kabouzeid.gramophone.util.Util; import com.kabouzeid.gramophone.util.Util;
@ -57,7 +58,7 @@ import java.util.List;
/** /**
* @author Karim Abou Zeid (kabouzeid), Andrew Neal * @author Karim Abou Zeid (kabouzeid), Andrew Neal
*/ */
public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener { public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks {
public static final String TAG = MusicService.class.getSimpleName(); public static final String TAG = MusicService.class.getSimpleName();
public static final String PHONOGRAPH_PACKAGE_NAME = "com.kabouzeid.gramophone"; public static final String PHONOGRAPH_PACKAGE_NAME = "com.kabouzeid.gramophone";
@ -100,7 +101,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private static final int UNDUCK = 7; private static final int UNDUCK = 7;
private final IBinder musicBind = new MusicBinder(); private final IBinder musicBind = new MusicBinder();
private MultiPlayer player; private Playback playback;
private ArrayList<Song> playingQueue = new ArrayList<>(); private ArrayList<Song> playingQueue = new ArrayList<>();
private ArrayList<Song> originalPlayingQueue = new ArrayList<>(); private ArrayList<Song> originalPlayingQueue = new ArrayList<>();
private int position = -1; private int position = -1;
@ -114,7 +115,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private RemoteControlClient remoteControlClient; private RemoteControlClient remoteControlClient;
private PowerManager.WakeLock wakeLock; private PowerManager.WakeLock wakeLock;
private MusicPlayerHandler playerHandler; private PlaybackHandler playerHandler;
private final AudioManager.OnAudioFocusChangeListener audioFocusListener = new AudioManager.OnAudioFocusChangeListener() { private final AudioManager.OnAudioFocusChangeListener audioFocusListener = new AudioManager.OnAudioFocusChangeListener() {
@Override @Override
public void onAudioFocusChange(final int focusChange) { public void onAudioFocusChange(final int focusChange) {
@ -161,17 +162,15 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
wakeLock.setReferenceCounted(false); wakeLock.setReferenceCounted(false);
musicPlayerHandlerThread = new HandlerThread("MusicPlayerHandler", musicPlayerHandlerThread = new HandlerThread("PlaybackHandler");
Process.THREAD_PRIORITY_BACKGROUND);
musicPlayerHandlerThread.start(); musicPlayerHandlerThread.start();
playerHandler = new MusicPlayerHandler(this, musicPlayerHandlerThread.getLooper()); playerHandler = new PlaybackHandler(this, musicPlayerHandlerThread.getLooper());
player = new MultiPlayer(this); playback = new MultiPlayer(this);
player.setHandler(playerHandler); playback.setCallbacks(this);
// queue saving needs to run on a separate thread so that it doesn't block the player handler events // queue saving needs to run on a separate thread so that it doesn't block the playback handler events
queueSaveHandlerThread = new HandlerThread("QueueSaveHandler", queueSaveHandlerThread = new HandlerThread("QueueSaveHandler", Process.THREAD_PRIORITY_BACKGROUND);
Process.THREAD_PRIORITY_BACKGROUND);
queueSaveHandlerThread.start(); queueSaveHandlerThread.start();
queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper()); queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper());
@ -326,20 +325,20 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} else { } else {
queueSaveHandlerThread.quit(); queueSaveHandlerThread.quit();
} }
player.release(); playback.release();
player = null; playback = null;
} }
public void stop() { public void stop() {
pausedByTransientLossOfFocus = false; pausedByTransientLossOfFocus = false;
savePositionInTrack(); savePositionInTrack();
player.stop(); playback.stop();
notifyChange(PLAY_STATE_CHANGED); notifyChange(PLAY_STATE_CHANGED);
getAudioManager().abandonAudioFocus(audioFocusListener); getAudioManager().abandonAudioFocus(audioFocusListener);
} }
public boolean isPlaying() { public boolean isPlaying() {
return player.isPlaying(); return playback.isPlaying();
} }
public void saveState() { public void saveState() {
@ -369,17 +368,13 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
return position; return position;
} }
private void setPositionInternal(int position) {
this.position = position;
}
public void playNextSong(boolean force) { public void playNextSong(boolean force) {
playSongAt(getNextPosition(force)); playSongAt(getNextPosition(force));
} }
private boolean openTrackAndPrepareNextAt(int position) { private boolean openTrackAndPrepareNextAt(int position) {
synchronized (this) { synchronized (this) {
setPositionInternal(position); this.position = position;
boolean prepared = openCurrent(); boolean prepared = openCurrent();
if (prepared) prepareNextImpl(); if (prepared) prepareNextImpl();
notifyChange(META_CHANGED); notifyChange(META_CHANGED);
@ -391,7 +386,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private boolean openCurrent() { private boolean openCurrent() {
synchronized (this) { synchronized (this) {
try { try {
return player.setDataSource(getTrackUri(getCurrentSong())); return playback.setDataSource(getTrackUri(getCurrentSong()));
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
@ -407,7 +402,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
synchronized (this) { synchronized (this) {
try { try {
int nextPosition = getNextPosition(false); int nextPosition = getNextPosition(false);
player.setNextDataSource(getTrackUri(getSongAt(nextPosition))); playback.setNextDataSource(getTrackUri(getSongAt(nextPosition)));
this.nextPosition = nextPosition; this.nextPosition = nextPosition;
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@ -418,7 +413,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private void closeAudioEffectSession() { private void closeAudioEffectSession() {
final Intent audioEffectsIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); final Intent audioEffectsIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, player.getAudioSessionId()); audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, playback.getAudioSessionId());
audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
sendBroadcast(audioEffectsIntent); sendBroadcast(audioEffectsIntent);
} }
@ -606,7 +601,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
this.originalPlayingQueue = restoredOriginalQueue; this.originalPlayingQueue = restoredOriginalQueue;
this.playingQueue = restoredQueue; this.playingQueue = restoredQueue;
setPositionInternal(restoredPosition); position = restoredPosition;
openCurrent(); openCurrent();
prepareNext(); prepareNext();
@ -668,7 +663,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private void rePosition(int deletedPosition) { private void rePosition(int deletedPosition) {
int currentPosition = getPosition(); int currentPosition = getPosition();
if (deletedPosition < currentPosition) { if (deletedPosition < currentPosition) {
setPositionInternal(currentPosition - 1); position = currentPosition - 1;
} else if (deletedPosition == currentPosition) { } else if (deletedPosition == currentPosition) {
if (playingQueue.size() > deletedPosition) { if (playingQueue.size() > deletedPosition) {
setPosition(position); setPosition(position);
@ -688,11 +683,11 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
originalPlayingQueue.add(to, tmpSong); originalPlayingQueue.add(to, tmpSong);
} }
if (from > currentPosition && to <= currentPosition) { if (from > currentPosition && to <= currentPosition) {
setPositionInternal(currentPosition + 1); position = currentPosition + 1;
} else if (from < currentPosition && to >= currentPosition) { } else if (from < currentPosition && to >= currentPosition) {
setPositionInternal(currentPosition - 1); position = currentPosition - 1;
} else if (from == currentPosition) { } else if (from == currentPosition) {
setPositionInternal(to); position = to;
} }
notifyChange(QUEUE_CHANGED); notifyChange(QUEUE_CHANGED);
} }
@ -727,8 +722,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public void pause() { public void pause() {
pausedByTransientLossOfFocus = false; pausedByTransientLossOfFocus = false;
if (player.isPlaying()) { if (playback.isPlaying()) {
player.pause(); playback.pause();
notifyChange(PLAY_STATE_CHANGED); notifyChange(PLAY_STATE_CHANGED);
} }
} }
@ -736,12 +731,12 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public void play() { public void play() {
synchronized (this) { synchronized (this) {
if (requestFocus()) { if (requestFocus()) {
if (!player.isPlaying()) { if (!playback.isPlaying()) {
if (!player.isInitialized()) { if (!playback.isInitialized()) {
playSongAt(getPosition()); playSongAt(getPosition());
} else { } else {
registerReceiversAndRemoteControlClient(); registerReceiversAndRemoteControlClient();
player.start(); playback.start();
if (notHandledMetaChangedForCurrentTrack) { if (notHandledMetaChangedForCurrentTrack) {
handleChange(META_CHANGED); handleChange(META_CHANGED);
notHandledMetaChangedForCurrentTrack = false; notHandledMetaChangedForCurrentTrack = false;
@ -795,15 +790,15 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
public int getSongProgressMillis() { public int getSongProgressMillis() {
return player.position(); return playback.position();
} }
public int getSongDurationMillis() { public int getSongDurationMillis() {
return player.duration(); return playback.duration();
} }
public int seek(int millis) { public int seek(int millis) {
int newPosition = player.seek(millis); int newPosition = playback.seek(millis);
savePositionInTrack(); savePositionInTrack();
return newPosition; return newPosition;
} }
@ -842,7 +837,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
case SHUFFLE_MODE_SHUFFLE: case SHUFFLE_MODE_SHUFFLE:
this.shuffleMode = shuffleMode; this.shuffleMode = shuffleMode;
ShuffleHelper.makeShuffleList(this.getPlayingQueue(), getPosition()); ShuffleHelper.makeShuffleList(this.getPlayingQueue(), getPosition());
setPositionInternal(0); position = 0;
break; break;
case SHUFFLE_MODE_NONE: case SHUFFLE_MODE_NONE:
this.shuffleMode = shuffleMode; this.shuffleMode = shuffleMode;
@ -854,7 +849,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
newPosition = getPlayingQueue().indexOf(song); newPosition = getPlayingQueue().indexOf(song);
} }
} }
setPositionInternal(newPosition); position = newPosition;
break; break;
} }
notifyChange(SHUFFLE_MODE_CHANGED); notifyChange(SHUFFLE_MODE_CHANGED);
@ -924,7 +919,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
public int getAudioSessionId() { public int getAudioSessionId() {
return player.getAudioSessionId(); return playback.getAudioSessionId();
} }
public void releaseWakeLock() { public void releaseWakeLock() {
@ -944,7 +939,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
if (sharedPreferences.getBoolean(key, false)) { if (sharedPreferences.getBoolean(key, false)) {
prepareNext(); prepareNext();
} else { } else {
player.setNextDataSource(null); playback.setNextDataSource(null);
} }
break; break;
case PreferenceUtil.ALBUM_ART_ON_LOCKSCREEN: case PreferenceUtil.ALBUM_ART_ON_LOCKSCREEN:
@ -957,6 +952,17 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
} }
@Override
public void onTrackWentToNext() {
playerHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT);
}
@Override
public void onTrackEnded() {
acquireWakeLock(30000);
playerHandler.sendEmptyMessage(TRACK_ENDED);
}
private static final class QueueSaveHandler extends Handler { private static final class QueueSaveHandler extends Handler {
@NonNull @NonNull
private final WeakReference<MusicService> mService; private final WeakReference<MusicService> mService;
@ -977,12 +983,12 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
} }
private static final class MusicPlayerHandler extends Handler { private static final class PlaybackHandler extends Handler {
@NonNull @NonNull
private final WeakReference<MusicService> mService; private final WeakReference<MusicService> mService;
private float currentDuckVolume = 1.0f; private float currentDuckVolume = 1.0f;
public MusicPlayerHandler(final MusicService service, @NonNull final Looper looper) { public PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
super(looper); super(looper);
mService = new WeakReference<>(service); mService = new WeakReference<>(service);
} }
@ -1002,7 +1008,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} else { } else {
currentDuckVolume = .2f; currentDuckVolume = .2f;
} }
service.player.setVolume(currentDuckVolume); service.playback.setVolume(currentDuckVolume);
break; break;
case UNDUCK: case UNDUCK:
@ -1012,7 +1018,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} else { } else {
currentDuckVolume = 1.0f; currentDuckVolume = 1.0f;
} }
service.player.setVolume(currentDuckVolume); service.playback.setVolume(currentDuckVolume);
break; break;
case TRACK_WENT_TO_NEXT: case TRACK_WENT_TO_NEXT:
@ -1020,7 +1026,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
service.pause(); service.pause();
service.seek(0); service.seek(0);
} else { } else {
service.setPositionInternal(service.nextPosition); service.position = service.nextPosition;
service.prepareNextImpl(); service.prepareNextImpl();
service.notifyChange(META_CHANGED); service.notifyChange(META_CHANGED);
} }
@ -1033,6 +1039,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} else { } else {
service.playNextSong(false); service.playNextSong(false);
} }
sendEmptyMessage(RELEASE_WAKELOCK);
break; break;
case RELEASE_WAKELOCK: case RELEASE_WAKELOCK:
@ -1065,14 +1072,14 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
break; break;
case AudioManager.AUDIOFOCUS_LOSS: case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player // Lost focus for an unbounded amount of time: stop playback and release media playback
service.pause(); service.pause();
service.unregisterReceiversAndRemoteControlClient(); service.unregisterReceiversAndRemoteControlClient();
break; break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop // Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback // playback. We don't release the media playback because playback
// is likely to resume // is likely to resume
boolean wasPlaying = service.isPlaying(); boolean wasPlaying = service.isPlaying();
service.pause(); service.pause();

View file

@ -0,0 +1,45 @@
package com.kabouzeid.gramophone.service.Playback;
import android.support.annotation.Nullable;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public interface Playback {
boolean setDataSource(String path);
void setNextDataSource(@Nullable String path);
void setCallbacks(PlaybackCallbacks callbacks);
boolean isInitialized();
boolean start();
void stop();
void release();
boolean pause();
boolean isPlaying();
int duration();
int position();
int seek(int whereto);
boolean setVolume(float vol);
boolean setAudioSessionId(int sessionId);
int getAudioSessionId();
interface PlaybackCallbacks {
void onTrackWentToNext();
void onTrackEnded();
}
}