From a86501c7957754b9761c1ae7a6e57d8d742f6e28 Mon Sep 17 00:00:00 2001 From: James Falcon Date: Wed, 1 May 2013 21:29:43 -0500 Subject: Adding audio variable speed playback support at the service level --- .../danoeh/antennapod/service/PlaybackService.java | 603 +++++++++++---------- src/de/danoeh/antennapod/util/DuckType.java | 115 ++++ .../antennapod/util/playback/AudioPlayer.java | 30 + .../danoeh/antennapod/util/playback/IPlayer.java | 64 +++ .../antennapod/util/playback/VideoPlayer.java | 62 +++ 5 files changed, 578 insertions(+), 296 deletions(-) create mode 100644 src/de/danoeh/antennapod/util/DuckType.java create mode 100644 src/de/danoeh/antennapod/util/playback/AudioPlayer.java create mode 100644 src/de/danoeh/antennapod/util/playback/IPlayer.java create mode 100644 src/de/danoeh/antennapod/util/playback/VideoPlayer.java (limited to 'src') diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index 47c73648a..545d3e555 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -51,9 +51,13 @@ import de.danoeh.antennapod.preferences.UserPreferences; import de.danoeh.antennapod.receiver.MediaButtonReceiver; import de.danoeh.antennapod.receiver.PlayerWidget; import de.danoeh.antennapod.util.BitmapDecoder; +import de.danoeh.antennapod.util.DuckType; 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.PlayableException; +import de.danoeh.antennapod.util.playback.VideoPlayer; /** Controls the MediaPlayer that plays a FeedMedia-file */ public class PlaybackService extends Service { @@ -73,8 +77,8 @@ public class PlaybackService extends Service { 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"; - 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 EXTRA_NOTIFICATION_CODE = "extra.de.danoeh.antennapod.service.notificationCode"; public static final String EXTRA_NOTIFICATION_TYPE = "extra.de.danoeh.antennapod.service.notificationType"; @@ -120,7 +124,7 @@ public class PlaybackService extends Service { private AudioManager audioManager; private ComponentName mediaButtonReceiver; - private MediaPlayer player; + private IPlayer player; private RemoteControlClient remoteControlClient; private Playable media; @@ -215,55 +219,61 @@ public class PlaybackService extends Service { status = PlayerStatus.STOPPED; audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); manager = FeedManager.getInstance(); - schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE, - new ThreadFactory() { + schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE, new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setPriority(Thread.MIN_PRIORITY); - return t; - } - }, new RejectedExecutionHandler() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + }, new RejectedExecutionHandler() { - @Override - public void rejectedExecution(Runnable r, - ThreadPoolExecutor executor) { - Log.w(TAG, "SchedEx rejected submission of new task"); - } - }); - player = createMediaPlayer(); + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + Log.w(TAG, "SchedEx rejected submission of new task"); + } + }); - mediaButtonReceiver = new ComponentName(getPackageName(), - MediaButtonReceiver.class.getName()); + mediaButtonReceiver = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName()); audioManager.registerMediaButtonEventReceiver(mediaButtonReceiver); if (android.os.Build.VERSION.SDK_INT >= 14) { - audioManager - .registerRemoteControlClient(setupRemoteControlClient()); + audioManager.registerRemoteControlClient(setupRemoteControlClient()); } - registerReceiver(headsetDisconnected, new IntentFilter( - Intent.ACTION_HEADSET_PLUG)); - registerReceiver(shutdownReceiver, new IntentFilter( - ACTION_SHUTDOWN_PLAYBACK_SERVICE)); - registerReceiver(audioBecomingNoisy, new IntentFilter( - AudioManager.ACTION_AUDIO_BECOMING_NOISY)); - registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter( - ACTION_SKIP_CURRENT_EPISODE)); + registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); + registerReceiver(shutdownReceiver, new IntentFilter(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + registerReceiver(audioBecomingNoisy, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); + registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(ACTION_SKIP_CURRENT_EPISODE)); } - private MediaPlayer createMediaPlayer() { - return createMediaPlayer(new MediaPlayer()); + private IPlayer createMediaPlayer() { + 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) { - if (mp != null) { - mp.setOnPreparedListener(preparedListener); - mp.setOnCompletionListener(completionListener); - mp.setOnSeekCompleteListener(onSeekCompleteListener); - mp.setOnErrorListener(onErrorListener); - mp.setOnBufferingUpdateListener(onBufferingUpdateListener); - mp.setOnInfoListener(onInfoListener); + private IPlayer createMediaPlayer(IPlayer mp) { + if (mp != null && media != null) { + if (media.getMediaType() == MediaType.AUDIO) { + ((AudioPlayer) mp).setOnPreparedListener(audioPreparedListener); + ((AudioPlayer) mp).setOnCompletionListener(audioCompletionListener); + ((AudioPlayer) mp).setOnSeekCompleteListener(audioSeekCompleteListener); + ((AudioPlayer) mp).setOnErrorListener(audioErrorListener); + ((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; } @@ -315,8 +325,7 @@ public class PlaybackService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "Gained audio focus"); if (pausedBecauseOfTransientAudiofocusLoss) { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_RAISE, 0); + audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0); play(); } break; @@ -324,8 +333,7 @@ public class PlaybackService extends Service { if (status == PlayerStatus.PLAYING) { if (AppConfig.DEBUG) Log.d(TAG, "Lost audio focus temporarily. Ducking..."); - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_LOWER, 0); + audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0); pausedBecauseOfTransientAudiofocusLoss = true; } break; @@ -354,8 +362,7 @@ public class PlaybackService extends Service { } else { Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE); - boolean playbackType = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, - true); + boolean playbackType = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, true); if (playable == null) { Log.e(TAG, "Playable extra wasn't sent to the service"); if (media == null) { @@ -363,22 +370,17 @@ public class PlaybackService extends Service { } // Intent values appear to be valid // check if already playing and playbackType is the same - } else if (media == null || playable != media - || playbackType != shouldStream) { + } else if (media == null || playable != media || playbackType != shouldStream) { pause(true, false); - player.reset(); sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0); - if (media == null - || playable.getIdentifier() != media.getIdentifier()) { + if (media == null || playable.getIdentifier() != media.getIdentifier()) { media = playable; } if (media != null) { shouldStream = playbackType; - startWhenPrepared = intent.getBooleanExtra( - EXTRA_START_WHEN_PREPARED, false); - prepareImmediately = intent.getBooleanExtra( - EXTRA_PREPARE_IMMEDIATELY, false); + startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false); + prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false); initMediaplayer(); } else { @@ -442,8 +444,7 @@ public class PlaybackService extends Service { Log.d(TAG, "Setting display"); player.setDisplay(null); player.setDisplay(sh); - if (status == PlayerStatus.STOPPED - || status == PlayerStatus.AWAITING_VIDEO_SURFACE) { + if (status == PlayerStatus.STOPPED || status == PlayerStatus.AWAITING_VIDEO_SURFACE) { try { InitTask initTask = new InitTask() { @@ -453,13 +454,11 @@ public class PlaybackService extends Service { if (result != null) { try { if (shouldStream) { - player.setDataSource(media - .getStreamUrl()); + player.setDataSource(media.getStreamUrl()); setStatus(PlayerStatus.PREPARING); player.prepareAsync(); } else { - player.setDataSource(media - .getLocalMediaUrl()); + player.setDataSource(media.getLocalMediaUrl()); setStatus(PlayerStatus.PREPARING); player.prepareAsync(); } @@ -468,8 +467,7 @@ public class PlaybackService extends Service { } } else { setStatus(PlayerStatus.ERROR); - sendBroadcast(new Intent( - ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); } } } @@ -500,7 +498,6 @@ public class PlaybackService extends Service { player.setDisplay(null); player.reset(); player.release(); - player = createMediaPlayer(); status = PlayerStatus.STOPPED; if (media != null) { initMediaplayer(); @@ -516,6 +513,10 @@ public class PlaybackService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "Setting up media player"); try { + if (player != null) { + player.release(); + } + player = createMediaPlayer(); MediaType mediaType = media.getMediaType(); if (mediaType == MediaType.AUDIO) { if (AppConfig.DEBUG) @@ -533,11 +534,9 @@ public class PlaybackService extends Service { playingVideo = false; try { if (shouldStream) { - player.setDataSource(media - .getStreamUrl()); + player.setDataSource(media.getStreamUrl()); } else if (media.localFileAvailable()) { - player.setDataSource(media - .getLocalMediaUrl()); + player.setDataSource(media.getLocalMediaUrl()); } if (prepareImmediately) { @@ -550,20 +549,17 @@ public class PlaybackService extends Service { e.printStackTrace(); media = null; setStatus(PlayerStatus.ERROR); - sendBroadcast(new Intent( - ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); } } else { Log.e(TAG, "InitTask could not load metadata"); media = null; setStatus(PlayerStatus.ERROR); - sendBroadcast(new Intent( - ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); } } else { if (AppConfig.DEBUG) - Log.d(TAG, - "Status of player has changed during initialization. Stopping init process."); + Log.d(TAG, "Status of player has changed during initialization. Stopping init process."); } } @@ -592,14 +588,11 @@ public class PlaybackService extends Service { } private void setupPositionSaver() { - if (positionSaverFuture == null - || (positionSaverFuture.isCancelled() || positionSaverFuture - .isDone())) { + if (positionSaverFuture == null || (positionSaverFuture.isCancelled() || positionSaverFuture.isDone())) { positionSaver = new PositionSaver(); - positionSaverFuture = schedExecutor.scheduleAtFixedRate( - positionSaver, PositionSaver.WAITING_INTERVALL, - PositionSaver.WAITING_INTERVALL, TimeUnit.MILLISECONDS); + positionSaverFuture = schedExecutor.scheduleAtFixedRate(positionSaver, PositionSaver.WAITING_INTERVALL, 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 + 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(MediaPlayer mp) { + 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) - Log.d(TAG, "Resource prepared"); - mp.seekTo(media.getPosition()); - if (media.getDuration() == 0) { + Log.d(TAG, "Setting duration of media"); + media.setDuration(mp.getDuration()); + } + setStatus(PlayerStatus.PREPARED); + if (chapterLoader != null) { + chapterLoader.interrupt(); + } + chapterLoader = new Thread() { + @Override + public void run() { if (AppConfig.DEBUG) - Log.d(TAG, "Setting duration of media"); - media.setDuration(mp.getDuration()); - } - setStatus(PlayerStatus.PREPARED); - if (chapterLoader != null) { - 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); - } + 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"); } - }; - chapterLoader.start(); - - if (startWhenPrepared) { - play(); + if (AppConfig.DEBUG) + Log.d(TAG, "Chapter loader stopped"); } + }; + 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 + public void onSeekComplete(android.media.MediaPlayer mp) { + genericSeekCompleteListener(); + } + }; + + 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 - public void onSeekComplete(MediaPlayer mp) { - if (status == PlayerStatus.SEEKING) { - setStatus(statusBeforeSeek); - } + public boolean onInfo(com.aocate.media.MediaPlayer mp, int what, int extra) { + return genericInfoListener(what); + } + }; + private final android.media.MediaPlayer.OnInfoListener videoInfoListener = new android.media.MediaPlayer.OnInfoListener() { + @Override + public boolean onInfo(android.media.MediaPlayer mp, int what, int extra) { + return genericInfoListener(what); } }; - private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() { + private boolean genericInfoListener(int what) { + switch (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 final com.aocate.media.MediaPlayer.OnErrorListener audioErrorListener = new com.aocate.media.MediaPlayer.OnErrorListener() { @Override - public boolean onInfo(MediaPlayer mp, int what, int extra) { - switch (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; - } + public boolean onError(com.aocate.media.MediaPlayer mp, int what, int extra) { + return genericOnError(mp, what, extra); } }; - private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() { - private static final String TAG = "PlaybackService.onErrorListener"; - + private final android.media.MediaPlayer.OnErrorListener videoErrorListener = new android.media.MediaPlayer.OnErrorListener() { @Override - public boolean onError(MediaPlayer mp, int what, int extra) { - Log.w(TAG, "An error has occured: " + what); - if (mp.isPlaying()) { - pause(true, true); - } - sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what); - setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING); - stopSelf(); - return true; + public boolean onError(android.media.MediaPlayer mp, int what, int extra) { + return genericOnError(mp, what, extra); } }; - private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() { + 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(MediaPlayer mp) { - endPlayback(true); + public void onCompletion(android.media.MediaPlayer mp) { + genericOnCompletion(); } }; - private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { + private void genericOnCompletion() { + endPlayback(true); + } + private final com.aocate.media.MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener = new com.aocate.media.MediaPlayer.OnBufferingUpdateListener() { @Override - public void onBufferingUpdate(MediaPlayer mp, int percent) { - sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent); + 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) { if (AppConfig.DEBUG) Log.d(TAG, "Playback ended"); @@ -727,8 +780,7 @@ public class PlaybackService extends Service { ((FeedMedia) media).setPlaybackCompletionDate(new Date()); manager.markItemRead(PlaybackService.this, item, true, true); nextItem = manager.getQueueSuccessorOfItem(item); - isInQueue = media instanceof FeedMedia - && manager.isInQueue(((FeedMedia) media).getItem()); + isInQueue = media instanceof FeedMedia && manager.isInQueue(((FeedMedia) media).getItem()); if (isInQueue) { manager.removeQueueItem(PlaybackService.this, item, true); } @@ -744,8 +796,7 @@ public class PlaybackService extends Service { // is an episode in the queue left. // Start playback immediately if continuous playback is enabled boolean loadNextItem = isInQueue && nextItem != null; - playNextEpisode = playNextEpisode && loadNextItem - && UserPreferences.isFollowQueue(); + playNextEpisode = playNextEpisode && loadNextItem && UserPreferences.isFollowQueue(); if (loadNextItem) { if (AppConfig.DEBUG) Log.d(TAG, "Loading next item in queue"); @@ -779,8 +830,7 @@ public class PlaybackService extends Service { if (media != null) { resetVideoSurface(); refreshRemoteControlClientState(); - sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, - notificationCode); + sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, notificationCode); } else { sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0); stopSelf(); @@ -789,8 +839,7 @@ public class PlaybackService extends Service { public void setSleepTimer(long waitingTime) { if (AppConfig.DEBUG) - Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) - + " milliseconds"); + Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds"); if (sleepTimerFuture != null) { sleepTimerFuture.cancel(true); } @@ -819,7 +868,7 @@ public class PlaybackService extends Service { * file is being streamed */ public void pause(boolean abandonFocus, boolean reinit) { - if (player.isPlaying()) { + if (player != null && player.isPlaying()) { if (AppConfig.DEBUG) Log.d(TAG, "Pausing playback."); player.pause(); @@ -843,9 +892,7 @@ public class PlaybackService extends Service { public void stop() { if (AppConfig.DEBUG) Log.d(TAG, "Stopping playback"); - if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED - || status == PlayerStatus.STOPPED - || status == PlayerStatus.PLAYING) { + if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED || status == PlayerStatus.STOPPED || status == PlayerStatus.PLAYING) { player.stop(); } setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING); @@ -868,18 +915,14 @@ public class PlaybackService extends Service { /** Resets the media player and moves into INITIALIZED state. */ public void reinit() { player.reset(); - player = createMediaPlayer(player); prepareImmediately = false; initMediaplayer(); } @SuppressLint("NewApi") public void play() { - if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED - || status == PlayerStatus.STOPPED) { - int focusGained = audioManager.requestAudioFocus( - audioFocusChangeListener, AudioManager.STREAM_MUSIC, - AudioManager.AUDIOFOCUS_GAIN); + if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED || status == PlayerStatus.STOPPED) { + int focusGained = audioManager.requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { if (AppConfig.DEBUG) @@ -890,7 +933,7 @@ public class PlaybackService extends Service { player.start(); if (status != PlayerStatus.PAUSED) { - player.seekTo((int) media.getPosition()); + player.seekTo(media.getPosition()); } setStatus(PlayerStatus.PLAYING); setupPositionSaver(); @@ -898,11 +941,9 @@ public class PlaybackService extends Service { setupNotification(); pausedBecauseOfTransientAudiofocusLoss = false; if (android.os.Build.VERSION.SDK_INT >= 14) { - audioManager - .registerRemoteControlClient(remoteControlClient); + audioManager.registerRemoteControlClient(remoteControlClient); } - audioManager - .registerMediaButtonEventReceiver(mediaButtonReceiver); + audioManager.registerMediaButtonEventReceiver(mediaButtonReceiver); media.onPlaybackStart(); } else { if (AppConfig.DEBUG) @@ -915,42 +956,24 @@ public class PlaybackService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "Writing playback preferences"); - SharedPreferences.Editor editor = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()).edit(); + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); if (media != null) { - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, - media.getPlayableType()); - editor.putBoolean( - PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM, - shouldStream); - editor.putBoolean( - PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO, - playingVideo); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, media.getPlayableType()); + editor.putBoolean(PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM, shouldStream); + editor.putBoolean(PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO, playingVideo); if (media instanceof FeedMedia) { FeedMedia fMedia = (FeedMedia) media; - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, - fMedia.getItem().getFeed().getId()); - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, - fMedia.getId()); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, fMedia.getItem().getFeed().getId()); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, fMedia.getId()); } else { - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, PlaybackPreferences.NO_MEDIA_PLAYING); } media.writeToPreferences(editor); } else { - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, - PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, PlaybackPreferences.NO_MEDIA_PLAYING); } editor.commit(); @@ -984,8 +1007,7 @@ public class PlaybackService extends Service { /** Prepares notification and starts the service in the foreground. */ @SuppressLint("NewApi") private void setupNotification() { - final PendingIntent pIntent = PendingIntent.getActivity(this, 0, - PlaybackService.getPlayerActivityIntent(this), + final PendingIntent pIntent = PendingIntent.getActivity(this, 0, PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT); if (notificationSetupTask != null) { @@ -1000,17 +1022,13 @@ public class PlaybackService extends Service { Log.d(TAG, "Starting background work"); if (android.os.Build.VERSION.SDK_INT >= 11) { if (media != null && media != null) { - int iconSize = getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_width); - icon = BitmapDecoder - .decodeBitmapFromWorkerTaskResource(iconSize, - media); + int iconSize = getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width); + icon = BitmapDecoder.decodeBitmapFromWorkerTaskResource(iconSize, media); } } if (icon == null) { - icon = BitmapFactory.decodeResource(getResources(), - R.drawable.ic_stat_antenna); + icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stat_antenna); } return null; @@ -1019,40 +1037,24 @@ public class PlaybackService extends Service { @Override protected void onPostExecute(Void result) { super.onPostExecute(result); - if (!isCancelled() && status == PlayerStatus.PLAYING - && media != null) { + if (!isCancelled() && status == PlayerStatus.PLAYING && media != null) { String contentText = media.getFeedTitle(); String contentTitle = media.getEpisodeTitle(); Notification notification = null; if (android.os.Build.VERSION.SDK_INT >= 16) { - Intent pauseButtonIntent = new Intent( - PlaybackService.this, PlaybackService.class); - pauseButtonIntent.putExtra( - MediaButtonReceiver.EXTRA_KEYCODE, - KeyEvent.KEYCODE_MEDIA_PAUSE); - PendingIntent pauseButtonPendingIntent = PendingIntent - .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) + Intent pauseButtonIntent = new Intent(PlaybackService.this, PlaybackService.class); + pauseButtonIntent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, KeyEvent.KEYCODE_MEDIA_PAUSE); + PendingIntent pauseButtonPendingIntent = PendingIntent.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) - .addAction(android.R.drawable.ic_media_pause, - getString(R.string.pause_label), - pauseButtonPendingIntent); + .addAction(android.R.drawable.ic_media_pause, getString(R.string.pause_label), pauseButtonPendingIntent); notification = notificationBuilder.build(); } else { - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder( - PlaybackService.this) - .setContentTitle(contentTitle) - .setContentText(contentText).setOngoing(true) - .setContentIntent(pIntent).setLargeIcon(icon) - .setSmallIcon(R.drawable.ic_stat_antenna); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(PlaybackService.this) + .setContentTitle(contentTitle).setContentText(contentText).setOngoing(true).setContentIntent(pIntent) + .setLargeIcon(icon).setSmallIcon(R.drawable.ic_stat_antenna); notification = notificationBuilder.getNotification(); } 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) { - notificationSetupTask - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + notificationSetupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { notificationSetupTask.execute(); } @@ -1086,9 +1087,7 @@ public class PlaybackService extends Service { public void seek(int i) { saveCurrentPosition(); - if (status == PlayerStatus.INITIALIZED - || status == PlayerStatus.INITIALIZING - || status == PlayerStatus.PREPARING) { + if (status == PlayerStatus.INITIALIZED || status == PlayerStatus.INITIALIZING || status == PlayerStatus.PREPARING) { media.setPosition(i); setStartWhenPrepared(true); prepare(); @@ -1115,9 +1114,7 @@ public class PlaybackService extends Service { if (position != INVALID_TIME) { if (AppConfig.DEBUG) Log.d(TAG, "Saving current position to " + position); - media.saveCurrentPosition(PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()), - position); + media.saveCurrentPosition(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()), position); } } @@ -1132,22 +1129,17 @@ public class PlaybackService extends Service { @SuppressLint("NewApi") private void setupWidgetUpdater() { - if (widgetUpdaterFuture == null - || (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture - .isDone())) { + if (widgetUpdaterFuture == null || (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture.isDone())) { widgetUpdater = new WidgetUpdateWorker(); - widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate( - widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL, - WidgetUpdateWorker.NOTIFICATION_INTERVALL, - TimeUnit.MILLISECONDS); + widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate(widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL, + WidgetUpdateWorker.NOTIFICATION_INTERVALL, TimeUnit.MILLISECONDS); } } private void updateWidget() { if (AppConfig.DEBUG) Log.d(TAG, "Sending widget update request"); - PlaybackService.this.sendBroadcast(new Intent( - PlayerWidget.FORCE_WIDGET_UPDATE)); + PlaybackService.this.sendBroadcast(new Intent(PlayerWidget.FORCE_WIDGET_UPDATE)); } public boolean sleepTimerActive() { @@ -1166,13 +1158,11 @@ public class PlaybackService extends Service { private RemoteControlClient setupRemoteControlClient() { Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); mediaButtonIntent.setComponent(mediaButtonReceiver); - PendingIntent mediaPendingIntent = PendingIntent.getBroadcast( - getApplicationContext(), 0, mediaButtonIntent, 0); + PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); remoteControlClient = new RemoteControlClient(mediaPendingIntent); int controlFlags; if (android.os.Build.VERSION.SDK_INT < 16) { - controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE - | RemoteControlClient.FLAG_KEY_MEDIA_NEXT; + controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_NEXT; } else { controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE; } @@ -1187,34 +1177,26 @@ public class PlaybackService extends Service { if (remoteControlClient != null) { switch (status) { case PLAYING: - remoteControlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); + remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); break; case PAUSED: case INITIALIZED: - remoteControlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED); + remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED); break; case STOPPED: - remoteControlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED); + remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED); break; case ERROR: - remoteControlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR); + remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR); break; default: - remoteControlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING); + remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING); } if (media != null) { - MetadataEditor editor = remoteControlClient - .editMetadata(false); - editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, - media.getEpisodeTitle()); + MetadataEditor editor = remoteControlClient.editMetadata(false); + editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, media.getEpisodeTitle()); - editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, - media.getFeedTitle()); + editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, media.getFeedTitle()); editor.apply(); } @@ -1226,28 +1208,28 @@ public class PlaybackService extends Service { private void bluetoothNotifyChange() { boolean isPlaying = false; - + if (status == PlayerStatus.PLAYING) { isPlaying = true; } - - Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED); - i.putExtra("id", 1); - i.putExtra("artist", ""); - i.putExtra("album", media.getFeedTitle()); - i.putExtra("track", media.getEpisodeTitle()); - i.putExtra("playing", isPlaying); - i.putExtra("ListSize", manager.getQueueSize(false)); - i.putExtra("duration", media.getDuration()); - i.putExtra("position", media.getPosition()); - sendBroadcast(i); - } - + + Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED); + i.putExtra("id", 1); + i.putExtra("artist", ""); + i.putExtra("album", media.getFeedTitle()); + i.putExtra("track", media.getEpisodeTitle()); + i.putExtra("playing", isPlaying); + i.putExtra("ListSize", manager.getQueueSize(false)); + i.putExtra("duration", media.getDuration()); + i.putExtra("position", media.getPosition()); + sendBroadcast(i); + } + /** * Pauses playback when the headset is disconnected and the preference is * set */ - private BroadcastReceiver headsetDisconnected = new BroadcastReceiver() { + private final BroadcastReceiver headsetDisconnected = new BroadcastReceiver() { private static final String TAG = "headsetDisconnected"; 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 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. */ private void pauseIfPauseOnDisconnect() { - if (UserPreferences.isPauseOnHeadsetDisconnect() - && status == PlayerStatus.PLAYING) { + if (UserPreferences.isPauseOnHeadsetDisconnect() && status == PlayerStatus.PLAYING) { pause(true, true); } } - private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() { + private final BroadcastReceiver shutdownReceiver = new BroadcastReceiver() { @Override 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 public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_SKIP_CURRENT_EPISODE)) { @@ -1329,8 +1310,7 @@ public class PlaybackService extends Service { try { saveCurrentPosition(); } catch (IllegalStateException e) { - Log.w(TAG, - "saveCurrentPosition was called in illegal state"); + Log.w(TAG, "saveCurrentPosition was called in illegal state"); } } } @@ -1419,7 +1399,7 @@ public class PlaybackService extends Service { return media; } - public MediaPlayer getPlayer() { + public IPlayer getPlayer() { return player; } @@ -1432,6 +1412,38 @@ public class PlaybackService extends Service { 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 * 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) { - SharedPreferences.Editor editor = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()).edit(); + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, id); editor.commit(); } diff --git a/src/de/danoeh/antennapod/util/DuckType.java b/src/de/danoeh/antennapod/util/DuckType.java new file mode 100644 index 000000000..0dfc01508 --- /dev/null +++ b/src/de/danoeh/antennapod/util/DuckType.java @@ -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 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 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; + } + } + +} \ No newline at end of file diff --git a/src/de/danoeh/antennapod/util/playback/AudioPlayer.java b/src/de/danoeh/antennapod/util/playback/AudioPlayer.java new file mode 100644 index 000000000..68d31324d --- /dev/null +++ b/src/de/danoeh/antennapod/util/playback/AudioPlayer.java @@ -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"); + } + } +} diff --git a/src/de/danoeh/antennapod/util/playback/IPlayer.java b/src/de/danoeh/antennapod/util/playback/IPlayer.java new file mode 100644 index 000000000..ca9b36358 --- /dev/null +++ b/src/de/danoeh/antennapod/util/playback/IPlayer.java @@ -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(); +} diff --git a/src/de/danoeh/antennapod/util/playback/VideoPlayer.java b/src/de/danoeh/antennapod/util/playback/VideoPlayer.java new file mode 100644 index 000000000..f0a50542c --- /dev/null +++ b/src/de/danoeh/antennapod/util/playback/VideoPlayer.java @@ -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"); + } +} -- cgit v1.2.3 From b2d8423543165bf086ad7da49d4196d8ff550cee Mon Sep 17 00:00:00 2001 From: James Falcon Date: Wed, 1 May 2013 22:41:51 -0500 Subject: Updating formatting to match remote --- .../danoeh/antennapod/service/PlaybackService.java | 313 ++++++++++++++------- 1 file changed, 211 insertions(+), 102 deletions(-) (limited to 'src') diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index 545d3e555..1d6d1448c 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -219,31 +219,39 @@ public class PlaybackService extends Service { status = PlayerStatus.STOPPED; audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); manager = FeedManager.getInstance(); - schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE, new ThreadFactory() { + schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE, + new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setPriority(Thread.MIN_PRIORITY); - return t; - } - }, new RejectedExecutionHandler() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + }, new RejectedExecutionHandler() { - @Override - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { - Log.w(TAG, "SchedEx rejected submission of new task"); - } - }); + @Override + public void rejectedExecution(Runnable r, + ThreadPoolExecutor executor) { + Log.w(TAG, "SchedEx rejected submission of new task"); + } + }); - mediaButtonReceiver = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName()); + mediaButtonReceiver = new ComponentName(getPackageName(), + MediaButtonReceiver.class.getName()); audioManager.registerMediaButtonEventReceiver(mediaButtonReceiver); if (android.os.Build.VERSION.SDK_INT >= 14) { - audioManager.registerRemoteControlClient(setupRemoteControlClient()); + audioManager + .registerRemoteControlClient(setupRemoteControlClient()); } - registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); - registerReceiver(shutdownReceiver, new IntentFilter(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); - registerReceiver(audioBecomingNoisy, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); - registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(ACTION_SKIP_CURRENT_EPISODE)); + registerReceiver(headsetDisconnected, new IntentFilter( + Intent.ACTION_HEADSET_PLUG)); + registerReceiver(shutdownReceiver, new IntentFilter( + ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + registerReceiver(audioBecomingNoisy, new IntentFilter( + AudioManager.ACTION_AUDIO_BECOMING_NOISY)); + registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter( + ACTION_SKIP_CURRENT_EPISODE)); } @@ -261,17 +269,23 @@ public class PlaybackService extends Service { if (mp != null && media != null) { if (media.getMediaType() == MediaType.AUDIO) { ((AudioPlayer) mp).setOnPreparedListener(audioPreparedListener); - ((AudioPlayer) mp).setOnCompletionListener(audioCompletionListener); - ((AudioPlayer) mp).setOnSeekCompleteListener(audioSeekCompleteListener); + ((AudioPlayer) mp) + .setOnCompletionListener(audioCompletionListener); + ((AudioPlayer) mp) + .setOnSeekCompleteListener(audioSeekCompleteListener); ((AudioPlayer) mp).setOnErrorListener(audioErrorListener); - ((AudioPlayer) mp).setOnBufferingUpdateListener(audioBufferingUpdateListener); + ((AudioPlayer) mp) + .setOnBufferingUpdateListener(audioBufferingUpdateListener); ((AudioPlayer) mp).setOnInfoListener(audioInfoListener); } else { ((VideoPlayer) mp).setOnPreparedListener(videoPreparedListener); - ((VideoPlayer) mp).setOnCompletionListener(videoCompletionListener); - ((VideoPlayer) mp).setOnSeekCompleteListener(videoSeekCompleteListener); + ((VideoPlayer) mp) + .setOnCompletionListener(videoCompletionListener); + ((VideoPlayer) mp) + .setOnSeekCompleteListener(videoSeekCompleteListener); ((VideoPlayer) mp).setOnErrorListener(videoErrorListener); - ((VideoPlayer) mp).setOnBufferingUpdateListener(videoBufferingUpdateListener); + ((VideoPlayer) mp) + .setOnBufferingUpdateListener(videoBufferingUpdateListener); ((VideoPlayer) mp).setOnInfoListener(videoInfoListener); } } @@ -325,7 +339,8 @@ public class PlaybackService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "Gained audio focus"); if (pausedBecauseOfTransientAudiofocusLoss) { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0); + audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_RAISE, 0); play(); } break; @@ -333,7 +348,8 @@ public class PlaybackService extends Service { if (status == PlayerStatus.PLAYING) { if (AppConfig.DEBUG) Log.d(TAG, "Lost audio focus temporarily. Ducking..."); - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0); + audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_LOWER, 0); pausedBecauseOfTransientAudiofocusLoss = true; } break; @@ -362,7 +378,8 @@ public class PlaybackService extends Service { } else { Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE); - boolean playbackType = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, true); + boolean playbackType = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, + true); if (playable == null) { Log.e(TAG, "Playable extra wasn't sent to the service"); if (media == null) { @@ -370,17 +387,21 @@ public class PlaybackService extends Service { } // Intent values appear to be valid // check if already playing and playbackType is the same - } else if (media == null || playable != media || playbackType != shouldStream) { + } else if (media == null || playable != media + || playbackType != shouldStream) { pause(true, false); sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0); - if (media == null || playable.getIdentifier() != media.getIdentifier()) { + if (media == null + || playable.getIdentifier() != media.getIdentifier()) { media = playable; } if (media != null) { shouldStream = playbackType; - startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false); - prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false); + startWhenPrepared = intent.getBooleanExtra( + EXTRA_START_WHEN_PREPARED, false); + prepareImmediately = intent.getBooleanExtra( + EXTRA_PREPARE_IMMEDIATELY, false); initMediaplayer(); } else { @@ -444,7 +465,8 @@ public class PlaybackService extends Service { Log.d(TAG, "Setting display"); player.setDisplay(null); player.setDisplay(sh); - if (status == PlayerStatus.STOPPED || status == PlayerStatus.AWAITING_VIDEO_SURFACE) { + if (status == PlayerStatus.STOPPED + || status == PlayerStatus.AWAITING_VIDEO_SURFACE) { try { InitTask initTask = new InitTask() { @@ -454,11 +476,13 @@ public class PlaybackService extends Service { if (result != null) { try { if (shouldStream) { - player.setDataSource(media.getStreamUrl()); + player.setDataSource(media + .getStreamUrl()); setStatus(PlayerStatus.PREPARING); player.prepareAsync(); } else { - player.setDataSource(media.getLocalMediaUrl()); + player.setDataSource(media + .getLocalMediaUrl()); setStatus(PlayerStatus.PREPARING); player.prepareAsync(); } @@ -467,7 +491,8 @@ public class PlaybackService extends Service { } } else { setStatus(PlayerStatus.ERROR); - sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + sendBroadcast(new Intent( + ACTION_SHUTDOWN_PLAYBACK_SERVICE)); } } } @@ -534,9 +559,11 @@ public class PlaybackService extends Service { playingVideo = false; try { if (shouldStream) { - player.setDataSource(media.getStreamUrl()); + player.setDataSource(media + .getStreamUrl()); } else if (media.localFileAvailable()) { - player.setDataSource(media.getLocalMediaUrl()); + player.setDataSource(media + .getLocalMediaUrl()); } if (prepareImmediately) { @@ -549,17 +576,20 @@ public class PlaybackService extends Service { e.printStackTrace(); media = null; setStatus(PlayerStatus.ERROR); - sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + sendBroadcast(new Intent( + ACTION_SHUTDOWN_PLAYBACK_SERVICE)); } } else { Log.e(TAG, "InitTask could not load metadata"); media = null; setStatus(PlayerStatus.ERROR); - sendBroadcast(new Intent(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + sendBroadcast(new Intent( + ACTION_SHUTDOWN_PLAYBACK_SERVICE)); } } else { if (AppConfig.DEBUG) - Log.d(TAG, "Status of player has changed during initialization. Stopping init process."); + Log.d(TAG, + "Status of player has changed during initialization. Stopping init process."); } } @@ -588,11 +618,14 @@ public class PlaybackService extends Service { } private void setupPositionSaver() { - if (positionSaverFuture == null || (positionSaverFuture.isCancelled() || positionSaverFuture.isDone())) { + if (positionSaverFuture == null + || (positionSaverFuture.isCancelled() || positionSaverFuture + .isDone())) { positionSaver = new PositionSaver(); - positionSaverFuture = schedExecutor.scheduleAtFixedRate(positionSaver, PositionSaver.WAITING_INTERVALL, PositionSaver.WAITING_INTERVALL, - TimeUnit.MILLISECONDS); + positionSaverFuture = schedExecutor.scheduleAtFixedRate( + positionSaver, PositionSaver.WAITING_INTERVALL, + PositionSaver.WAITING_INTERVALL, TimeUnit.MILLISECONDS); } } @@ -676,7 +709,8 @@ public class PlaybackService extends Service { private final com.aocate.media.MediaPlayer.OnInfoListener audioInfoListener = new com.aocate.media.MediaPlayer.OnInfoListener() { @Override - public boolean onInfo(com.aocate.media.MediaPlayer mp, int what, int extra) { + public boolean onInfo(com.aocate.media.MediaPlayer mp, int what, + int extra) { return genericInfoListener(what); } }; @@ -703,7 +737,8 @@ public class PlaybackService extends Service { private final com.aocate.media.MediaPlayer.OnErrorListener audioErrorListener = new com.aocate.media.MediaPlayer.OnErrorListener() { @Override - public boolean onError(com.aocate.media.MediaPlayer mp, int what, int extra) { + public boolean onError(com.aocate.media.MediaPlayer mp, int what, + int extra) { return genericOnError(mp, what, extra); } }; @@ -748,7 +783,8 @@ public class PlaybackService extends Service { 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) { + public void onBufferingUpdate(com.aocate.media.MediaPlayer mp, + int percent) { genericOnBufferingUpdate(percent); } }; @@ -780,7 +816,8 @@ public class PlaybackService extends Service { ((FeedMedia) media).setPlaybackCompletionDate(new Date()); manager.markItemRead(PlaybackService.this, item, true, true); nextItem = manager.getQueueSuccessorOfItem(item); - isInQueue = media instanceof FeedMedia && manager.isInQueue(((FeedMedia) media).getItem()); + isInQueue = media instanceof FeedMedia + && manager.isInQueue(((FeedMedia) media).getItem()); if (isInQueue) { manager.removeQueueItem(PlaybackService.this, item, true); } @@ -796,7 +833,8 @@ public class PlaybackService extends Service { // is an episode in the queue left. // Start playback immediately if continuous playback is enabled boolean loadNextItem = isInQueue && nextItem != null; - playNextEpisode = playNextEpisode && loadNextItem && UserPreferences.isFollowQueue(); + playNextEpisode = playNextEpisode && loadNextItem + && UserPreferences.isFollowQueue(); if (loadNextItem) { if (AppConfig.DEBUG) Log.d(TAG, "Loading next item in queue"); @@ -830,7 +868,8 @@ public class PlaybackService extends Service { if (media != null) { resetVideoSurface(); refreshRemoteControlClientState(); - sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, notificationCode); + sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, + notificationCode); } else { sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0); stopSelf(); @@ -839,7 +878,8 @@ public class PlaybackService extends Service { public void setSleepTimer(long waitingTime) { if (AppConfig.DEBUG) - Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds"); + Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + + " milliseconds"); if (sleepTimerFuture != null) { sleepTimerFuture.cancel(true); } @@ -892,7 +932,9 @@ public class PlaybackService extends Service { public void stop() { if (AppConfig.DEBUG) Log.d(TAG, "Stopping playback"); - if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED || status == PlayerStatus.STOPPED || status == PlayerStatus.PLAYING) { + if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED + || status == PlayerStatus.STOPPED + || status == PlayerStatus.PLAYING) { player.stop(); } setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING); @@ -921,8 +963,11 @@ public class PlaybackService extends Service { @SuppressLint("NewApi") public void play() { - if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED || status == PlayerStatus.STOPPED) { - int focusGained = audioManager.requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); + if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED + || status == PlayerStatus.STOPPED) { + int focusGained = audioManager.requestAudioFocus( + audioFocusChangeListener, AudioManager.STREAM_MUSIC, + AudioManager.AUDIOFOCUS_GAIN); if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { if (AppConfig.DEBUG) @@ -941,9 +986,11 @@ public class PlaybackService extends Service { setupNotification(); pausedBecauseOfTransientAudiofocusLoss = false; if (android.os.Build.VERSION.SDK_INT >= 14) { - audioManager.registerRemoteControlClient(remoteControlClient); + audioManager + .registerRemoteControlClient(remoteControlClient); } - audioManager.registerMediaButtonEventReceiver(mediaButtonReceiver); + audioManager + .registerMediaButtonEventReceiver(mediaButtonReceiver); media.onPlaybackStart(); } else { if (AppConfig.DEBUG) @@ -956,24 +1003,42 @@ public class PlaybackService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "Writing playback preferences"); - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); + SharedPreferences.Editor editor = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()).edit(); if (media != null) { - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, media.getPlayableType()); - editor.putBoolean(PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM, shouldStream); - editor.putBoolean(PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO, playingVideo); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, + media.getPlayableType()); + editor.putBoolean( + PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM, + shouldStream); + editor.putBoolean( + PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO, + playingVideo); if (media instanceof FeedMedia) { FeedMedia fMedia = (FeedMedia) media; - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, fMedia.getItem().getFeed().getId()); - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, fMedia.getId()); + editor.putLong( + PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, + fMedia.getItem().getFeed().getId()); + editor.putLong( + PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, + fMedia.getId()); } else { - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong( + PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, + PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong( + PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, + PlaybackPreferences.NO_MEDIA_PLAYING); } media.writeToPreferences(editor); } else { - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, + PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, + PlaybackPreferences.NO_MEDIA_PLAYING); + editor.putLong( + PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, + PlaybackPreferences.NO_MEDIA_PLAYING); } editor.commit(); @@ -1007,7 +1072,8 @@ public class PlaybackService extends Service { /** Prepares notification and starts the service in the foreground. */ @SuppressLint("NewApi") private void setupNotification() { - final PendingIntent pIntent = PendingIntent.getActivity(this, 0, PlaybackService.getPlayerActivityIntent(this), + final PendingIntent pIntent = PendingIntent.getActivity(this, 0, + PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT); if (notificationSetupTask != null) { @@ -1022,13 +1088,17 @@ public class PlaybackService extends Service { Log.d(TAG, "Starting background work"); if (android.os.Build.VERSION.SDK_INT >= 11) { if (media != null && media != null) { - int iconSize = getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width); - icon = BitmapDecoder.decodeBitmapFromWorkerTaskResource(iconSize, media); + int iconSize = getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_width); + icon = BitmapDecoder + .decodeBitmapFromWorkerTaskResource(iconSize, + media); } } if (icon == null) { - icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stat_antenna); + icon = BitmapFactory.decodeResource(getResources(), + R.drawable.ic_stat_antenna); } return null; @@ -1037,24 +1107,40 @@ public class PlaybackService extends Service { @Override protected void onPostExecute(Void result) { super.onPostExecute(result); - if (!isCancelled() && status == PlayerStatus.PLAYING && media != null) { + if (!isCancelled() && status == PlayerStatus.PLAYING + && media != null) { String contentText = media.getFeedTitle(); String contentTitle = media.getEpisodeTitle(); Notification notification = null; if (android.os.Build.VERSION.SDK_INT >= 16) { - Intent pauseButtonIntent = new Intent(PlaybackService.this, PlaybackService.class); - pauseButtonIntent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, KeyEvent.KEYCODE_MEDIA_PAUSE); - PendingIntent pauseButtonPendingIntent = PendingIntent.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) + Intent pauseButtonIntent = new Intent( + PlaybackService.this, PlaybackService.class); + pauseButtonIntent.putExtra( + MediaButtonReceiver.EXTRA_KEYCODE, + KeyEvent.KEYCODE_MEDIA_PAUSE); + PendingIntent pauseButtonPendingIntent = PendingIntent + .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) - .addAction(android.R.drawable.ic_media_pause, getString(R.string.pause_label), pauseButtonPendingIntent); + .addAction(android.R.drawable.ic_media_pause, + getString(R.string.pause_label), + pauseButtonPendingIntent); notification = notificationBuilder.build(); } else { - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(PlaybackService.this) - .setContentTitle(contentTitle).setContentText(contentText).setOngoing(true).setContentIntent(pIntent) - .setLargeIcon(icon).setSmallIcon(R.drawable.ic_stat_antenna); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder( + PlaybackService.this) + .setContentTitle(contentTitle) + .setContentText(contentText).setOngoing(true) + .setContentIntent(pIntent).setLargeIcon(icon) + .setSmallIcon(R.drawable.ic_stat_antenna); notification = notificationBuilder.getNotification(); } startForeground(NOTIFICATION_ID, notification); @@ -1065,7 +1151,8 @@ public class PlaybackService extends Service { }; if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - notificationSetupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + notificationSetupTask + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { notificationSetupTask.execute(); } @@ -1087,7 +1174,9 @@ public class PlaybackService extends Service { public void seek(int i) { saveCurrentPosition(); - if (status == PlayerStatus.INITIALIZED || status == PlayerStatus.INITIALIZING || status == PlayerStatus.PREPARING) { + if (status == PlayerStatus.INITIALIZED + || status == PlayerStatus.INITIALIZING + || status == PlayerStatus.PREPARING) { media.setPosition(i); setStartWhenPrepared(true); prepare(); @@ -1114,7 +1203,9 @@ public class PlaybackService extends Service { if (position != INVALID_TIME) { if (AppConfig.DEBUG) Log.d(TAG, "Saving current position to " + position); - media.saveCurrentPosition(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()), position); + media.saveCurrentPosition(PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()), + position); } } @@ -1129,17 +1220,22 @@ public class PlaybackService extends Service { @SuppressLint("NewApi") private void setupWidgetUpdater() { - if (widgetUpdaterFuture == null || (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture.isDone())) { + if (widgetUpdaterFuture == null + || (widgetUpdaterFuture.isCancelled() || widgetUpdaterFuture + .isDone())) { widgetUpdater = new WidgetUpdateWorker(); - widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate(widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL, - WidgetUpdateWorker.NOTIFICATION_INTERVALL, TimeUnit.MILLISECONDS); + widgetUpdaterFuture = schedExecutor.scheduleAtFixedRate( + widgetUpdater, WidgetUpdateWorker.NOTIFICATION_INTERVALL, + WidgetUpdateWorker.NOTIFICATION_INTERVALL, + TimeUnit.MILLISECONDS); } } private void updateWidget() { if (AppConfig.DEBUG) Log.d(TAG, "Sending widget update request"); - PlaybackService.this.sendBroadcast(new Intent(PlayerWidget.FORCE_WIDGET_UPDATE)); + PlaybackService.this.sendBroadcast(new Intent( + PlayerWidget.FORCE_WIDGET_UPDATE)); } public boolean sleepTimerActive() { @@ -1158,11 +1254,13 @@ public class PlaybackService extends Service { private RemoteControlClient setupRemoteControlClient() { Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); mediaButtonIntent.setComponent(mediaButtonReceiver); - PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); + PendingIntent mediaPendingIntent = PendingIntent.getBroadcast( + getApplicationContext(), 0, mediaButtonIntent, 0); remoteControlClient = new RemoteControlClient(mediaPendingIntent); int controlFlags; if (android.os.Build.VERSION.SDK_INT < 16) { - controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_NEXT; + controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE + | RemoteControlClient.FLAG_KEY_MEDIA_NEXT; } else { controlFlags = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE; } @@ -1177,26 +1275,34 @@ public class PlaybackService extends Service { if (remoteControlClient != null) { switch (status) { case PLAYING: - remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); + remoteControlClient + .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); break; case PAUSED: case INITIALIZED: - remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED); + remoteControlClient + .setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED); break; case STOPPED: - remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED); + remoteControlClient + .setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED); break; case ERROR: - remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR); + remoteControlClient + .setPlaybackState(RemoteControlClient.PLAYSTATE_ERROR); break; default: - remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING); + remoteControlClient + .setPlaybackState(RemoteControlClient.PLAYSTATE_BUFFERING); } if (media != null) { - MetadataEditor editor = remoteControlClient.editMetadata(false); - editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, media.getEpisodeTitle()); + MetadataEditor editor = remoteControlClient + .editMetadata(false); + editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, + media.getEpisodeTitle()); - editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, media.getFeedTitle()); + editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, + media.getFeedTitle()); editor.apply(); } @@ -1266,7 +1372,8 @@ public class PlaybackService extends Service { /** Pauses playback if PREF_PAUSE_ON_HEADSET_DISCONNECT was set to true. */ private void pauseIfPauseOnDisconnect() { - if (UserPreferences.isPauseOnHeadsetDisconnect() && status == PlayerStatus.PLAYING) { + if (UserPreferences.isPauseOnHeadsetDisconnect() + && status == PlayerStatus.PLAYING) { pause(true, true); } } @@ -1310,7 +1417,8 @@ public class PlaybackService extends Service { try { saveCurrentPosition(); } catch (IllegalStateException e) { - Log.w(TAG, "saveCurrentPosition was called in illegal state"); + Log.w(TAG, + "saveCurrentPosition was called in illegal state"); } } } @@ -1492,7 +1600,8 @@ public class PlaybackService extends Service { } private void setCurrentlyPlayingMedia(long id) { - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); + SharedPreferences.Editor editor = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()).edit(); editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, id); editor.commit(); } -- cgit v1.2.3 From e65c1b7322327754d2f0cfc46138fe7fdab79693 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Thu, 2 May 2013 20:57:10 +0200 Subject: Added methods for controlling playback speed to playback controller --- src/de/danoeh/antennapod/service/PlaybackService.java | 10 ++++++++++ .../antennapod/util/playback/PlaybackController.java | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'src') diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index 56bdce375..421aae85e 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -1553,6 +1553,16 @@ public class PlaybackService extends Service { } } } + + public double getCurrentPlaybackSpeed() { + if (media.getMediaType() == MediaType.AUDIO && player instanceof AudioPlayer) { + AudioPlayer audioPlayer = (AudioPlayer) player; + if (audioPlayer.canSetSpeed()) { + return audioPlayer.getCurrentSpeedMultiplier(); + } + } + return -1; + } /** * call getDuration() on mediaplayer or return INVALID_TIME if player is in diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java index cebb11cf0..b80cb54bc 100644 --- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java +++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java @@ -654,6 +654,24 @@ public abstract class PlaybackController { return false; } + public boolean canSetPlaybackSpeed() { + return playbackService != null && playbackService.canSetSpeed(); + } + + public void setPlaybackSpeed(double speed) { + if (playbackService != null) { + playbackService.setSpeed(speed); + } + } + + public double getCurrentPlaybackSpeedMultiplier() { + if (canSetPlaybackSpeed()) { + return playbackService.getCurrentPlaybackSpeed(); + } else { + return -1; + } + } + /** * Returns true if PlaybackController can communicate with the playback * service. -- cgit v1.2.3 From 62d7a927ad6a53b4ffa58bd8fef6368d698c3938 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Fri, 3 May 2013 18:31:39 +0200 Subject: Added buttons to control playback speed --- .../antennapod/activity/AudioplayerActivity.java | 51 +++++++++++++++++++++- .../antennapod/activity/MediaplayerActivity.java | 9 ++++ .../fragment/ExternalPlayerFragment.java | 6 +++ .../danoeh/antennapod/service/PlaybackService.java | 10 ++++- .../util/playback/PlaybackController.java | 8 +++- 5 files changed, 78 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index b73a17125..f43f29db3 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -11,6 +11,7 @@ import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; +import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView.ScaleType; import android.widget.ListView; @@ -57,6 +58,7 @@ public class AudioplayerActivity extends MediaplayerActivity { private TextView txtvTitle; private TextView txtvFeed; + private Button butPlaybackSpeed; private ImageButton butNavLeft; private ImageButton butNavRight; @@ -219,7 +221,7 @@ public class AudioplayerActivity extends MediaplayerActivity { if (savedPosition != -1) { switchToFragment(savedPosition); } - + } @Override @@ -365,6 +367,7 @@ public class AudioplayerActivity extends MediaplayerActivity { txtvFeed = (TextView) findViewById(R.id.txtvFeed); butNavLeft = (ImageButton) findViewById(R.id.butNavLeft); butNavRight = (ImageButton) findViewById(R.id.butNavRight); + butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); butNavLeft.setOnClickListener(new OnClickListener() { @@ -392,6 +395,50 @@ public class AudioplayerActivity extends MediaplayerActivity { } } }); + + butPlaybackSpeed.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + final double PLAYBACK_SPEED_STEP = 0.5; + final double PLAYBACK_SPEED_MAX = 2.0; + final double PLAYBACK_SPEED_DEFAULT = 1.0; + + if (controller != null && controller.canSetPlaybackSpeed()) { + double currentPlaybackSpeed = controller + .getCurrentPlaybackSpeedMultiplier(); + if (currentPlaybackSpeed != -1) { + if (currentPlaybackSpeed >= PLAYBACK_SPEED_MAX) { + controller.setPlaybackSpeed(PLAYBACK_SPEED_DEFAULT); + } else { + controller.setPlaybackSpeed(currentPlaybackSpeed + + PLAYBACK_SPEED_STEP); + } + } else { + controller.setPlaybackSpeed(PLAYBACK_SPEED_DEFAULT); + } + + } + } + }); + } + + @Override + protected void onPlaybackSpeedChange() { + super.onPlaybackSpeedChange(); + updateButPlaybackSpeed(); + } + + private void updateButPlaybackSpeed() { + double playbackSpeed; + if (controller == null + || (playbackSpeed = controller + .getCurrentPlaybackSpeedMultiplier()) == -1) { + butPlaybackSpeed.setVisibility(View.GONE); + } else { + butPlaybackSpeed.setVisibility(View.VISIBLE); + butPlaybackSpeed.setText(String.format("%.1fx", playbackSpeed)); + } } @Override @@ -423,7 +470,7 @@ public class AudioplayerActivity extends MediaplayerActivity { ((AudioplayerContentFragment) currentlyShownFragment) .onDataSetChanged(media); } - + updateButPlaybackSpeed(); } public void notifyMediaPositionChanged() { diff --git a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java index 16b03809a..9fa9fbf52 100644 --- a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -132,10 +132,19 @@ public abstract class MediaplayerActivity extends SherlockFragmentActivity public void onPlaybackEnd() { finish(); } + + @Override + public void onPlaybackSpeedChange() { + MediaplayerActivity.this.onPlaybackSpeedChange(); + } }; } + protected void onPlaybackSpeedChange() { + + } + protected void onServiceQueried() { supportInvalidateOptionsMenu(); } diff --git a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index a50e820b6..e46017328 100644 --- a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -174,6 +174,12 @@ public class ExternalPlayerFragment extends SherlockFragment { .newOnPlayButtonClickListener()); } } + + @Override + public void onPlaybackSpeedChange() { + // TODO Auto-generated method stub + + } }; } diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index 421aae85e..bf90cf9c9 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -109,6 +109,7 @@ public class PlaybackService extends Service { public static final int NOTIFICATION_TYPE_BUFFER_END = 6; /** No more episodes are going to be played. */ public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7; + public static final int NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE = 8; /** * Returned by getPositionSafe() or getDurationSafe() if the playbackService @@ -1541,6 +1542,10 @@ public class PlaybackService extends Service { AudioPlayer audioPlayer = (AudioPlayer) player; if (audioPlayer.canSetSpeed()) { audioPlayer.setPlaybackSpeed((float) speed); + if (AppConfig.DEBUG) + Log.d(TAG, "Playback speed was set to " + speed); + sendNotificationBroadcast( + NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE, 0); } } } @@ -1553,9 +1558,10 @@ public class PlaybackService extends Service { } } } - + public double getCurrentPlaybackSpeed() { - if (media.getMediaType() == MediaType.AUDIO && player instanceof AudioPlayer) { + if (media.getMediaType() == MediaType.AUDIO + && player instanceof AudioPlayer) { AudioPlayer audioPlayer = (AudioPlayer) player; if (audioPlayer.canSetSpeed()) { return audioPlayer.getCurrentSpeedMultiplier(); diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java index b80cb54bc..0060c756f 100644 --- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java +++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java @@ -327,6 +327,8 @@ public abstract class PlaybackController { break; case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END: onPlaybackEnd(); + case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE: + onPlaybackSpeedChange(); break; } @@ -354,6 +356,8 @@ public abstract class PlaybackController { } } }; + + public abstract void onPlaybackSpeedChange(); public abstract void onShutdownNotification(); @@ -657,13 +661,13 @@ public abstract class PlaybackController { public boolean canSetPlaybackSpeed() { return playbackService != null && playbackService.canSetSpeed(); } - + public void setPlaybackSpeed(double speed) { if (playbackService != null) { playbackService.setSpeed(speed); } } - + public double getCurrentPlaybackSpeedMultiplier() { if (canSetPlaybackSpeed()) { return playbackService.getCurrentPlaybackSpeed(); -- cgit v1.2.3 From 5b58466fd21c8bc97fe00698369228ea96566c33 Mon Sep 17 00:00:00 2001 From: James Falcon Date: Fri, 3 May 2013 16:25:20 -0500 Subject: Added variable speed playback control as a global option. --- .../antennapod/activity/PreferenceActivity.java | 46 ++++++++++++++++++++++ .../antennapod/dialog/GetSpeedPlaybackPlugin.java | 31 +++++++++++++++ .../antennapod/preferences/UserPreferences.java | 25 +++++++++++- .../danoeh/antennapod/service/PlaybackService.java | 18 +++++++-- 4 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java (limited to 'src') diff --git a/src/de/danoeh/antennapod/activity/PreferenceActivity.java b/src/de/danoeh/antennapod/activity/PreferenceActivity.java index c59b14c03..a8b11bcbc 100644 --- a/src/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/src/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -5,7 +5,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources.Theme; import android.net.wifi.WifiConfiguration; @@ -26,6 +28,7 @@ import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.asynctask.FlattrClickWorker; import de.danoeh.antennapod.asynctask.OpmlExportWorker; +import de.danoeh.antennapod.dialog.GetSpeedPlaybackPlugin; import de.danoeh.antennapod.feed.FeedManager; import de.danoeh.antennapod.preferences.UserPreferences; import de.danoeh.antennapod.util.flattr.FlattrUtils; @@ -41,6 +44,7 @@ public class PreferenceActivity extends SherlockPreferenceActivity { private static final String PREF_ABOUT = "prefAbout"; private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir"; private static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings"; + private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher"; private CheckBoxPreference[] selectedNetworks; @@ -166,6 +170,14 @@ public class PreferenceActivity extends SherlockPreferenceActivity { return true; } }); + findPreference(PREF_PLAYBACK_SPEED_LAUNCHER) + .setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + setPlaybackSpeed(); + return true; + } + }); buildAutodownloadSelectedNetworsPreference(); setSelectedNetworksEnabled(UserPreferences @@ -224,6 +236,40 @@ public class PreferenceActivity extends SherlockPreferenceActivity { } } + private void setPlaybackSpeed() { + if (com.aocate.media.MediaPlayer + .isPrestoLibraryInstalled(PreferenceActivity.this)) { + int currentIndex = 0; + final String[] speedValues = getResources().getStringArray( + R.array.playback_speed_values); + for (int i = 0; i < speedValues.length; i++) { + // Probably shouldn't float compare here... + if (Float.parseFloat(speedValues[i]) == UserPreferences + .getPlaybackSpeed()) { + currentIndex = i; + } + } + AlertDialog.Builder builder = new AlertDialog.Builder( + PreferenceActivity.this); + builder.setTitle(R.string.set_playback_speed_label); + builder.setSingleChoiceItems(R.array.playback_speed_values, + currentIndex, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + UserPreferences.setPlaybackSpeed( + PreferenceActivity.this, + Float.valueOf(speedValues[which])); + dialog.dismiss(); + + } + }); + builder.create().show(); + + } else { + GetSpeedPlaybackPlugin.showDialog(this); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { return true; diff --git a/src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java b/src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java new file mode 100644 index 000000000..f4aabfdeb --- /dev/null +++ b/src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java @@ -0,0 +1,31 @@ +package de.danoeh.antennapod.dialog; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import de.danoeh.antennapod.R; + +public class GetSpeedPlaybackPlugin { + private GetSpeedPlaybackPlugin() { + } + + public static void showDialog(final Context context) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.no_playback_plugin_title); + builder.setMessage(R.string.no_playback_plugin_msg); + builder.setNegativeButton(R.string.close_label, null); + builder.setPositiveButton(R.string.download_plugin_label, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent playStoreIntent = new Intent( + Intent.ACTION_VIEW, + Uri.parse("market://details?id=com.falconware.prestissimo")); + context.startActivity(playStoreIntent); + } + }); + builder.create().show(); + } +} diff --git a/src/de/danoeh/antennapod/preferences/UserPreferences.java b/src/de/danoeh/antennapod/preferences/UserPreferences.java index f2f35f41d..0674849db 100644 --- a/src/de/danoeh/antennapod/preferences/UserPreferences.java +++ b/src/de/danoeh/antennapod/preferences/UserPreferences.java @@ -17,6 +17,7 @@ import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OpmlImportFromPathActivity; import de.danoeh.antennapod.receiver.FeedUpdateReceiver; +import de.danoeh.antennapod.service.PlaybackService; /** * Provides access to preferences set by the user in the settings screen. A @@ -41,11 +42,12 @@ public class UserPreferences implements public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter"; private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks"; public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize"; + private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed"; private static int EPISODE_CACHE_SIZE_UNLIMITED = -1; private static UserPreferences instance; - private Context context; + private final Context context; // Preferences private boolean pauseOnHeadsetDisconnect; @@ -60,6 +62,7 @@ public class UserPreferences implements private boolean enableAutodownloadWifiFilter; private String[] autodownloadSelectedNetworks; private int episodeCacheSize; + private float playbackSpeed; private UserPreferences(Context context) { this.context = context; @@ -108,6 +111,7 @@ public class UserPreferences implements episodeCacheSize = readEpisodeCacheSize(sp.getString( PREF_EPISODE_CACHE_SIZE, "20")); enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false); + playbackSpeed = sp.getFloat(PREF_PLAYBACK_SPEED, (float) 1.0); } private int readThemeValue(String valueFromPrefs) { @@ -196,6 +200,11 @@ public class UserPreferences implements return EPISODE_CACHE_SIZE_UNLIMITED; } + public static float getPlaybackSpeed() { + instanceAvailable(); + return instance.playbackSpeed; + } + /** * Returns the capacity of the episode cache. This method will return the * negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to @@ -250,9 +259,23 @@ public class UserPreferences implements PREF_EPISODE_CACHE_SIZE, "20")); } else if (key.equals(PREF_ENABLE_AUTODL)) { enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false); + } else if (key.equals(PREF_PLAYBACK_SPEED)) { + playbackSpeed = sp.getFloat(PREF_PLAYBACK_SPEED, (float) 1.0); } } + public static void setPlaybackSpeed(Context context, float speed) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context.getApplicationContext()); + SharedPreferences.Editor editor = prefs.edit(); + editor.putFloat(PREF_PLAYBACK_SPEED, speed); + editor.apply(); + + Intent intent = new Intent(context, PlaybackService.class); + intent.putExtra(PlaybackService.EXTRA_PLAYBACK_SPEED, speed); + context.startService(intent); + } + public static void setAutodownloadSelectedNetworks(Context context, String[] value) { SharedPreferences.Editor editor = PreferenceManager diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index 1d6d1448c..99d1afa7f 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -82,6 +82,7 @@ public class PlaybackService extends Service { 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_TYPE = "extra.de.danoeh.antennapod.service.notificationType"; + public static final String EXTRA_PLAYBACK_SPEED = "extra.de.danoeh.antennapod.service.playbackSpeed"; /** * If the PlaybackService receives this action, it will stop playback and @@ -371,10 +372,17 @@ public class PlaybackService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "OnStartCommand called"); int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1); + float playbackSpeed = intent.getFloatExtra(EXTRA_PLAYBACK_SPEED, -1); if (keycode != -1) { if (AppConfig.DEBUG) Log.d(TAG, "Received media button event"); handleKeycode(keycode); + } else if (playbackSpeed > 0) { + if (media == null) { + stopSelf(); + } else { + setSpeed(playbackSpeed); + } } else { Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE); @@ -419,6 +427,7 @@ public class PlaybackService extends Service { stopSelf(); } } + return Service.START_NOT_STICKY; } @@ -976,6 +985,7 @@ public class PlaybackService extends Service { Log.d(TAG, "Resuming/Starting playback"); writePlaybackPreferences(); + setSpeed(UserPreferences.getPlaybackSpeed()); player.start(); if (status != PlayerStatus.PAUSED) { player.seekTo(media.getPosition()); @@ -1534,20 +1544,20 @@ public class PlaybackService extends Service { return false; } - public void setSpeed(double speed) { + public void setSpeed(float speed) { if (media.getMediaType() == MediaType.AUDIO) { AudioPlayer audioPlayer = (AudioPlayer) player; if (audioPlayer.canSetSpeed()) { - audioPlayer.setPlaybackSpeed((float) speed); + audioPlayer.setPlaybackSpeed(speed); } } } - public void setPitch(double pitch) { + public void setPitch(float pitch) { if (media.getMediaType() == MediaType.AUDIO) { AudioPlayer audioPlayer = (AudioPlayer) player; if (audioPlayer.canSetPitch()) { - audioPlayer.setPlaybackPitch((float) pitch); + audioPlayer.setPlaybackPitch(pitch); } } } -- cgit v1.2.3 From 2db89cb807ae06b0f59039307e20c32cd9052b57 Mon Sep 17 00:00:00 2001 From: James Falcon Date: Sat, 4 May 2013 08:51:41 -0500 Subject: Bug fix for trying to set pitch when media is null. --- src/de/danoeh/antennapod/service/PlaybackService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index bf90cf9c9..6c7619476 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -1524,21 +1524,21 @@ public class PlaybackService extends Service { } public boolean canSetSpeed() { - if (media.getMediaType() == MediaType.AUDIO) { + if (media != null && media.getMediaType() == MediaType.AUDIO) { return ((AudioPlayer) player).canSetSpeed(); } return false; } public boolean canSetPitch() { - if (media.getMediaType() == MediaType.AUDIO) { + if (media != null && media.getMediaType() == MediaType.AUDIO) { return ((AudioPlayer) player).canSetPitch(); } return false; } public void setSpeed(double speed) { - if (media.getMediaType() == MediaType.AUDIO) { + if (media != null && media.getMediaType() == MediaType.AUDIO) { AudioPlayer audioPlayer = (AudioPlayer) player; if (audioPlayer.canSetSpeed()) { audioPlayer.setPlaybackSpeed((float) speed); @@ -1551,7 +1551,7 @@ public class PlaybackService extends Service { } public void setPitch(double pitch) { - if (media.getMediaType() == MediaType.AUDIO) { + if (media != null && media.getMediaType() == MediaType.AUDIO) { AudioPlayer audioPlayer = (AudioPlayer) player; if (audioPlayer.canSetPitch()) { audioPlayer.setPlaybackPitch((float) pitch); -- cgit v1.2.3 From 916074957ff66b08934cf262197b337c00bdbd67 Mon Sep 17 00:00:00 2001 From: James Falcon Date: Thu, 9 May 2013 16:02:15 -0500 Subject: Variable speed playback as user preferences and customization of playback speed. --- .../antennapod/activity/AudioplayerActivity.java | 55 +++++++++---- .../antennapod/activity/PreferenceActivity.java | 40 +-------- .../antennapod/dialog/GetSpeedPlaybackPlugin.java | 31 ------- .../antennapod/dialog/VariableSpeedDialog.java | 95 ++++++++++++++++++++++ .../antennapod/preferences/UserPreferences.java | 63 +++++++++++--- .../danoeh/antennapod/service/PlaybackService.java | 12 +-- .../util/playback/PlaybackController.java | 11 +-- 7 files changed, 195 insertions(+), 112 deletions(-) delete mode 100644 src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java create mode 100644 src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java (limited to 'src') diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index f43f29db3..a84e5b8f2 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -10,6 +10,7 @@ import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageButton; @@ -24,11 +25,13 @@ import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.ChapterListAdapter; import de.danoeh.antennapod.asynctask.ImageLoader; +import de.danoeh.antennapod.dialog.VariableSpeedDialog; import de.danoeh.antennapod.feed.Chapter; import de.danoeh.antennapod.feed.MediaType; import de.danoeh.antennapod.feed.SimpleChapter; import de.danoeh.antennapod.fragment.CoverFragment; import de.danoeh.antennapod.fragment.ItemDescriptionFragment; +import de.danoeh.antennapod.preferences.UserPreferences; import de.danoeh.antennapod.service.PlaybackService; import de.danoeh.antennapod.util.playback.ExternalMedia; import de.danoeh.antennapod.util.playback.Playable; @@ -400,27 +403,45 @@ public class AudioplayerActivity extends MediaplayerActivity { @Override public void onClick(View v) { - final double PLAYBACK_SPEED_STEP = 0.5; - final double PLAYBACK_SPEED_MAX = 2.0; - final double PLAYBACK_SPEED_DEFAULT = 1.0; - if (controller != null && controller.canSetPlaybackSpeed()) { - double currentPlaybackSpeed = controller - .getCurrentPlaybackSpeedMultiplier(); - if (currentPlaybackSpeed != -1) { - if (currentPlaybackSpeed >= PLAYBACK_SPEED_MAX) { - controller.setPlaybackSpeed(PLAYBACK_SPEED_DEFAULT); - } else { - controller.setPlaybackSpeed(currentPlaybackSpeed - + PLAYBACK_SPEED_STEP); - } + String[] availableSpeeds = UserPreferences + .getPlaybackSpeedArray(); + String currentSpeed = UserPreferences.getPlaybackSpeed(); + + // Provide initial value in case the speed list has changed + // out from under us + // and our current speed isn't in the new list + String newSpeed; + if (availableSpeeds.length > 0) { + newSpeed = availableSpeeds[0]; } else { - controller.setPlaybackSpeed(PLAYBACK_SPEED_DEFAULT); + newSpeed = "1.0"; } + for (int i = 0; i < availableSpeeds.length; i++) { + if (availableSpeeds[i].equals(currentSpeed)) { + if (i == availableSpeeds.length - 1) { + newSpeed = availableSpeeds[0]; + } else { + newSpeed = availableSpeeds[i + 1]; + } + break; + } + } + UserPreferences.setPlaybackSpeed(AudioplayerActivity.this, + newSpeed); + controller.setPlaybackSpeed(Float.parseFloat(newSpeed)); } } }); + + butPlaybackSpeed.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + VariableSpeedDialog.showDialog(AudioplayerActivity.this); + return true; + } + }); } @Override @@ -430,14 +451,12 @@ public class AudioplayerActivity extends MediaplayerActivity { } private void updateButPlaybackSpeed() { - double playbackSpeed; if (controller == null - || (playbackSpeed = controller - .getCurrentPlaybackSpeedMultiplier()) == -1) { + || (controller.getCurrentPlaybackSpeedMultiplier() == -1)) { butPlaybackSpeed.setVisibility(View.GONE); } else { butPlaybackSpeed.setVisibility(View.VISIBLE); - butPlaybackSpeed.setText(String.format("%.1fx", playbackSpeed)); + butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed()); } } diff --git a/src/de/danoeh/antennapod/activity/PreferenceActivity.java b/src/de/danoeh/antennapod/activity/PreferenceActivity.java index 21998ea73..373fbdf99 100644 --- a/src/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/src/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -5,9 +5,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources.Theme; import android.net.wifi.WifiConfiguration; @@ -29,7 +27,7 @@ import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.asynctask.FlattrClickWorker; import de.danoeh.antennapod.asynctask.OpmlExportWorker; -import de.danoeh.antennapod.dialog.GetSpeedPlaybackPlugin; +import de.danoeh.antennapod.dialog.VariableSpeedDialog; import de.danoeh.antennapod.feed.FeedManager; import de.danoeh.antennapod.preferences.UserPreferences; import de.danoeh.antennapod.util.flattr.FlattrUtils; @@ -175,7 +173,7 @@ public class PreferenceActivity extends SherlockPreferenceActivity { .setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - setPlaybackSpeed(); + VariableSpeedDialog.showDialog(PreferenceActivity.this); return true; } }); @@ -264,40 +262,6 @@ public class PreferenceActivity extends SherlockPreferenceActivity { } } - private void setPlaybackSpeed() { - if (com.aocate.media.MediaPlayer - .isPrestoLibraryInstalled(PreferenceActivity.this)) { - int currentIndex = 0; - final String[] speedValues = getResources().getStringArray( - R.array.playback_speed_values); - for (int i = 0; i < speedValues.length; i++) { - // Probably shouldn't float compare here... - if (Float.parseFloat(speedValues[i]) == UserPreferences - .getPlaybackSpeed()) { - currentIndex = i; - } - } - AlertDialog.Builder builder = new AlertDialog.Builder( - PreferenceActivity.this); - builder.setTitle(R.string.set_playback_speed_label); - builder.setSingleChoiceItems(R.array.playback_speed_values, - currentIndex, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - UserPreferences.setPlaybackSpeed( - PreferenceActivity.this, - Float.valueOf(speedValues[which])); - dialog.dismiss(); - - } - }); - builder.create().show(); - - } else { - GetSpeedPlaybackPlugin.showDialog(this); - } - } - @Override public boolean onCreateOptionsMenu(Menu menu) { return true; diff --git a/src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java b/src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java deleted file mode 100644 index f4aabfdeb..000000000 --- a/src/de/danoeh/antennapod/dialog/GetSpeedPlaybackPlugin.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.danoeh.antennapod.dialog; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import de.danoeh.antennapod.R; - -public class GetSpeedPlaybackPlugin { - private GetSpeedPlaybackPlugin() { - } - - public static void showDialog(final Context context) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.no_playback_plugin_title); - builder.setMessage(R.string.no_playback_plugin_msg); - builder.setNegativeButton(R.string.close_label, null); - builder.setPositiveButton(R.string.download_plugin_label, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Intent playStoreIntent = new Intent( - Intent.ACTION_VIEW, - Uri.parse("market://details?id=com.falconware.prestissimo")); - context.startActivity(playStoreIntent); - } - }); - builder.create().show(); - } -} diff --git a/src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java new file mode 100644 index 000000000..308235f95 --- /dev/null +++ b/src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -0,0 +1,95 @@ +package de.danoeh.antennapod.dialog; + +import java.util.Arrays; +import java.util.List; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.preferences.UserPreferences; + +public class VariableSpeedDialog { + private VariableSpeedDialog() { + } + + public static void showDialog(final Context context) { + if (com.aocate.media.MediaPlayer.isPrestoLibraryInstalled(context)) { + showSpeedSelectorDialog(context); + } else { + showGetPluginDialog(context); + } + } + + private static void showGetPluginDialog(final Context context) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.no_playback_plugin_title); + builder.setMessage(R.string.no_playback_plugin_msg); + builder.setNegativeButton(R.string.close_label, null); + builder.setPositiveButton(R.string.download_plugin_label, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent playStoreIntent = new Intent( + Intent.ACTION_VIEW, + Uri.parse("market://details?id=com.falconware.prestissimo")); + context.startActivity(playStoreIntent); + } + }); + builder.create().show(); + } + + private static void showSpeedSelectorDialog(final Context context) { + final String[] speedValues = context.getResources().getStringArray( + R.array.playback_speed_values); + // According to Java spec these get initialized to false on creation + final boolean[] speedChecked = new boolean[speedValues.length]; + + // Build the "isChecked" array so that multiChoice dialog is + // populated correctly + List selectedSpeedList = Arrays.asList(UserPreferences + .getPlaybackSpeedArray()); + for (int i = 0; i < speedValues.length; i++) { + speedChecked[i] = selectedSpeedList.contains(speedValues[i]); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.set_playback_speed_label); + builder.setMultiChoiceItems(R.array.playback_speed_values, + speedChecked, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, + boolean isChecked) { + speedChecked[which] = isChecked; + } + + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int choiceCount = 0; + for (int i = 0; i < speedChecked.length; i++) { + if (speedChecked[i]) { + choiceCount++; + } + } + String[] newSpeedValues = new String[choiceCount]; + int newSpeedIndex = 0; + for (int i = 0; i < speedChecked.length; i++) { + if (speedChecked[i]) { + newSpeedValues[newSpeedIndex++] = speedValues[i]; + } + } + + UserPreferences.setPlaybackSpeedArray(context, + newSpeedValues); + + } + }); + builder.create().show(); + } +} diff --git a/src/de/danoeh/antennapod/preferences/UserPreferences.java b/src/de/danoeh/antennapod/preferences/UserPreferences.java index 0674849db..b2c1927b4 100644 --- a/src/de/danoeh/antennapod/preferences/UserPreferences.java +++ b/src/de/danoeh/antennapod/preferences/UserPreferences.java @@ -5,6 +5,8 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONException; import android.app.AlarmManager; import android.app.PendingIntent; @@ -17,7 +19,6 @@ import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OpmlImportFromPathActivity; import de.danoeh.antennapod.receiver.FeedUpdateReceiver; -import de.danoeh.antennapod.service.PlaybackService; /** * Provides access to preferences set by the user in the settings screen. A @@ -43,6 +44,7 @@ public class UserPreferences implements private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks"; public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize"; private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed"; + private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray"; private static int EPISODE_CACHE_SIZE_UNLIMITED = -1; @@ -62,7 +64,8 @@ public class UserPreferences implements private boolean enableAutodownloadWifiFilter; private String[] autodownloadSelectedNetworks; private int episodeCacheSize; - private float playbackSpeed; + private String playbackSpeed; + private String[] playbackSpeedArray; private UserPreferences(Context context) { this.context = context; @@ -111,7 +114,9 @@ public class UserPreferences implements episodeCacheSize = readEpisodeCacheSize(sp.getString( PREF_EPISODE_CACHE_SIZE, "20")); enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false); - playbackSpeed = sp.getFloat(PREF_PLAYBACK_SPEED, (float) 1.0); + playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0"); + playbackSpeedArray = readPlaybackSpeedArray(sp.getString( + PREF_PLAYBACK_SPEED_ARRAY, null)); } private int readThemeValue(String valueFromPrefs) { @@ -139,6 +144,28 @@ public class UserPreferences implements } } + private String[] readPlaybackSpeedArray(String valueFromPrefs) { + String[] playbackSpeeds = null; + // If this preference hasn't been set yet, return all options + if (valueFromPrefs == null) { + playbackSpeeds = context.getResources().getStringArray( + R.array.playback_speed_values); + } else { + try { + JSONArray jsonArray = new JSONArray(valueFromPrefs); + playbackSpeeds = new String[jsonArray.length()]; + for (int i = 0; i < jsonArray.length(); i++) { + playbackSpeeds[i] = jsonArray.getString(i); + } + } catch (JSONException e) { + Log.e(TAG, + "Got JSON error when trying to get speeds from JSONArray"); + e.printStackTrace(); + } + } + return playbackSpeeds; + } + private static void instanceAvailable() { if (instance == null) { throw new IllegalStateException( @@ -200,11 +227,16 @@ public class UserPreferences implements return EPISODE_CACHE_SIZE_UNLIMITED; } - public static float getPlaybackSpeed() { + public static String getPlaybackSpeed() { instanceAvailable(); return instance.playbackSpeed; } + public static String[] getPlaybackSpeedArray() { + instanceAvailable(); + return instance.playbackSpeedArray; + } + /** * Returns the capacity of the episode cache. This method will return the * negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to @@ -260,20 +292,31 @@ public class UserPreferences implements } else if (key.equals(PREF_ENABLE_AUTODL)) { enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false); } else if (key.equals(PREF_PLAYBACK_SPEED)) { - playbackSpeed = sp.getFloat(PREF_PLAYBACK_SPEED, (float) 1.0); + playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0"); + } else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) { + playbackSpeedArray = readPlaybackSpeedArray(sp.getString( + PREF_PLAYBACK_SPEED_ARRAY, null)); } } - public static void setPlaybackSpeed(Context context, float speed) { + public static void setPlaybackSpeed(Context context, String speed) { SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(context.getApplicationContext()); SharedPreferences.Editor editor = prefs.edit(); - editor.putFloat(PREF_PLAYBACK_SPEED, speed); + editor.putString(PREF_PLAYBACK_SPEED, speed); editor.apply(); + } - Intent intent = new Intent(context, PlaybackService.class); - intent.putExtra(PlaybackService.EXTRA_PLAYBACK_SPEED, speed); - context.startService(intent); + public static void setPlaybackSpeedArray(Context context, String[] speeds) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context.getApplicationContext()); + SharedPreferences.Editor editor = prefs.edit(); + JSONArray jsonArray = new JSONArray(); + for (String speed : speeds) { + jsonArray.put(speed); + } + editor.putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString()); + editor.apply(); } public static void setAutodownloadSelectedNetworks(Context context, diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index afd8de68f..302d532b2 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -82,7 +82,6 @@ public class PlaybackService extends Service { 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_TYPE = "extra.de.danoeh.antennapod.service.notificationType"; - public static final String EXTRA_PLAYBACK_SPEED = "extra.de.danoeh.antennapod.service.playbackSpeed"; /** * If the PlaybackService receives this action, it will stop playback and @@ -373,17 +372,10 @@ public class PlaybackService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "OnStartCommand called"); int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1); - float playbackSpeed = intent.getFloatExtra(EXTRA_PLAYBACK_SPEED, -1); if (keycode != -1) { if (AppConfig.DEBUG) Log.d(TAG, "Received media button event"); handleKeycode(keycode); - } else if (playbackSpeed > 0) { - if (media == null) { - stopSelf(); - } else { - setSpeed(playbackSpeed); - } } else { Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE); @@ -988,7 +980,7 @@ public class PlaybackService extends Service { Log.d(TAG, "Resuming/Starting playback"); writePlaybackPreferences(); - setSpeed(UserPreferences.getPlaybackSpeed()); + setSpeed(Float.parseFloat(UserPreferences.getPlaybackSpeed())); player.start(); if (status != PlayerStatus.PAUSED) { player.seekTo(media.getPosition()); @@ -1569,7 +1561,7 @@ public class PlaybackService extends Service { } } - public double getCurrentPlaybackSpeed() { + public float getCurrentPlaybackSpeed() { if (media.getMediaType() == MediaType.AUDIO && player instanceof AudioPlayer) { AudioPlayer audioPlayer = (AudioPlayer) player; diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java index 0060c756f..b15c3d2b0 100644 --- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java +++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java @@ -255,7 +255,8 @@ public abstract class PlaybackController { public abstract void onPositionObserverUpdate(); - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { + @Override public void onServiceConnected(ComponentName className, IBinder service) { playbackService = ((PlaybackService.LocalBinder) service) .getService(); @@ -343,7 +344,7 @@ public abstract class PlaybackController { }; - private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() { + private final BroadcastReceiver shutdownReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -356,7 +357,7 @@ public abstract class PlaybackController { } } }; - + public abstract void onPlaybackSpeedChange(); public abstract void onShutdownNotification(); @@ -662,13 +663,13 @@ public abstract class PlaybackController { return playbackService != null && playbackService.canSetSpeed(); } - public void setPlaybackSpeed(double speed) { + public void setPlaybackSpeed(float speed) { if (playbackService != null) { playbackService.setSpeed(speed); } } - public double getCurrentPlaybackSpeedMultiplier() { + public float getCurrentPlaybackSpeedMultiplier() { if (canSetPlaybackSpeed()) { return playbackService.getCurrentPlaybackSpeed(); } else { -- cgit v1.2.3 From 0a607117d6c8d26d48adede0c903e0b7772a4327 Mon Sep 17 00:00:00 2001 From: James Falcon Date: Mon, 13 May 2013 21:53:44 -0500 Subject: Reduce default speeds to every .1 instead of every .05 so user doesn't have to do a lot of unchecking. Also, be consistant about where float or double is used and be consistent about which context is used for UserPreferences. --- .../antennapod/activity/AudioplayerActivity.java | 3 +- .../antennapod/dialog/VariableSpeedDialog.java | 3 +- .../antennapod/preferences/UserPreferences.java | 42 ++++++++++++---------- 3 files changed, 26 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index a84e5b8f2..a5104426c 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -428,8 +428,7 @@ public class AudioplayerActivity extends MediaplayerActivity { break; } } - UserPreferences.setPlaybackSpeed(AudioplayerActivity.this, - newSpeed); + UserPreferences.setPlaybackSpeed(newSpeed); controller.setPlaybackSpeed(Float.parseFloat(newSpeed)); } } diff --git a/src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index 308235f95..bcff905d9 100644 --- a/src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/src/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -85,8 +85,7 @@ public class VariableSpeedDialog { } } - UserPreferences.setPlaybackSpeedArray(context, - newSpeedValues); + UserPreferences.setPlaybackSpeedArray(newSpeedValues); } }); diff --git a/src/de/danoeh/antennapod/preferences/UserPreferences.java b/src/de/danoeh/antennapod/preferences/UserPreferences.java index b2c1927b4..8ee03527a 100644 --- a/src/de/danoeh/antennapod/preferences/UserPreferences.java +++ b/src/de/danoeh/antennapod/preferences/UserPreferences.java @@ -2,6 +2,8 @@ package de.danoeh.antennapod.preferences; import java.io.File; import java.io.IOException; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; @@ -89,6 +91,7 @@ public class UserPreferences implements createNoMediaFile(); PreferenceManager.getDefaultSharedPreferences(context) .registerOnSharedPreferenceChangeListener(instance); + } private void loadPreferences() { @@ -145,17 +148,25 @@ public class UserPreferences implements } private String[] readPlaybackSpeedArray(String valueFromPrefs) { - String[] playbackSpeeds = null; - // If this preference hasn't been set yet, return all options + String[] selectedSpeeds = null; + // If this preference hasn't been set yet, return the default options if (valueFromPrefs == null) { - playbackSpeeds = context.getResources().getStringArray( + String[] allSpeeds = context.getResources().getStringArray( R.array.playback_speed_values); + List speedList = new LinkedList(); + for (String speedStr : allSpeeds) { + float speed = Float.parseFloat(speedStr); + if (speed < 2.0001 && speed * 10 % 1 == 0) { + speedList.add(speedStr); + } + } + selectedSpeeds = speedList.toArray(new String[speedList.size()]); } else { try { JSONArray jsonArray = new JSONArray(valueFromPrefs); - playbackSpeeds = new String[jsonArray.length()]; + selectedSpeeds = new String[jsonArray.length()]; for (int i = 0; i < jsonArray.length(); i++) { - playbackSpeeds[i] = jsonArray.getString(i); + selectedSpeeds[i] = jsonArray.getString(i); } } catch (JSONException e) { Log.e(TAG, @@ -163,7 +174,7 @@ public class UserPreferences implements e.printStackTrace(); } } - return playbackSpeeds; + return selectedSpeeds; } private static void instanceAvailable() { @@ -299,24 +310,19 @@ public class UserPreferences implements } } - public static void setPlaybackSpeed(Context context, String speed) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(context.getApplicationContext()); - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(PREF_PLAYBACK_SPEED, speed); - editor.apply(); + public static void setPlaybackSpeed(String speed) { + PreferenceManager.getDefaultSharedPreferences(instance.context).edit() + .putString(PREF_PLAYBACK_SPEED, speed).apply(); } - public static void setPlaybackSpeedArray(Context context, String[] speeds) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(context.getApplicationContext()); - SharedPreferences.Editor editor = prefs.edit(); + public static void setPlaybackSpeedArray(String[] speeds) { JSONArray jsonArray = new JSONArray(); for (String speed : speeds) { jsonArray.put(speed); } - editor.putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString()); - editor.apply(); + PreferenceManager.getDefaultSharedPreferences(instance.context).edit() + .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString()) + .apply(); } public static void setAutodownloadSelectedNetworks(Context context, -- cgit v1.2.3 From eeec2d846f4b9af140a9620756cd29437d9294e1 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Tue, 20 Aug 2013 19:04:31 +0200 Subject: Fixed ConcurrentModificationException in performAutoCleanup --- src/de/danoeh/antennapod/storage/DBTasks.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/de/danoeh/antennapod/storage/DBTasks.java b/src/de/danoeh/antennapod/storage/DBTasks.java index b1efda658..f25625949 100644 --- a/src/de/danoeh/antennapod/storage/DBTasks.java +++ b/src/de/danoeh/antennapod/storage/DBTasks.java @@ -406,10 +406,11 @@ public final class DBTasks { private static int performAutoCleanup(final Context context, final int episodeNumber) { - List candidates = DBReader.getDownloadedItems(context); + List candidates = new ArrayList(); + List downloadedItems = DBReader.getDownloadedItems(context); List queue = DBReader.getQueue(context); List delete; - for (FeedItem item : candidates) { + for (FeedItem item : downloadedItems) { if (item.hasMedia() && item.getMedia().isDownloaded() && !queue.contains(item) && item.isRead()) { candidates.add(item); -- cgit v1.2.3 From e8d0abe954b6cda97c99f90e9b0da75e8fa5f73a Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Fri, 23 Aug 2013 14:58:09 +0200 Subject: Catch RuntimeException thrown by MediaPlayer constructor --- src/de/danoeh/antennapod/service/download/DownloadService.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index c84a6f913..8b090981c 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -828,8 +828,9 @@ public class DownloadService extends Service { media.setFile_url(request.getDestination()); // Get duration - MediaPlayer mediaplayer = new MediaPlayer(); + MediaPlayer mediaplayer = null; try { + mediaplayer = new MediaPlayer(); mediaplayer.setDataSource(media.getFile_url()); mediaplayer.prepare(); media.setDuration(mediaplayer.getDuration()); @@ -838,8 +839,13 @@ public class DownloadService extends Service { mediaplayer.reset(); } catch (IOException e) { e.printStackTrace(); + } catch (RuntimeException e) { + // Thrown by MediaPlayer initialization on some devices + e.printStackTrace(); } finally { - mediaplayer.release(); + if (mediaplayer != null) { + mediaplayer.release(); + } } if (media.getItem().getChapters() == null) { -- cgit v1.2.3