Adding audio variable speed playback support at the service level
This commit is contained in:
parent
d086579e09
commit
a86501c795
|
@ -51,9 +51,13 @@ import de.danoeh.antennapod.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.receiver.MediaButtonReceiver;
|
import de.danoeh.antennapod.receiver.MediaButtonReceiver;
|
||||||
import de.danoeh.antennapod.receiver.PlayerWidget;
|
import de.danoeh.antennapod.receiver.PlayerWidget;
|
||||||
import de.danoeh.antennapod.util.BitmapDecoder;
|
import de.danoeh.antennapod.util.BitmapDecoder;
|
||||||
|
import de.danoeh.antennapod.util.DuckType;
|
||||||
import de.danoeh.antennapod.util.flattr.FlattrUtils;
|
import de.danoeh.antennapod.util.flattr.FlattrUtils;
|
||||||
|
import de.danoeh.antennapod.util.playback.AudioPlayer;
|
||||||
|
import de.danoeh.antennapod.util.playback.IPlayer;
|
||||||
import de.danoeh.antennapod.util.playback.Playable;
|
import de.danoeh.antennapod.util.playback.Playable;
|
||||||
import de.danoeh.antennapod.util.playback.Playable.PlayableException;
|
import de.danoeh.antennapod.util.playback.Playable.PlayableException;
|
||||||
|
import de.danoeh.antennapod.util.playback.VideoPlayer;
|
||||||
|
|
||||||
/** Controls the MediaPlayer that plays a FeedMedia-file */
|
/** Controls the MediaPlayer that plays a FeedMedia-file */
|
||||||
public class PlaybackService extends Service {
|
public class PlaybackService extends Service {
|
||||||
|
@ -73,7 +77,7 @@ public class PlaybackService extends Service {
|
||||||
public static final String EXTRA_PREPARE_IMMEDIATELY = "extra.de.danoeh.antennapod.service.prepareImmediately";
|
public static final String EXTRA_PREPARE_IMMEDIATELY = "extra.de.danoeh.antennapod.service.prepareImmediately";
|
||||||
|
|
||||||
public static final String ACTION_PLAYER_STATUS_CHANGED = "action.de.danoeh.antennapod.service.playerStatusChanged";
|
public static final String ACTION_PLAYER_STATUS_CHANGED = "action.de.danoeh.antennapod.service.playerStatusChanged";
|
||||||
private static final String AVRCP_ACTION_PLAYER_STATUS_CHANGED= "com.android.music.playstatechanged";
|
private static final String AVRCP_ACTION_PLAYER_STATUS_CHANGED = "com.android.music.playstatechanged";
|
||||||
|
|
||||||
public static final String ACTION_PLAYER_NOTIFICATION = "action.de.danoeh.antennapod.service.playerNotification";
|
public static final String ACTION_PLAYER_NOTIFICATION = "action.de.danoeh.antennapod.service.playerNotification";
|
||||||
public static final String EXTRA_NOTIFICATION_CODE = "extra.de.danoeh.antennapod.service.notificationCode";
|
public static final String EXTRA_NOTIFICATION_CODE = "extra.de.danoeh.antennapod.service.notificationCode";
|
||||||
|
@ -120,7 +124,7 @@ public class PlaybackService extends Service {
|
||||||
private AudioManager audioManager;
|
private AudioManager audioManager;
|
||||||
private ComponentName mediaButtonReceiver;
|
private ComponentName mediaButtonReceiver;
|
||||||
|
|
||||||
private MediaPlayer player;
|
private IPlayer player;
|
||||||
private RemoteControlClient remoteControlClient;
|
private RemoteControlClient remoteControlClient;
|
||||||
|
|
||||||
private Playable media;
|
private Playable media;
|
||||||
|
@ -215,55 +219,61 @@ public class PlaybackService extends Service {
|
||||||
status = PlayerStatus.STOPPED;
|
status = PlayerStatus.STOPPED;
|
||||||
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||||
manager = FeedManager.getInstance();
|
manager = FeedManager.getInstance();
|
||||||
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE,
|
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE, new ThreadFactory() {
|
||||||
new ThreadFactory() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Thread newThread(Runnable r) {
|
public Thread newThread(Runnable r) {
|
||||||
Thread t = new Thread(r);
|
Thread t = new Thread(r);
|
||||||
t.setPriority(Thread.MIN_PRIORITY);
|
t.setPriority(Thread.MIN_PRIORITY);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}, new RejectedExecutionHandler() {
|
}, new RejectedExecutionHandler() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void rejectedExecution(Runnable r,
|
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
|
||||||
ThreadPoolExecutor executor) {
|
Log.w(TAG, "SchedEx rejected submission of new task");
|
||||||
Log.w(TAG, "SchedEx rejected submission of new task");
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
player = createMediaPlayer();
|
|
||||||
|
|
||||||
mediaButtonReceiver = new ComponentName(getPackageName(),
|
mediaButtonReceiver = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName());
|
||||||
MediaButtonReceiver.class.getName());
|
|
||||||
audioManager.registerMediaButtonEventReceiver(mediaButtonReceiver);
|
audioManager.registerMediaButtonEventReceiver(mediaButtonReceiver);
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 14) {
|
if (android.os.Build.VERSION.SDK_INT >= 14) {
|
||||||
audioManager
|
audioManager.registerRemoteControlClient(setupRemoteControlClient());
|
||||||
.registerRemoteControlClient(setupRemoteControlClient());
|
|
||||||
}
|
}
|
||||||
registerReceiver(headsetDisconnected, new IntentFilter(
|
registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
|
||||||
Intent.ACTION_HEADSET_PLUG));
|
registerReceiver(shutdownReceiver, new IntentFilter(ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
||||||
registerReceiver(shutdownReceiver, new IntentFilter(
|
registerReceiver(audioBecomingNoisy, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
|
||||||
ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(ACTION_SKIP_CURRENT_EPISODE));
|
||||||
registerReceiver(audioBecomingNoisy, new IntentFilter(
|
|
||||||
AudioManager.ACTION_AUDIO_BECOMING_NOISY));
|
|
||||||
registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(
|
|
||||||
ACTION_SKIP_CURRENT_EPISODE));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaPlayer createMediaPlayer() {
|
private IPlayer createMediaPlayer() {
|
||||||
return createMediaPlayer(new MediaPlayer());
|
IPlayer player;
|
||||||
|
if (media == null || media.getMediaType() == MediaType.VIDEO) {
|
||||||
|
player = new VideoPlayer();
|
||||||
|
} else {
|
||||||
|
player = new AudioPlayer(this);
|
||||||
|
}
|
||||||
|
return createMediaPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaPlayer createMediaPlayer(MediaPlayer mp) {
|
private IPlayer createMediaPlayer(IPlayer mp) {
|
||||||
if (mp != null) {
|
if (mp != null && media != null) {
|
||||||
mp.setOnPreparedListener(preparedListener);
|
if (media.getMediaType() == MediaType.AUDIO) {
|
||||||
mp.setOnCompletionListener(completionListener);
|
((AudioPlayer) mp).setOnPreparedListener(audioPreparedListener);
|
||||||
mp.setOnSeekCompleteListener(onSeekCompleteListener);
|
((AudioPlayer) mp).setOnCompletionListener(audioCompletionListener);
|
||||||
mp.setOnErrorListener(onErrorListener);
|
((AudioPlayer) mp).setOnSeekCompleteListener(audioSeekCompleteListener);
|
||||||
mp.setOnBufferingUpdateListener(onBufferingUpdateListener);
|
((AudioPlayer) mp).setOnErrorListener(audioErrorListener);
|
||||||
mp.setOnInfoListener(onInfoListener);
|
((AudioPlayer) mp).setOnBufferingUpdateListener(audioBufferingUpdateListener);
|
||||||
|
((AudioPlayer) mp).setOnInfoListener(audioInfoListener);
|
||||||
|
} else {
|
||||||
|
((VideoPlayer) mp).setOnPreparedListener(videoPreparedListener);
|
||||||
|
((VideoPlayer) mp).setOnCompletionListener(videoCompletionListener);
|
||||||
|
((VideoPlayer) mp).setOnSeekCompleteListener(videoSeekCompleteListener);
|
||||||
|
((VideoPlayer) mp).setOnErrorListener(videoErrorListener);
|
||||||
|
((VideoPlayer) mp).setOnBufferingUpdateListener(videoBufferingUpdateListener);
|
||||||
|
((VideoPlayer) mp).setOnInfoListener(videoInfoListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mp;
|
return mp;
|
||||||
}
|
}
|
||||||
|
@ -315,8 +325,7 @@ public class PlaybackService extends Service {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Gained audio focus");
|
Log.d(TAG, "Gained audio focus");
|
||||||
if (pausedBecauseOfTransientAudiofocusLoss) {
|
if (pausedBecauseOfTransientAudiofocusLoss) {
|
||||||
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
|
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0);
|
||||||
AudioManager.ADJUST_RAISE, 0);
|
|
||||||
play();
|
play();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -324,8 +333,7 @@ public class PlaybackService extends Service {
|
||||||
if (status == PlayerStatus.PLAYING) {
|
if (status == PlayerStatus.PLAYING) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Lost audio focus temporarily. Ducking...");
|
Log.d(TAG, "Lost audio focus temporarily. Ducking...");
|
||||||
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
|
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0);
|
||||||
AudioManager.ADJUST_LOWER, 0);
|
|
||||||
pausedBecauseOfTransientAudiofocusLoss = true;
|
pausedBecauseOfTransientAudiofocusLoss = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -354,8 +362,7 @@ public class PlaybackService extends Service {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
|
Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
|
||||||
boolean playbackType = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
|
boolean playbackType = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, true);
|
||||||
true);
|
|
||||||
if (playable == null) {
|
if (playable == null) {
|
||||||
Log.e(TAG, "Playable extra wasn't sent to the service");
|
Log.e(TAG, "Playable extra wasn't sent to the service");
|
||||||
if (media == null) {
|
if (media == null) {
|
||||||
|
@ -363,22 +370,17 @@ public class PlaybackService extends Service {
|
||||||
}
|
}
|
||||||
// Intent values appear to be valid
|
// Intent values appear to be valid
|
||||||
// check if already playing and playbackType is the same
|
// check if already playing and playbackType is the same
|
||||||
} else if (media == null || playable != media
|
} else if (media == null || playable != media || playbackType != shouldStream) {
|
||||||
|| playbackType != shouldStream) {
|
|
||||||
pause(true, false);
|
pause(true, false);
|
||||||
player.reset();
|
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
|
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
|
||||||
if (media == null
|
if (media == null || playable.getIdentifier() != media.getIdentifier()) {
|
||||||
|| playable.getIdentifier() != media.getIdentifier()) {
|
|
||||||
media = playable;
|
media = playable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
shouldStream = playbackType;
|
shouldStream = playbackType;
|
||||||
startWhenPrepared = intent.getBooleanExtra(
|
startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
|
||||||
EXTRA_START_WHEN_PREPARED, false);
|
prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false);
|
||||||
prepareImmediately = intent.getBooleanExtra(
|
|
||||||
EXTRA_PREPARE_IMMEDIATELY, false);
|
|
||||||
initMediaplayer();
|
initMediaplayer();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -442,8 +444,7 @@ public class PlaybackService extends Service {
|
||||||
Log.d(TAG, "Setting display");
|
Log.d(TAG, "Setting display");
|
||||||
player.setDisplay(null);
|
player.setDisplay(null);
|
||||||
player.setDisplay(sh);
|
player.setDisplay(sh);
|
||||||
if (status == PlayerStatus.STOPPED
|
if (status == PlayerStatus.STOPPED || status == PlayerStatus.AWAITING_VIDEO_SURFACE) {
|
||||||
|| status == PlayerStatus.AWAITING_VIDEO_SURFACE) {
|
|
||||||
try {
|
try {
|
||||||
InitTask initTask = new InitTask() {
|
InitTask initTask = new InitTask() {
|
||||||
|
|
||||||
|
@ -453,13 +454,11 @@ public class PlaybackService extends Service {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
try {
|
try {
|
||||||
if (shouldStream) {
|
if (shouldStream) {
|
||||||
player.setDataSource(media
|
player.setDataSource(media.getStreamUrl());
|
||||||
.getStreamUrl());
|
|
||||||
setStatus(PlayerStatus.PREPARING);
|
setStatus(PlayerStatus.PREPARING);
|
||||||
player.prepareAsync();
|
player.prepareAsync();
|
||||||
} else {
|
} else {
|
||||||
player.setDataSource(media
|
player.setDataSource(media.getLocalMediaUrl());
|
||||||
.getLocalMediaUrl());
|
|
||||||
setStatus(PlayerStatus.PREPARING);
|
setStatus(PlayerStatus.PREPARING);
|
||||||
player.prepareAsync();
|
player.prepareAsync();
|
||||||
}
|
}
|
||||||
|
@ -468,8 +467,7 @@ public class PlaybackService extends Service {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setStatus(PlayerStatus.ERROR);
|
setStatus(PlayerStatus.ERROR);
|
||||||
sendBroadcast(new Intent(
|
sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
||||||
ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,7 +498,6 @@ public class PlaybackService extends Service {
|
||||||
player.setDisplay(null);
|
player.setDisplay(null);
|
||||||
player.reset();
|
player.reset();
|
||||||
player.release();
|
player.release();
|
||||||
player = createMediaPlayer();
|
|
||||||
status = PlayerStatus.STOPPED;
|
status = PlayerStatus.STOPPED;
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
initMediaplayer();
|
initMediaplayer();
|
||||||
|
@ -516,6 +513,10 @@ public class PlaybackService extends Service {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Setting up media player");
|
Log.d(TAG, "Setting up media player");
|
||||||
try {
|
try {
|
||||||
|
if (player != null) {
|
||||||
|
player.release();
|
||||||
|
}
|
||||||
|
player = createMediaPlayer();
|
||||||
MediaType mediaType = media.getMediaType();
|
MediaType mediaType = media.getMediaType();
|
||||||
if (mediaType == MediaType.AUDIO) {
|
if (mediaType == MediaType.AUDIO) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
|
@ -533,11 +534,9 @@ public class PlaybackService extends Service {
|
||||||
playingVideo = false;
|
playingVideo = false;
|
||||||
try {
|
try {
|
||||||
if (shouldStream) {
|
if (shouldStream) {
|
||||||
player.setDataSource(media
|
player.setDataSource(media.getStreamUrl());
|
||||||
.getStreamUrl());
|
|
||||||
} else if (media.localFileAvailable()) {
|
} else if (media.localFileAvailable()) {
|
||||||
player.setDataSource(media
|
player.setDataSource(media.getLocalMediaUrl());
|
||||||
.getLocalMediaUrl());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prepareImmediately) {
|
if (prepareImmediately) {
|
||||||
|
@ -550,20 +549,17 @@ public class PlaybackService extends Service {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
media = null;
|
media = null;
|
||||||
setStatus(PlayerStatus.ERROR);
|
setStatus(PlayerStatus.ERROR);
|
||||||
sendBroadcast(new Intent(
|
sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
||||||
ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "InitTask could not load metadata");
|
Log.e(TAG, "InitTask could not load metadata");
|
||||||
media = null;
|
media = null;
|
||||||
setStatus(PlayerStatus.ERROR);
|
setStatus(PlayerStatus.ERROR);
|
||||||
sendBroadcast(new Intent(
|
sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
||||||
ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG,
|
Log.d(TAG, "Status of player has changed during initialization. Stopping init process.");
|
||||||
"Status of player has changed during initialization. Stopping init process.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,14 +588,11 @@ public class PlaybackService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPositionSaver() {
|
private void setupPositionSaver() {
|
||||||
if (positionSaverFuture == null
|
if (positionSaverFuture == null || (positionSaverFuture.isCancelled() || positionSaverFuture.isDone())) {
|
||||||
|| (positionSaverFuture.isCancelled() || positionSaverFuture
|
|
||||||
.isDone())) {
|
|
||||||
|
|
||||||
positionSaver = new PositionSaver();
|
positionSaver = new PositionSaver();
|
||||||
positionSaverFuture = schedExecutor.scheduleAtFixedRate(
|
positionSaverFuture = schedExecutor.scheduleAtFixedRate(positionSaver, PositionSaver.WAITING_INTERVALL, PositionSaver.WAITING_INTERVALL,
|
||||||
positionSaver, PositionSaver.WAITING_INTERVALL,
|
TimeUnit.MILLISECONDS);
|
||||||
PositionSaver.WAITING_INTERVALL, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,106 +604,166 @@ public class PlaybackService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
|
private final com.aocate.media.MediaPlayer.OnPreparedListener audioPreparedListener = new com.aocate.media.MediaPlayer.OnPreparedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPrepared(MediaPlayer mp) {
|
public void onPrepared(com.aocate.media.MediaPlayer mp) {
|
||||||
|
genericOnPrepared(mp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final android.media.MediaPlayer.OnPreparedListener videoPreparedListener = new android.media.MediaPlayer.OnPreparedListener() {
|
||||||
|
@Override
|
||||||
|
public void onPrepared(android.media.MediaPlayer mp) {
|
||||||
|
genericOnPrepared(mp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final void genericOnPrepared(Object inObj) {
|
||||||
|
IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
|
||||||
|
if (AppConfig.DEBUG)
|
||||||
|
Log.d(TAG, "Resource prepared");
|
||||||
|
mp.seekTo(media.getPosition());
|
||||||
|
if (media.getDuration() == 0) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Resource prepared");
|
Log.d(TAG, "Setting duration of media");
|
||||||
mp.seekTo(media.getPosition());
|
media.setDuration(mp.getDuration());
|
||||||
if (media.getDuration() == 0) {
|
}
|
||||||
|
setStatus(PlayerStatus.PREPARED);
|
||||||
|
if (chapterLoader != null) {
|
||||||
|
chapterLoader.interrupt();
|
||||||
|
}
|
||||||
|
chapterLoader = new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Setting duration of media");
|
Log.d(TAG, "Chapter loader started");
|
||||||
media.setDuration(mp.getDuration());
|
if (media != null && media.getChapters() == null) {
|
||||||
}
|
media.loadChapterMarks();
|
||||||
setStatus(PlayerStatus.PREPARED);
|
if (!isInterrupted() && media.getChapters() != null) {
|
||||||
if (chapterLoader != null) {
|
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
|
||||||
chapterLoader.interrupt();
|
|
||||||
}
|
|
||||||
chapterLoader = new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (AppConfig.DEBUG)
|
|
||||||
Log.d(TAG, "Chapter loader started");
|
|
||||||
if (media != null && media.getChapters() == null) {
|
|
||||||
media.loadChapterMarks();
|
|
||||||
if (!isInterrupted() && media.getChapters() != null) {
|
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (AppConfig.DEBUG)
|
|
||||||
Log.d(TAG, "Chapter loader stopped");
|
|
||||||
}
|
}
|
||||||
};
|
if (AppConfig.DEBUG)
|
||||||
chapterLoader.start();
|
Log.d(TAG, "Chapter loader stopped");
|
||||||
|
|
||||||
if (startWhenPrepared) {
|
|
||||||
play();
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
chapterLoader.start();
|
||||||
|
|
||||||
|
if (startWhenPrepared) {
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final com.aocate.media.MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener = new com.aocate.media.MediaPlayer.OnSeekCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onSeekComplete(com.aocate.media.MediaPlayer mp) {
|
||||||
|
genericSeekCompleteListener();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private MediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
|
private final android.media.MediaPlayer.OnSeekCompleteListener videoSeekCompleteListener = new android.media.MediaPlayer.OnSeekCompleteListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSeekComplete(MediaPlayer mp) {
|
public void onSeekComplete(android.media.MediaPlayer mp) {
|
||||||
if (status == PlayerStatus.SEEKING) {
|
genericSeekCompleteListener();
|
||||||
setStatus(statusBeforeSeek);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() {
|
private final void genericSeekCompleteListener() {
|
||||||
|
if (status == PlayerStatus.SEEKING) {
|
||||||
|
setStatus(statusBeforeSeek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final com.aocate.media.MediaPlayer.OnInfoListener audioInfoListener = new com.aocate.media.MediaPlayer.OnInfoListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onInfo(MediaPlayer mp, int what, int extra) {
|
public boolean onInfo(com.aocate.media.MediaPlayer mp, int what, int extra) {
|
||||||
switch (what) {
|
return genericInfoListener(what);
|
||||||
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
|
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
|
|
||||||
return true;
|
|
||||||
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
|
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
|
private final android.media.MediaPlayer.OnInfoListener videoInfoListener = new android.media.MediaPlayer.OnInfoListener() {
|
||||||
private static final String TAG = "PlaybackService.onErrorListener";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
public boolean onInfo(android.media.MediaPlayer mp, int what, int extra) {
|
||||||
Log.w(TAG, "An error has occured: " + what);
|
return genericInfoListener(what);
|
||||||
if (mp.isPlaying()) {
|
}
|
||||||
pause(true, true);
|
};
|
||||||
}
|
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
|
private boolean genericInfoListener(int what) {
|
||||||
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
|
switch (what) {
|
||||||
stopSelf();
|
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
|
||||||
|
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
|
||||||
return true;
|
return true;
|
||||||
|
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
|
||||||
|
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
|
|
||||||
|
|
||||||
|
private final com.aocate.media.MediaPlayer.OnErrorListener audioErrorListener = new com.aocate.media.MediaPlayer.OnErrorListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCompletion(MediaPlayer mp) {
|
public boolean onError(com.aocate.media.MediaPlayer mp, int what, int extra) {
|
||||||
endPlayback(true);
|
return genericOnError(mp, what, extra);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
|
private final android.media.MediaPlayer.OnErrorListener videoErrorListener = new android.media.MediaPlayer.OnErrorListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBufferingUpdate(MediaPlayer mp, int percent) {
|
public boolean onError(android.media.MediaPlayer mp, int what, int extra) {
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
|
return genericOnError(mp, what, extra);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private boolean genericOnError(Object inObj, int what, int extra) {
|
||||||
|
final String TAG = "PlaybackService.onErrorListener";
|
||||||
|
Log.w(TAG, "An error has occured: " + what + " " + extra);
|
||||||
|
IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
|
||||||
|
if (mp.isPlaying()) {
|
||||||
|
pause(true, true);
|
||||||
|
}
|
||||||
|
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
|
||||||
|
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
|
stopSelf();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final com.aocate.media.MediaPlayer.OnCompletionListener audioCompletionListener = new com.aocate.media.MediaPlayer.OnCompletionListener() {
|
||||||
|
@Override
|
||||||
|
public void onCompletion(com.aocate.media.MediaPlayer mp) {
|
||||||
|
genericOnCompletion();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final android.media.MediaPlayer.OnCompletionListener videoCompletionListener = new android.media.MediaPlayer.OnCompletionListener() {
|
||||||
|
@Override
|
||||||
|
public void onCompletion(android.media.MediaPlayer mp) {
|
||||||
|
genericOnCompletion();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void genericOnCompletion() {
|
||||||
|
endPlayback(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final com.aocate.media.MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener = new com.aocate.media.MediaPlayer.OnBufferingUpdateListener() {
|
||||||
|
@Override
|
||||||
|
public void onBufferingUpdate(com.aocate.media.MediaPlayer mp, int percent) {
|
||||||
|
genericOnBufferingUpdate(percent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final android.media.MediaPlayer.OnBufferingUpdateListener videoBufferingUpdateListener = new android.media.MediaPlayer.OnBufferingUpdateListener() {
|
||||||
|
@Override
|
||||||
|
public void onBufferingUpdate(android.media.MediaPlayer mp, int percent) {
|
||||||
|
genericOnBufferingUpdate(percent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void genericOnBufferingUpdate(int percent) {
|
||||||
|
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
|
||||||
|
}
|
||||||
|
|
||||||
private void endPlayback(boolean playNextEpisode) {
|
private void endPlayback(boolean playNextEpisode) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Playback ended");
|
Log.d(TAG, "Playback ended");
|
||||||
|
@ -727,8 +780,7 @@ public class PlaybackService extends Service {
|
||||||
((FeedMedia) media).setPlaybackCompletionDate(new Date());
|
((FeedMedia) media).setPlaybackCompletionDate(new Date());
|
||||||
manager.markItemRead(PlaybackService.this, item, true, true);
|
manager.markItemRead(PlaybackService.this, item, true, true);
|
||||||
nextItem = manager.getQueueSuccessorOfItem(item);
|
nextItem = manager.getQueueSuccessorOfItem(item);
|
||||||
isInQueue = media instanceof FeedMedia
|
isInQueue = media instanceof FeedMedia && manager.isInQueue(((FeedMedia) media).getItem());
|
||||||
&& manager.isInQueue(((FeedMedia) media).getItem());
|
|
||||||
if (isInQueue) {
|
if (isInQueue) {
|
||||||
manager.removeQueueItem(PlaybackService.this, item, true);
|
manager.removeQueueItem(PlaybackService.this, item, true);
|
||||||
}
|
}
|
||||||
|
@ -744,8 +796,7 @@ public class PlaybackService extends Service {
|
||||||
// is an episode in the queue left.
|
// is an episode in the queue left.
|
||||||
// Start playback immediately if continuous playback is enabled
|
// Start playback immediately if continuous playback is enabled
|
||||||
boolean loadNextItem = isInQueue && nextItem != null;
|
boolean loadNextItem = isInQueue && nextItem != null;
|
||||||
playNextEpisode = playNextEpisode && loadNextItem
|
playNextEpisode = playNextEpisode && loadNextItem && UserPreferences.isFollowQueue();
|
||||||
&& UserPreferences.isFollowQueue();
|
|
||||||
if (loadNextItem) {
|
if (loadNextItem) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Loading next item in queue");
|
Log.d(TAG, "Loading next item in queue");
|
||||||
|
@ -779,8 +830,7 @@ public class PlaybackService extends Service {
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
resetVideoSurface();
|
resetVideoSurface();
|
||||||
refreshRemoteControlClientState();
|
refreshRemoteControlClientState();
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
|
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, notificationCode);
|
||||||
notificationCode);
|
|
||||||
} else {
|
} else {
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
|
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
|
||||||
stopSelf();
|
stopSelf();
|
||||||
|
@ -789,8 +839,7 @@ public class PlaybackService extends Service {
|
||||||
|
|
||||||
public void setSleepTimer(long waitingTime) {
|
public void setSleepTimer(long waitingTime) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
|
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds");
|
||||||
+ " milliseconds");
|
|
||||||
if (sleepTimerFuture != null) {
|
if (sleepTimerFuture != null) {
|
||||||
sleepTimerFuture.cancel(true);
|
sleepTimerFuture.cancel(true);
|
||||||
}
|
}
|
||||||
|
@ -819,7 +868,7 @@ public class PlaybackService extends Service {
|
||||||
* file is being streamed
|
* file is being streamed
|
||||||
*/
|
*/
|
||||||
public void pause(boolean abandonFocus, boolean reinit) {
|
public void pause(boolean abandonFocus, boolean reinit) {
|
||||||
if (player.isPlaying()) {
|
if (player != null && player.isPlaying()) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Pausing playback.");
|
Log.d(TAG, "Pausing playback.");
|
||||||
player.pause();
|
player.pause();
|
||||||
|
@ -843,9 +892,7 @@ public class PlaybackService extends Service {
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Stopping playback");
|
Log.d(TAG, "Stopping playback");
|
||||||
if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED
|
if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED || status == PlayerStatus.STOPPED || status == PlayerStatus.PLAYING) {
|
||||||
|| status == PlayerStatus.STOPPED
|
|
||||||
|| status == PlayerStatus.PLAYING) {
|
|
||||||
player.stop();
|
player.stop();
|
||||||
}
|
}
|
||||||
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
|
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
|
@ -868,18 +915,14 @@ public class PlaybackService extends Service {
|
||||||
/** Resets the media player and moves into INITIALIZED state. */
|
/** Resets the media player and moves into INITIALIZED state. */
|
||||||
public void reinit() {
|
public void reinit() {
|
||||||
player.reset();
|
player.reset();
|
||||||
player = createMediaPlayer(player);
|
|
||||||
prepareImmediately = false;
|
prepareImmediately = false;
|
||||||
initMediaplayer();
|
initMediaplayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
public void play() {
|
public void play() {
|
||||||
if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED
|
if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED || status == PlayerStatus.STOPPED) {
|
||||||
|| status == PlayerStatus.STOPPED) {
|
int focusGained = audioManager.requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
||||||
int focusGained = audioManager.requestAudioFocus(
|
|
||||||
audioFocusChangeListener, AudioManager.STREAM_MUSIC,
|
|
||||||
AudioManager.AUDIOFOCUS_GAIN);
|
|
||||||
|
|
||||||
if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
|
@ -890,7 +933,7 @@ public class PlaybackService extends Service {
|
||||||
|
|
||||||
player.start();
|
player.start();
|
||||||
if (status != PlayerStatus.PAUSED) {
|
if (status != PlayerStatus.PAUSED) {
|
||||||
player.seekTo((int) media.getPosition());
|
player.seekTo(media.getPosition());
|
||||||
}
|
}
|
||||||
setStatus(PlayerStatus.PLAYING);
|
setStatus(PlayerStatus.PLAYING);
|
||||||
setupPositionSaver();
|
setupPositionSaver();
|
||||||
|
@ -898,11 +941,9 @@ public class PlaybackService extends Service {
|
||||||
setupNotification();
|
setupNotification();
|
||||||
pausedBecauseOfTransientAudiofocusLoss = false;
|
pausedBecauseOfTransientAudiofocusLoss = false;
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 14) {
|
if (android.os.Build.VERSION.SDK_INT >= 14) {
|
||||||
audioManager
|
audioManager.registerRemoteControlClient(remoteControlClient);
|
||||||
.registerRemoteControlClient(remoteControlClient);
|
|
||||||
}
|
}
|
||||||
audioManager
|
audioManager.registerMediaButtonEventReceiver(mediaButtonReceiver);
|
||||||
.registerMediaButtonEventReceiver(mediaButtonReceiver);
|
|
||||||
media.onPlaybackStart();
|
media.onPlaybackStart();
|
||||||
} else {
|
} else {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
|
@ -915,42 +956,24 @@ public class PlaybackService extends Service {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Writing playback preferences");
|
Log.d(TAG, "Writing playback preferences");
|
||||||
|
|
||||||
SharedPreferences.Editor editor = PreferenceManager
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, media.getPlayableType());
|
||||||
media.getPlayableType());
|
editor.putBoolean(PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM, shouldStream);
|
||||||
editor.putBoolean(
|
editor.putBoolean(PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO, playingVideo);
|
||||||
PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM,
|
|
||||||
shouldStream);
|
|
||||||
editor.putBoolean(
|
|
||||||
PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO,
|
|
||||||
playingVideo);
|
|
||||||
if (media instanceof FeedMedia) {
|
if (media instanceof FeedMedia) {
|
||||||
FeedMedia fMedia = (FeedMedia) media;
|
FeedMedia fMedia = (FeedMedia) media;
|
||||||
editor.putLong(
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, fMedia.getItem().getFeed().getId());
|
||||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, fMedia.getId());
|
||||||
fMedia.getItem().getFeed().getId());
|
|
||||||
editor.putLong(
|
|
||||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
|
||||||
fMedia.getId());
|
|
||||||
} else {
|
} else {
|
||||||
editor.putLong(
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
|
||||||
editor.putLong(
|
|
||||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
|
||||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
|
||||||
}
|
}
|
||||||
media.writeToPreferences(editor);
|
media.writeToPreferences(editor);
|
||||||
} else {
|
} else {
|
||||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
|
||||||
editor.putLong(
|
|
||||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
|
||||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
@ -984,8 +1007,7 @@ public class PlaybackService extends Service {
|
||||||
/** Prepares notification and starts the service in the foreground. */
|
/** Prepares notification and starts the service in the foreground. */
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
private void setupNotification() {
|
private void setupNotification() {
|
||||||
final PendingIntent pIntent = PendingIntent.getActivity(this, 0,
|
final PendingIntent pIntent = PendingIntent.getActivity(this, 0, PlaybackService.getPlayerActivityIntent(this),
|
||||||
PlaybackService.getPlayerActivityIntent(this),
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
if (notificationSetupTask != null) {
|
if (notificationSetupTask != null) {
|
||||||
|
@ -1000,17 +1022,13 @@ public class PlaybackService extends Service {
|
||||||
Log.d(TAG, "Starting background work");
|
Log.d(TAG, "Starting background work");
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||||
if (media != null && media != null) {
|
if (media != null && media != null) {
|
||||||
int iconSize = getResources().getDimensionPixelSize(
|
int iconSize = getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
|
||||||
android.R.dimen.notification_large_icon_width);
|
icon = BitmapDecoder.decodeBitmapFromWorkerTaskResource(iconSize, media);
|
||||||
icon = BitmapDecoder
|
|
||||||
.decodeBitmapFromWorkerTaskResource(iconSize,
|
|
||||||
media);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (icon == null) {
|
if (icon == null) {
|
||||||
icon = BitmapFactory.decodeResource(getResources(),
|
icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stat_antenna);
|
||||||
R.drawable.ic_stat_antenna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -1019,40 +1037,24 @@ public class PlaybackService extends Service {
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void result) {
|
protected void onPostExecute(Void result) {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
if (!isCancelled() && status == PlayerStatus.PLAYING
|
if (!isCancelled() && status == PlayerStatus.PLAYING && media != null) {
|
||||||
&& media != null) {
|
|
||||||
String contentText = media.getFeedTitle();
|
String contentText = media.getFeedTitle();
|
||||||
String contentTitle = media.getEpisodeTitle();
|
String contentTitle = media.getEpisodeTitle();
|
||||||
Notification notification = null;
|
Notification notification = null;
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 16) {
|
if (android.os.Build.VERSION.SDK_INT >= 16) {
|
||||||
Intent pauseButtonIntent = new Intent(
|
Intent pauseButtonIntent = new Intent(PlaybackService.this, PlaybackService.class);
|
||||||
PlaybackService.this, PlaybackService.class);
|
pauseButtonIntent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, KeyEvent.KEYCODE_MEDIA_PAUSE);
|
||||||
pauseButtonIntent.putExtra(
|
PendingIntent pauseButtonPendingIntent = PendingIntent.getService(PlaybackService.this, 0, pauseButtonIntent,
|
||||||
MediaButtonReceiver.EXTRA_KEYCODE,
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
KeyEvent.KEYCODE_MEDIA_PAUSE);
|
Notification.Builder notificationBuilder = new Notification.Builder(PlaybackService.this).setContentTitle(contentTitle)
|
||||||
PendingIntent pauseButtonPendingIntent = PendingIntent
|
.setContentText(contentText).setOngoing(true).setContentIntent(pIntent).setLargeIcon(icon)
|
||||||
.getService(PlaybackService.this, 0,
|
|
||||||
pauseButtonIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
Notification.Builder notificationBuilder = new Notification.Builder(
|
|
||||||
PlaybackService.this)
|
|
||||||
.setContentTitle(contentTitle)
|
|
||||||
.setContentText(contentText)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setContentIntent(pIntent)
|
|
||||||
.setLargeIcon(icon)
|
|
||||||
.setSmallIcon(R.drawable.ic_stat_antenna)
|
.setSmallIcon(R.drawable.ic_stat_antenna)
|
||||||
.addAction(android.R.drawable.ic_media_pause,
|
.addAction(android.R.drawable.ic_media_pause, getString(R.string.pause_label), pauseButtonPendingIntent);
|
||||||
getString(R.string.pause_label),
|
|
||||||
pauseButtonPendingIntent);
|
|
||||||
notification = notificationBuilder.build();
|
notification = notificationBuilder.build();
|
||||||
} else {
|
} else {
|
||||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
|
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(PlaybackService.this)
|
||||||
PlaybackService.this)
|
.setContentTitle(contentTitle).setContentText(contentText).setOngoing(true).setContentIntent(pIntent)
|
||||||
.setContentTitle(contentTitle)
|
.setLargeIcon(icon).setSmallIcon(R.drawable.ic_stat_antenna);
|
||||||
.setContentText(contentText).setOngoing(true)
|
|
||||||
.setContentIntent(pIntent).setLargeIcon(icon)
|
|
||||||
.setSmallIcon(R.drawable.ic_stat_antenna);
|
|
||||||
notification = notificationBuilder.getNotification();
|
notification = notificationBuilder.getNotification();
|
||||||
}
|
}
|
||||||
startForeground(NOTIFICATION_ID, notification);
|
startForeground(NOTIFICATION_ID, notification);
|
||||||
|
@ -1063,8 +1065,7 @@ public class PlaybackService extends Service {
|
||||||
|
|
||||||
};
|
};
|
||||||
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
|
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
|
||||||
notificationSetupTask
|
notificationSetupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
} else {
|
} else {
|
||||||
notificationSetupTask.execute();
|
notificationSetupTask.execute();
|
||||||
}
|
}
|
||||||
|
@ -1086,9 +1087,7 @@ public class PlaybackService extends Service {
|
||||||
|
|
||||||
public void seek(int i) {
|
public void seek(int i) {
|
||||||
saveCurrentPosition();
|
saveCurrentPosition();
|
||||||
if (status == PlayerStatus.INITIALIZED
|
if (status == PlayerStatus.INITIALIZED || status == PlayerStatus.INITIALIZING || status == PlayerStatus.PREPARING) {
|
||||||
|| status == PlayerStatus.INITIALIZING
|
|
||||||
|| status == PlayerStatus.PREPARING) {
|
|
||||||
media.setPosition(i);
|
media.setPosition(i);
|
||||||
setStartWhenPrepared(true);
|
setStartWhenPrepared(true);
|
||||||
prepare();
|
prepare();
|
||||||
|
@ -1115,9 +1114,7 @@ public class PlaybackService extends Service {
|
||||||
if (position != INVALID_TIME) {
|
if (position != INVALID_TIME) {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Saving current position to " + position);
|
Log.d(TAG, "Saving current position to " + position);
|
||||||
media.saveCurrentPosition(PreferenceManager
|
media.saveCurrentPosition(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()), position);
|
||||||
.getDefaultSharedPreferences(getApplicationContext()),
|
|
||||||
position);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1132,22 +1129,17 @@ public class PlaybackService extends Service {
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
private void setupWidgetUpdater() {
|
private void setupWidgetUpdater() {
|
||||||
if (widgetUpdaterFuture == null
|
if (widgetUpdaterFuture == null || (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture.isDone())) {
|
||||||
|| (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture
|
|
||||||
.isDone())) {
|
|
||||||
widgetUpdater = new WidgetUpdateWorker();
|
widgetUpdater = new WidgetUpdateWorker();
|
||||||
widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate(
|
widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate(widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL,
|
||||||
widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL,
|
WidgetUpdateWorker.NOTIFICATION_INTERVALL, TimeUnit.MILLISECONDS);
|
||||||
WidgetUpdateWorker.NOTIFICATION_INTERVALL,
|
|
||||||
TimeUnit.MILLISECONDS);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateWidget() {
|
private void updateWidget() {
|
||||||
if (AppConfig.DEBUG)
|
if (AppConfig.DEBUG)
|
||||||
Log.d(TAG, "Sending widget update request");
|
Log.d(TAG, "Sending widget update request");
|
||||||
PlaybackService.this.sendBroadcast(new Intent(
|
PlaybackService.this.sendBroadcast(new Intent(PlayerWidget.FORCE_WIDGET_UPDATE));
|
||||||
PlayerWidget.FORCE_WIDGET_UPDATE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sleepTimerActive() {
|
public boolean sleepTimerActive() {
|
||||||
|
@ -1166,13 +1158,11 @@ public class PlaybackService extends Service {
|
||||||
private RemoteControlClient setupRemoteControlClient() {
|
private RemoteControlClient setupRemoteControlClient() {
|
||||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||||
mediaButtonIntent.setComponent(mediaButtonReceiver);
|
mediaButtonIntent.setComponent(mediaButtonReceiver);
|
||||||
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(
|
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
|
||||||
getApplicationContext(), 0, mediaButtonIntent, 0);
|
|
||||||
remoteControlClient = new RemoteControlClient(mediaPendingIntent);
|
remoteControlClient = new RemoteControlClient(mediaPendingIntent);
|
||||||
int controlFlags;
|
int controlFlags;
|
||||||
if (android.os.Build.VERSION.SDK_INT < 16) {
|
if (android.os.Build.VERSION.SDK_INT < 16) {
|
||||||
controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
|
controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
|
||||||
| RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
|
|
||||||
} else {
|
} else {
|
||||||
controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
|
controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
|
||||||
}
|
}
|
||||||
|
@ -1187,34 +1177,26 @@ public class PlaybackService extends Service {
|
||||||
if (remoteControlClient != null) {
|
if (remoteControlClient != null) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case PLAYING:
|
case PLAYING:
|
||||||
remoteControlClient
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
|
||||||
.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
|
|
||||||
break;
|
break;
|
||||||
case PAUSED:
|
case PAUSED:
|
||||||
case INITIALIZED:
|
case INITIALIZED:
|
||||||
remoteControlClient
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
|
||||||
.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
|
|
||||||
break;
|
break;
|
||||||
case STOPPED:
|
case STOPPED:
|
||||||
remoteControlClient
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
|
||||||
.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
|
|
||||||
break;
|
break;
|
||||||
case ERROR:
|
case ERROR:
|
||||||
remoteControlClient
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR);
|
||||||
.setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
remoteControlClient
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING);
|
||||||
.setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING);
|
|
||||||
}
|
}
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
MetadataEditor editor = remoteControlClient
|
MetadataEditor editor = remoteControlClient.editMetadata(false);
|
||||||
.editMetadata(false);
|
editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, media.getEpisodeTitle());
|
||||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
|
|
||||||
media.getEpisodeTitle());
|
|
||||||
|
|
||||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
|
editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, media.getFeedTitle());
|
||||||
media.getFeedTitle());
|
|
||||||
|
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
@ -1231,23 +1213,23 @@ public class PlaybackService extends Service {
|
||||||
isPlaying = true;
|
isPlaying = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
|
Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
|
||||||
i.putExtra("id", 1);
|
i.putExtra("id", 1);
|
||||||
i.putExtra("artist", "");
|
i.putExtra("artist", "");
|
||||||
i.putExtra("album", media.getFeedTitle());
|
i.putExtra("album", media.getFeedTitle());
|
||||||
i.putExtra("track", media.getEpisodeTitle());
|
i.putExtra("track", media.getEpisodeTitle());
|
||||||
i.putExtra("playing", isPlaying);
|
i.putExtra("playing", isPlaying);
|
||||||
i.putExtra("ListSize", manager.getQueueSize(false));
|
i.putExtra("ListSize", manager.getQueueSize(false));
|
||||||
i.putExtra("duration", media.getDuration());
|
i.putExtra("duration", media.getDuration());
|
||||||
i.putExtra("position", media.getPosition());
|
i.putExtra("position", media.getPosition());
|
||||||
sendBroadcast(i);
|
sendBroadcast(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses playback when the headset is disconnected and the preference is
|
* Pauses playback when the headset is disconnected and the preference is
|
||||||
* set
|
* set
|
||||||
*/
|
*/
|
||||||
private BroadcastReceiver headsetDisconnected = new BroadcastReceiver() {
|
private final BroadcastReceiver headsetDisconnected = new BroadcastReceiver() {
|
||||||
private static final String TAG = "headsetDisconnected";
|
private static final String TAG = "headsetDisconnected";
|
||||||
private static final int UNPLUGGED = 0;
|
private static final int UNPLUGGED = 0;
|
||||||
|
|
||||||
|
@ -1270,7 +1252,7 @@ public class PlaybackService extends Service {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private BroadcastReceiver audioBecomingNoisy = new BroadcastReceiver() {
|
private final BroadcastReceiver audioBecomingNoisy = new BroadcastReceiver() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
@ -1284,13 +1266,12 @@ public class PlaybackService extends Service {
|
||||||
|
|
||||||
/** Pauses playback if PREF_PAUSE_ON_HEADSET_DISCONNECT was set to true. */
|
/** Pauses playback if PREF_PAUSE_ON_HEADSET_DISCONNECT was set to true. */
|
||||||
private void pauseIfPauseOnDisconnect() {
|
private void pauseIfPauseOnDisconnect() {
|
||||||
if (UserPreferences.isPauseOnHeadsetDisconnect()
|
if (UserPreferences.isPauseOnHeadsetDisconnect() && status == PlayerStatus.PLAYING) {
|
||||||
&& status == PlayerStatus.PLAYING) {
|
|
||||||
pause(true, true);
|
pause(true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
@ -1303,7 +1284,7 @@ public class PlaybackService extends Service {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private BroadcastReceiver skipCurrentEpisodeReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver skipCurrentEpisodeReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent.getAction().equals(ACTION_SKIP_CURRENT_EPISODE)) {
|
if (intent.getAction().equals(ACTION_SKIP_CURRENT_EPISODE)) {
|
||||||
|
@ -1329,8 +1310,7 @@ public class PlaybackService extends Service {
|
||||||
try {
|
try {
|
||||||
saveCurrentPosition();
|
saveCurrentPosition();
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
Log.w(TAG,
|
Log.w(TAG, "saveCurrentPosition was called in illegal state");
|
||||||
"saveCurrentPosition was called in illegal state");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1419,7 +1399,7 @@ public class PlaybackService extends Service {
|
||||||
return media;
|
return media;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaPlayer getPlayer() {
|
public IPlayer getPlayer() {
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1432,6 +1412,38 @@ public class PlaybackService extends Service {
|
||||||
postStatusUpdateIntent();
|
postStatusUpdateIntent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canSetSpeed() {
|
||||||
|
if (media.getMediaType() == MediaType.AUDIO) {
|
||||||
|
return ((AudioPlayer) player).canSetSpeed();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canSetPitch() {
|
||||||
|
if (media.getMediaType() == MediaType.AUDIO) {
|
||||||
|
return ((AudioPlayer) player).canSetPitch();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeed(double speed) {
|
||||||
|
if (media.getMediaType() == MediaType.AUDIO) {
|
||||||
|
AudioPlayer audioPlayer = (AudioPlayer) player;
|
||||||
|
if (audioPlayer.canSetSpeed()) {
|
||||||
|
audioPlayer.setPlaybackSpeed((float) speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPitch(double pitch) {
|
||||||
|
if (media.getMediaType() == MediaType.AUDIO) {
|
||||||
|
AudioPlayer audioPlayer = (AudioPlayer) player;
|
||||||
|
if (audioPlayer.canSetPitch()) {
|
||||||
|
audioPlayer.setPlaybackPitch((float) pitch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* call getDuration() on mediaplayer or return INVALID_TIME if player is in
|
* call getDuration() on mediaplayer or return INVALID_TIME if player is in
|
||||||
* an invalid state. This method should be used instead of calling
|
* an invalid state. This method should be used instead of calling
|
||||||
|
@ -1480,8 +1492,7 @@ public class PlaybackService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCurrentlyPlayingMedia(long id) {
|
private void setCurrentlyPlayingMedia(long id) {
|
||||||
SharedPreferences.Editor editor = PreferenceManager
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
|
||||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, id);
|
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, id);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */
|
||||||
|
|
||||||
|
package de.danoeh.antennapod.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows "duck typing" or dynamic invocation based on method signature rather
|
||||||
|
* than type hierarchy. In other words, rather than checking whether something
|
||||||
|
* IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck.
|
||||||
|
*
|
||||||
|
* To use first use the coerce static method to indicate the object you want to
|
||||||
|
* do Duck Typing for, then specify an interface to the to method which you want
|
||||||
|
* to coerce the type to, e.g:
|
||||||
|
*
|
||||||
|
* public interface Foo { void aMethod(); } class Bar { ... public void
|
||||||
|
* aMethod() { ... } ... } Bar bar = ...; Foo foo =
|
||||||
|
* DuckType.coerce(bar).to(Foo.class); foo.aMethod();
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DuckType {
|
||||||
|
|
||||||
|
private final Object objectToCoerce;
|
||||||
|
|
||||||
|
private DuckType(Object objectToCoerce) {
|
||||||
|
this.objectToCoerce = objectToCoerce;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CoercedProxy implements InvocationHandler {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
Method delegateMethod = findMethodBySignature(method);
|
||||||
|
assert delegateMethod != null;
|
||||||
|
return delegateMethod.invoke(DuckType.this.objectToCoerce, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the duck typed object to coerce.
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* the object to coerce
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static DuckType coerce(Object object) {
|
||||||
|
return new DuckType(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coerce the Duck Typed object to the given interface providing it
|
||||||
|
* implements all the necessary methods.
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
* @param iface
|
||||||
|
* @return an instance of the given interface that wraps the duck typed
|
||||||
|
* class
|
||||||
|
* @throws ClassCastException
|
||||||
|
* if the object being coerced does not implement all the
|
||||||
|
* methods in the given interface.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
public <T> T to(Class iface) {
|
||||||
|
assert iface.isInterface() : "cannot coerce object to a class, must be an interface";
|
||||||
|
if (isA(iface)) {
|
||||||
|
return (T) iface.cast(objectToCoerce);
|
||||||
|
}
|
||||||
|
if (quacksLikeA(iface)) {
|
||||||
|
return generateProxy(iface);
|
||||||
|
}
|
||||||
|
throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private boolean isA(Class iface) {
|
||||||
|
return objectToCoerce.getClass().isInstance(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the duck typed object can be used with the given
|
||||||
|
* interface.
|
||||||
|
*
|
||||||
|
* @param Type
|
||||||
|
* of the interface to check.
|
||||||
|
* @param iface
|
||||||
|
* Interface class to check
|
||||||
|
* @return true if the object will support all the methods in the interface,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public boolean quacksLikeA(Class iface) {
|
||||||
|
for (Method method : iface.getMethods()) {
|
||||||
|
if (findMethodBySignature(method) == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
private <T> T generateProxy(Class iface) {
|
||||||
|
return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method findMethodBySignature(Method method) {
|
||||||
|
try {
|
||||||
|
return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package de.danoeh.antennapod.util.playback;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
|
||||||
|
import com.aocate.media.MediaPlayer;
|
||||||
|
|
||||||
|
public class AudioPlayer extends MediaPlayer implements IPlayer {
|
||||||
|
private static final String TAG = "AudioPlayer";
|
||||||
|
|
||||||
|
public AudioPlayer(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setScreenOnWhilePlaying(boolean screenOn) {
|
||||||
|
Log.e(TAG, "Setting screen on while playing not supported in Audio Player");
|
||||||
|
throw new UnsupportedOperationException("Setting screen on while playing not supported in Audio Player");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDisplay(SurfaceHolder sh) {
|
||||||
|
if (sh != null) {
|
||||||
|
Log.e(TAG, "Setting display not supported in Audio Player");
|
||||||
|
throw new UnsupportedOperationException("Setting display not supported in Audio Player");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package de.danoeh.antennapod.util.playback;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
|
||||||
|
public interface IPlayer {
|
||||||
|
boolean canSetPitch();
|
||||||
|
|
||||||
|
boolean canSetSpeed();
|
||||||
|
|
||||||
|
float getCurrentPitchStepsAdjustment();
|
||||||
|
|
||||||
|
int getCurrentPosition();
|
||||||
|
|
||||||
|
float getCurrentSpeedMultiplier();
|
||||||
|
|
||||||
|
int getDuration();
|
||||||
|
|
||||||
|
float getMaxSpeedMultiplier();
|
||||||
|
|
||||||
|
float getMinSpeedMultiplier();
|
||||||
|
|
||||||
|
boolean isLooping();
|
||||||
|
|
||||||
|
boolean isPlaying();
|
||||||
|
|
||||||
|
void pause();
|
||||||
|
|
||||||
|
void prepare() throws IllegalStateException, IOException;
|
||||||
|
|
||||||
|
void prepareAsync();
|
||||||
|
|
||||||
|
void release();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void seekTo(int msec);
|
||||||
|
|
||||||
|
void setAudioStreamType(int streamtype);
|
||||||
|
|
||||||
|
void setScreenOnWhilePlaying(boolean screenOn);
|
||||||
|
|
||||||
|
void setDataSource(String path) throws IllegalStateException, IOException,
|
||||||
|
IllegalArgumentException, SecurityException;
|
||||||
|
|
||||||
|
void setDisplay(SurfaceHolder sh);
|
||||||
|
|
||||||
|
void setEnableSpeedAdjustment(boolean enableSpeedAdjustment);
|
||||||
|
|
||||||
|
void setLooping(boolean looping);
|
||||||
|
|
||||||
|
void setPitchStepsAdjustment(float pitchSteps);
|
||||||
|
|
||||||
|
void setPlaybackPitch(float f);
|
||||||
|
|
||||||
|
void setPlaybackSpeed(float f);
|
||||||
|
|
||||||
|
void setVolume(float left, float right);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package de.danoeh.antennapod.util.playback;
|
||||||
|
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class VideoPlayer extends MediaPlayer implements IPlayer {
|
||||||
|
private static final String TAG = "VideoPlayer";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSetPitch() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSetSpeed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getCurrentPitchStepsAdjustment() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getCurrentSpeedMultiplier() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getMaxSpeedMultiplier() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getMinSpeedMultiplier() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) throws UnsupportedOperationException {
|
||||||
|
Log.e(TAG, "Setting enable speed adjustment unsupported in video player");
|
||||||
|
throw new UnsupportedOperationException("Setting enable speed adjustment unsupported in video player");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPitchStepsAdjustment(float pitchSteps) {
|
||||||
|
Log.e(TAG, "Setting pitch steps adjustment unsupported in video player");
|
||||||
|
throw new UnsupportedOperationException("Setting pitch steps adjustment unsupported in video player");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlaybackPitch(float f) {
|
||||||
|
Log.e(TAG, "Setting playback pitch unsupported in video player");
|
||||||
|
throw new UnsupportedOperationException("Setting playback pitch unsupported in video player");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlaybackSpeed(float f) {
|
||||||
|
Log.e(TAG, "Setting playback speed unsupported in video player");
|
||||||
|
throw new UnsupportedOperationException("Setting playback speed unsupported in video player");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue