Clean ups.
This commit is contained in:
parent
a5f8cc71c5
commit
34f8ffbaf2
3 changed files with 175 additions and 85 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue