diff options
Diffstat (limited to 'core/src/main')
6 files changed, 108 insertions, 111 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java index 64d4d14fd..e617d4192 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.core.feed; +import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.database.Cursor; @@ -14,12 +15,16 @@ import java.util.Date; import java.util.List; import java.util.concurrent.Callable; +import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction; +import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.util.ChapterUtils; +import de.danoeh.antennapod.core.util.flattr.FlattrUtils; import de.danoeh.antennapod.core.util.playback.Playable; public class FeedMedia extends FeedFile implements Playable { @@ -48,6 +53,8 @@ public class FeedMedia extends FeedFile implements Playable { private String mime_type; @Nullable private volatile FeedItem item; private Date playbackCompletionDate; + private int startPosition = -1; + private int playedDurationWhenStarted; // if null: unknown, will be checked private Boolean hasEmbeddedPicture; @@ -73,6 +80,7 @@ public class FeedMedia extends FeedFile implements Playable { this.duration = duration; this.position = position; this.played_duration = played_duration; + this.playedDurationWhenStarted = played_duration; this.size = size; this.mime_type = mime_type; this.playbackCompletionDate = playbackCompletionDate == null @@ -472,15 +480,59 @@ public class FeedMedia extends FeedFile implements Playable { } setPosition(newPosition); setLastPlayedTime(timeStamp); + if(startPosition>=0 && position > startPosition) { + setDuration(playedDurationWhenStarted + position - startPosition); + } DBWriter.setFeedMediaPlaybackInformation(this); } @Override public void onPlaybackStart() { + startPosition = (position > 0) ? position : 0; + playedDurationWhenStarted = played_duration; } + @Override - public void onPlaybackCompleted() { + public void onPlaybackPause(Context context) { + if (position > startPosition) { + played_duration = playedDurationWhenStarted + position - startPosition; + playedDurationWhenStarted = played_duration; + } + postPlaybackTasks(context, false); + startPosition = position; + } + @Override + public void onPlaybackCompleted(Context context) { + postPlaybackTasks(context, true); + startPosition = -1; + } + + private void postPlaybackTasks(Context context, boolean completed) { + if (item != null) { + // gpodder play action + if (startPosition >= 0 && (completed || startPosition < position) && + GpodnetPreferences.loggedIn()) { + GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.PLAY) + .currentDeviceId() + .currentTimestamp() + .started(startPosition / 1000) + .position((completed ? duration : position) / 1000) + .total(duration / 1000) + .build(); + GpodnetPreferences.enqueueEpisodeAction(action); + } + // Auto flattr + float autoFlattrThreshold = UserPreferences.getAutoFlattrPlayedDurationThreshold(); + if (FlattrUtils.hasToken() && + UserPreferences.isAutoFlattr() && + item.getPaymentLink() != null && + item.getFlattrStatus().getUnflattred() && + (completed && autoFlattrThreshold <= 1.0f || + played_duration >= autoFlattrThreshold * duration)) { + DBTasks.flattrItemIfLoggedIn(context, item); + } + } } @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java index 62e028475..4165eb84a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java @@ -146,6 +146,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { if (!media.getIdentifier().equals(playable.getIdentifier())) { final Playable oldMedia = media; executor.submit(() -> callback.onPostPlayback(oldMedia, false, true)); + } else { + media.onPlaybackPause(context); } setPlayerStatus(PlayerStatus.INDETERMINATE, null); @@ -253,6 +255,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { Log.d(TAG, "Pausing playback."); mediaPlayer.pause(); setPlayerStatus(PlayerStatus.PAUSED, media); + media.onPlaybackPause(context.getApplicationContext()); if (abandonFocus) { audioManager.abandonAudioFocus(audioFocusChangeListener); @@ -370,10 +373,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { if (playerStatus == PlayerStatus.PLAYING || playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) { - if (!stream) { - statusBeforeSeeking = playerStatus; - setPlayerStatus(PlayerStatus.SEEKING, media); - } + statusBeforeSeeking = playerStatus; + setPlayerStatus(PlayerStatus.SEEKING, media); if(seekLatch != null && seekLatch.getCount() > 0) { try { seekLatch.await(3, TimeUnit.SECONDS); @@ -382,6 +383,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { } } seekLatch = new CountDownLatch(1); + if (statusBeforeSeeking == PlayerStatus.PLAYING) { + media.setPosition(getPosition()); + media.onPlaybackPause(context); + } mediaPlayer.seekTo(t); try { seekLatch.await(3, TimeUnit.SECONDS); @@ -926,7 +931,14 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { seekLatch.countDown(); } playerLock.lock(); + media.setPosition(getPosition()); + if (playerStatus == PlayerStatus.PLAYING) { + media.onPlaybackStart(); + } if (playerStatus == PlayerStatus.SEEKING) { + if (statusBeforeSeeking == PlayerStatus.PLAYING) { + media.onPlaybackStart(); + } setPlayerStatus(statusBeforeSeeking, media); } playerLock.unlock(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index f674ae9dd..5af34fd9f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -52,9 +52,6 @@ import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.glide.ApGlideSettings; -import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction; -import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; @@ -63,7 +60,6 @@ import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.IntList; import de.danoeh.antennapod.core.util.QueueAccess; -import de.danoeh.antennapod.core.util.flattr.FlattrUtils; import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.Playable; @@ -206,8 +202,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { */ private MediaSessionCompat mediaSession; - private int startPosition; - private static volatile MediaType currentMediaType = MediaType.UNKNOWN; private final IBinder mBinder = new LocalBinder(); @@ -473,7 +467,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { UserPreferences.shouldHardwareButtonSkip()) { // assume the skip command comes from a notification or the lockscreen // a >| skip button should actually skip - mediaPlayer.endPlayback(); + mediaPlayer.skip(); } else { // assume skip command comes from a (bluetooth) media button // user actually wants to fast-forward @@ -530,7 +524,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() { @Override public void positionSaverTick() { - saveCurrentPosition(true, PlaybackServiceTaskManager.POSITION_SAVER_WAITING_INTERVAL); + saveCurrentPosition(); } @Override @@ -583,7 +577,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { case PAUSED: taskManager.cancelPositionSaver(); - saveCurrentPosition(false, 0); + saveCurrentPosition(); taskManager.cancelWidgetUpdater(); if ((UserPreferences.isPersistNotify() || isCasting) && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { @@ -595,22 +589,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { stopForeground(true); } writePlayerStatusPlaybackPreferences(); - - final Playable playable = newInfo.playable; - - // Gpodder: send play action - if(GpodnetPreferences.loggedIn() && playable instanceof FeedMedia) { - FeedMedia media = (FeedMedia) playable; - FeedItem item = media.getItem(); - GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY) - .currentDeviceId() - .currentTimestamp() - .started(startPosition / 1000) - .position(getCurrentPosition() / 1000) - .total(getDuration() / 1000) - .build(); - GpodnetPreferences.enqueueEpisodeAction(action); - } break; case STOPPED: @@ -627,7 +605,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { writePlayerStatusPlaybackPreferences(); setupNotification(newInfo); started = true; - startPosition = mediaPlayer.getPosition(); break; case ERROR: @@ -790,21 +767,10 @@ public class PlaybackService extends MediaBrowserServiceCompat { Log.d(TAG, "smart mark as played"); } - // auto-flattr if enabled - if (isAutoFlattrable(media) && UserPreferences.getAutoFlattrPlayedDurationThreshold() == 1.0f) { - DBTasks.flattrItemIfLoggedIn(PlaybackService.this, item); - } - - // gpodder play action - if (GpodnetPreferences.loggedIn()) { - GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY) - .currentDeviceId() - .currentTimestamp() - .started(startPosition / 1000) - .position(((ended || smartMarkAsPlayed) ? media.getDuration() : media.getPosition()) / 1000) - .total(media.getDuration() / 1000) - .build(); - GpodnetPreferences.enqueueEpisodeAction(action); + if (ended || smartMarkAsPlayed) { + media.onPlaybackCompleted(getApplicationContext()); + } else { + media.onPlaybackPause(getApplicationContext()); } if (item != null) { @@ -1234,29 +1200,13 @@ public class PlaybackService extends MediaBrowserServiceCompat { /** * Persists the current position and last played time of the media file. - * - * @param updatePlayedDuration true if played_duration should be updated. This applies only to FeedMedia objects - * @param deltaPlayedDuration value by which played_duration should be increased. */ - private synchronized void saveCurrentPosition(boolean updatePlayedDuration, int deltaPlayedDuration) { + private synchronized void saveCurrentPosition() { int position = getCurrentPosition(); int duration = getDuration(); - float playbackSpeed = getCurrentPlaybackSpeed(); final Playable playable = mediaPlayer.getPlayable(); if (position != INVALID_TIME && duration != INVALID_TIME && playable != null) { Log.d(TAG, "Saving current position to " + position); - if (updatePlayedDuration && playable instanceof FeedMedia) { - FeedMedia media = (FeedMedia) playable; - FeedItem item = media.getItem(); - media.setPlayedDuration(media.getPlayedDuration() + ((int) (deltaPlayedDuration * playbackSpeed))); - // Auto flattr - if (isAutoFlattrable(media) && - (media.getPlayedDuration() > UserPreferences.getAutoFlattrPlayedDurationThreshold() * duration)) { - Log.d(TAG, "saveCurrentPosition: performing auto flattr since played duration " + Integer.toString(media.getPlayedDuration()) - + " is " + UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100 + "% of file duration " + Integer.toString(duration)); - DBTasks.flattrItemIfLoggedIn(this, item); - } - } playable.saveCurrentPosition( PreferenceManager.getDefaultSharedPreferences(getApplicationContext()), position, @@ -1424,7 +1374,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void onReceive(Context context, Intent intent) { if (TextUtils.equals(intent.getAction(), ACTION_SKIP_CURRENT_EPISODE)) { Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent"); - mediaPlayer.endPlayback(); + mediaPlayer.skip(); } } }; @@ -1517,26 +1467,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void seekTo(final int t) { - if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING - && GpodnetPreferences.loggedIn()) { - final Playable playable = mediaPlayer.getPlayable(); - if (playable instanceof FeedMedia) { - FeedMedia media = (FeedMedia) playable; - FeedItem item = media.getItem(); - GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY) - .currentDeviceId() - .currentTimestamp() - .started(startPosition / 1000) - .position(getCurrentPosition() / 1000) - .total(getDuration() / 1000) - .build(); - GpodnetPreferences.enqueueEpisodeAction(action); - } - } mediaPlayer.seekTo(t); - if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING ) { - startPosition = t; - } } @@ -1545,10 +1476,10 @@ public class PlaybackService extends MediaBrowserServiceCompat { } /** - * @see LocalPSMP#seekToChapter(de.danoeh.antennapod.core.feed.Chapter) + * Seek to the start of the specified chapter. */ public void seekToChapter(Chapter c) { - mediaPlayer.seekToChapter(c); + seekTo((int) c.getStart()); } /** @@ -1575,15 +1506,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { return mediaPlayer.getVideoSize(); } - private boolean isAutoFlattrable(FeedMedia media) { - if (media != null) { - FeedItem item = media.getItem(); - return item != null && FlattrUtils.hasToken() && UserPreferences.isAutoFlattr() && item.getPaymentLink() != null && item.getFlattrStatus().getUnflattred(); - } else { - return false; - } - } - private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() { private static final String TAG = "MediaSessionCompat"; @@ -1656,7 +1578,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void onSkipToNext() { Log.d(TAG, "onSkipToNext()"); if(UserPreferences.shouldHardwareButtonSkip()) { - mediaPlayer.endPlayback(); + mediaPlayer.skip(); } else { seekDelta(UserPreferences.getFastFowardSecs() * 1000); } @@ -1699,7 +1621,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { PlaybackServiceMediaPlayer getMediaPlayer(); void setIsCasting(boolean isCasting); void sendNotificationBroadcast(int type, int code); - void saveCurrentPosition(boolean updatePlayedDuration, int deltaPlayedDuration); + void saveCurrentPosition(); void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info); MediaSessionCompat getMediaSession(); Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter); @@ -1733,8 +1655,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { } @Override - public void saveCurrentPosition(boolean updatePlayedDuration, int deltaPlayedDuration) { - PlaybackService.this.saveCurrentPosition(updatePlayedDuration, deltaPlayedDuration); + public void saveCurrentPosition() { + PlaybackService.this.saveCurrentPosition(); } @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java index c76ea3889..d52d3b8bc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java @@ -8,7 +8,6 @@ import android.util.Log; import android.util.Pair; import android.view.SurfaceHolder; -import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.util.playback.Playable; @@ -127,13 +126,6 @@ public abstract class PlaybackServiceMediaPlayer { public abstract void seekDelta(int d); /** - * Seek to the start of the specified chapter. - */ - public void seekToChapter(@NonNull Chapter c) { - seekTo((int) c.getStart()); - } - - /** * Returns the duration of the current media object or INVALID_TIME if the duration could not be retrieved. */ public abstract int getDuration(); @@ -233,7 +225,7 @@ public abstract class PlaybackServiceMediaPlayer { protected abstract void setPlayable(Playable playable); - public void endPlayback() { + public void skip() { endPlayback(true); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java index a37f98469..c4acdb65e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.core.util.playback; +import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.media.MediaMetadataRetriever; @@ -205,7 +206,12 @@ public class ExternalMedia implements Playable { } @Override - public void onPlaybackCompleted() { + public void onPlaybackPause(Context context) { + + } + + @Override + public void onPlaybackCompleted(Context context) { } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java index 6459d86ed..279c56338 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java @@ -138,14 +138,27 @@ public interface Playable extends Parcelable, void setLastPlayedTime(long lastPlayedTimestamp); /** - * Is called by the PlaybackService when playback starts. + * This method should be called every time playback starts on this object. + * <p/> + * Position held by this Playable should be set accurately before a call to this method is made. */ void onPlaybackStart(); /** - * Is called by the PlaybackService when playback is completed. + * This method should be called every time playback pauses or stops on this object, + * including just before a seeking operation is performed, after which a call to + * {@link #onPlaybackStart()} should be made. If playback completes, calling this method is not + * necessary, as long as a call to {@link #onPlaybackCompleted(Context)} is made. + * <p/> + * Position held by this Playable should be set accurately before a call to this method is made. */ - void onPlaybackCompleted(); + void onPlaybackPause(Context context); + + /** + * This method should be called when playback completes for this object. + * @param context + */ + void onPlaybackCompleted(Context context); /** * Returns an integer that must be unique among all Playable classes. The |