diff options
author | Stefan Mitrik <stevo.mit@gmail.com> | 2015-10-15 00:52:55 +0200 |
---|---|---|
committer | Stefan Mitrik <stevo.mit@gmail.com> | 2015-10-15 00:52:55 +0200 |
commit | f7dabd93351a229d16fba267138cb5cbc508fa57 (patch) | |
tree | be161414ef8170f179456711dd552c8e36c71077 /core | |
parent | 87b42c23dc6aff1fd14acde7d1773b84f67394f8 (diff) | |
download | AntennaPod-f7dabd93351a229d16fba267138cb5cbc508fa57.zip |
Rewind after pause feature
The playback is rewinded X seconds after the pause and resume. The
rewind duration depends on time that elapsed between the pause and
resume.
Diffstat (limited to 'core')
8 files changed, 164 insertions, 37 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 7397dd935..5a345b9d6 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 @@ -40,6 +40,7 @@ public class FeedMedia extends FeedFile implements Playable { private int duration; private int position; // Current position in file + private long lastPlayedTime; // Last time this media was played (in ms) private int played_duration; // How many ms of this file have been played (for autoflattring) private long size; // File size in Byte private String mime_type; @@ -62,7 +63,8 @@ public class FeedMedia extends FeedFile implements Playable { public FeedMedia(long id, FeedItem item, int duration, int position, long size, String mime_type, String file_url, String download_url, - boolean downloaded, Date playbackCompletionDate, int played_duration) { + boolean downloaded, Date playbackCompletionDate, int played_duration, + long lastPlayedTime) { super(file_url, download_url, downloaded); this.id = id; this.item = item; @@ -73,14 +75,15 @@ public class FeedMedia extends FeedFile implements Playable { this.mime_type = mime_type; this.playbackCompletionDate = playbackCompletionDate == null ? null : (Date) playbackCompletionDate.clone(); + this.lastPlayedTime = lastPlayedTime; } public FeedMedia(long id, FeedItem item, int duration, int position, long size, String mime_type, String file_url, String download_url, boolean downloaded, Date playbackCompletionDate, int played_duration, - Boolean hasEmbeddedPicture) { + Boolean hasEmbeddedPicture, long lastPlayedTime) { this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded, - playbackCompletionDate, played_duration); + playbackCompletionDate, played_duration, lastPlayedTime); this.hasEmbeddedPicture = hasEmbeddedPicture; } @@ -95,6 +98,7 @@ public class FeedMedia extends FeedFile implements Playable { int indexDownloadUrl = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL); int indexDownloaded = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOADED); int indexPlayedDuration = cursor.getColumnIndex(PodDBAdapter.KEY_PLAYED_DURATION); + int indexLastPlayedTime = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_PLAYED_TIME); long mediaId = cursor.getLong(indexId); Date playbackCompletionDate = null; @@ -128,7 +132,8 @@ public class FeedMedia extends FeedFile implements Playable { cursor.getInt(indexDownloaded) > 0, playbackCompletionDate, cursor.getInt(indexPlayedDuration), - hasEmbeddedPicture + hasEmbeddedPicture, + cursor.getLong(indexLastPlayedTime) ); } @@ -231,6 +236,11 @@ public class FeedMedia extends FeedFile implements Playable { this.duration = duration; } + @Override + public void setLastPlayedTime(long lastPlayedTime) { + this.lastPlayedTime = lastPlayedTime; + } + public int getPlayedDuration() { return played_duration; } @@ -243,6 +253,11 @@ public class FeedMedia extends FeedFile implements Playable { return position; } + @Override + public long getLastPlayedTime() { + return lastPlayedTime; + } + public void setPosition(int position) { this.position = position; if(position > 0 && item.isNew()) { @@ -336,6 +351,7 @@ public class FeedMedia extends FeedFile implements Playable { dest.writeByte((byte) ((downloaded) ? 1 : 0)); dest.writeLong((playbackCompletionDate != null) ? playbackCompletionDate.getTime() : 0); dest.writeInt(played_duration); + dest.writeLong(lastPlayedTime); } @Override @@ -438,12 +454,13 @@ public class FeedMedia extends FeedFile implements Playable { } @Override - public void saveCurrentPosition(SharedPreferences pref, int newPosition) { - DBWriter.setFeedMediaPlaybackInformation(this); + public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timeStamp) { if(item.isNew()) { DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); } setPosition(newPosition); + setLastPlayedTime(timeStamp); + DBWriter.setFeedMediaPlaybackInformation(this); } @Override @@ -488,7 +505,7 @@ public class FeedMedia extends FeedFile implements Playable { final long id = in.readLong(); final long itemID = in.readLong(); FeedMedia result = new FeedMedia(id, null, in.readInt(), in.readInt(), in.readLong(), in.readString(), in.readString(), - in.readString(), in.readByte() != 0, new Date(in.readLong()), in.readInt()); + in.readString(), in.readByte() != 0, new Date(in.readLong()), in.readInt(), in.readLong()); result.itemID = itemID; return result; } 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 cdd63ef62..ab49bad08 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 @@ -923,7 +923,7 @@ public class PlaybackService extends Service { } /** - * Saves the current position of the media file to the DB + * 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. @@ -948,8 +948,9 @@ public class PlaybackService extends Service { } } playable.saveCurrentPosition(PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()), - position + .getDefaultSharedPreferences(getApplicationContext()), + position, + System.currentTimeMillis() ); } } 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 9d6827ed9..41a9a08f8 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 @@ -39,6 +39,7 @@ import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.util.RewindAfterPauseUtils; import de.danoeh.antennapod.core.util.playback.AudioPlayer; import de.danoeh.antennapod.core.util.playback.IPlayer; import de.danoeh.antennapod.core.util.playback.Playable; @@ -330,10 +331,14 @@ public class PlaybackServiceMediaPlayer implements SharedPreferences.OnSharedPre if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { acquireWifiLockIfNecessary(); setSpeed(Float.parseFloat(UserPreferences.getPlaybackSpeed())); - mediaPlayer.start(); - if (playerStatus == PlayerStatus.PREPARED && media.getPosition() > 0) { - mediaPlayer.seekTo(media.getPosition()); + + if (media.getPosition() > 0) { + int newPosition = RewindAfterPauseUtils.calculatePositionWithRewind( + media.getPosition(), + media.getLastPlayedTime()); + mediaPlayer.seekTo(newPosition); } + mediaPlayer.start(); setPlayerStatus(PlayerStatus.PLAYING, media); pausedBecauseOfTransientAudiofocusLoss = false; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java index bae31b52b..73584c40a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java @@ -732,7 +732,7 @@ public class DBWriter { } /** - * Saves the 'position' and 'duration' attributes of a FeedMedia object + * Saves the 'position', 'duration' and 'last played time' attributes of a FeedMedia object * * @param media The FeedMedia object. */ diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index d55d4c231..7d1afc039 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -103,7 +103,7 @@ public class PodDBAdapter { public static final String KEY_HIDE = "hide"; public static final String KEY_LAST_UPDATE_FAILED = "last_update_failed"; public static final String KEY_HAS_EMBEDDED_PICTURE = "has_embedded_picture"; - + public static final String KEY_LAST_PLAYED_TIME = "last_played_time"; // Table names public static final String TABLE_NAME_FEEDS = "Feeds"; @@ -160,7 +160,8 @@ public class PodDBAdapter { + KEY_PLAYBACK_COMPLETION_DATE + " INTEGER," + KEY_FEEDITEM + " INTEGER," + KEY_PLAYED_DURATION + " INTEGER," - + KEY_HAS_EMBEDDED_PICTURE + " INTEGER)"; + + KEY_HAS_EMBEDDED_PICTURE + " INTEGER," + + KEY_LAST_PLAYED_TIME + " INTEGER)"; public static final String CREATE_TABLE_DOWNLOAD_LOG = "CREATE TABLE " + TABLE_NAME_DOWNLOAD_LOG + " (" + TABLE_PRIMARY_KEY + KEY_FEEDFILE @@ -444,6 +445,7 @@ public class PodDBAdapter { values.put(KEY_DOWNLOADED, media.isDownloaded()); values.put(KEY_FILE_URL, media.getFile_url()); values.put(KEY_HAS_EMBEDDED_PICTURE, media.hasEmbeddedPicture()); + values.put(KEY_LAST_PLAYED_TIME, media.getLastPlayedTime()); if (media.getPlaybackCompletionDate() != null) { values.put(KEY_PLAYBACK_COMPLETION_DATE, media.getPlaybackCompletionDate().getTime()); @@ -468,6 +470,7 @@ public class PodDBAdapter { values.put(KEY_POSITION, media.getPosition()); values.put(KEY_DURATION, media.getDuration()); values.put(KEY_PLAYED_DURATION, media.getPlayedDuration()); + values.put(KEY_LAST_PLAYED_TIME, media.getLastPlayedTime()); db.update(TABLE_NAME_FEED_MEDIA, values, KEY_ID + "=?", new String[]{String.valueOf(media.getId())}); } else { @@ -1439,7 +1442,7 @@ public class PodDBAdapter { */ private static class PodDBHelper extends SQLiteOpenHelper { - private final static int VERSION = 1040001; + private final static int VERSION = 1040002; private Context context; @@ -1673,6 +1676,10 @@ public class PodDBAdapter { if(oldVersion < 1040001) { db.execSQL(CREATE_TABLE_FAVORITES); } + if (oldVersion < 1040002) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " ADD COLUMN " + PodDBAdapter.KEY_LAST_PLAYED_TIME + " INTEGER DEFAULT 0"); + } EventBus.getDefault().post(ProgressEvent.end()); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java new file mode 100644 index 000000000..b48c9ac64 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java @@ -0,0 +1,47 @@ +package de.danoeh.antennapod.core.util; + +import java.util.concurrent.TimeUnit; + +/** + * This class calculates the proper rewind time after the pause and resume. + * <p> + * User might loose context if he/she pauses and resumes the media after longer time. + * Media file should be "rewinded" x seconds after user resumes the playback. + */ +public class RewindAfterPauseUtils { + + public static final long ELAPSED_TIME_FOR_SHORT_REWIND = TimeUnit.MINUTES.toMillis(3); + public static final long ELAPSED_TIME_FOR_MEDIUM_REWIND = TimeUnit.HOURS.toMillis(1); + public static final long ELAPSED_TIME_FOR_LONG_REWIND = TimeUnit.DAYS.toMillis(1); + + public static final long SHORT_REWIND = TimeUnit.SECONDS.toMillis(2); + public static final long MEDIUM_REWIND = TimeUnit.SECONDS.toMillis(5); + public static final long LONG_REWIND = TimeUnit.SECONDS.toMillis(10); + + /** + * @param currentPosition current position in a media file in ms + * @param lastPlayedTime timestamp when was media paused + * @return new rewinded position for playback in milliseconds + */ + public static int calculatePositionWithRewind(int currentPosition, long lastPlayedTime) { + if (currentPosition > 0 && lastPlayedTime > 0) { + long elapsedTime = System.currentTimeMillis() - lastPlayedTime; + long rewindTime = 0; + + if (elapsedTime > ELAPSED_TIME_FOR_LONG_REWIND) { + rewindTime = LONG_REWIND; + } else if (elapsedTime > ELAPSED_TIME_FOR_MEDIUM_REWIND) { + rewindTime = MEDIUM_REWIND; + } else if (elapsedTime > ELAPSED_TIME_FOR_SHORT_REWIND) { + rewindTime = SHORT_REWIND; + } + + int newPosition = currentPosition - (int) rewindTime; + + return newPosition > 0 ? newPosition : 0; + } + else { + return currentPosition; + } + } +} 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 49769f4f0..ec50dce7c 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 @@ -20,6 +20,7 @@ public class ExternalMedia implements Playable { public static final String PREF_SOURCE_URL = "ExternalMedia.PrefSourceUrl"; public static final String PREF_POSITION = "ExternalMedia.PrefPosition"; public static final String PREF_MEDIA_TYPE = "ExternalMedia.PrefMediaType"; + public static final String PREF_LAST_PLAYED_TIME = "ExternalMedia.PrefLastPlayedTime"; private String source; @@ -29,6 +30,7 @@ public class ExternalMedia implements Playable { private List<Chapter> chapters; private int duration; private int position; + private long lastPlayedTime; public ExternalMedia(String source, MediaType mediaType) { super(); @@ -36,9 +38,10 @@ public class ExternalMedia implements Playable { this.mediaType = mediaType; } - public ExternalMedia(String source, MediaType mediaType, int position) { + public ExternalMedia(String source, MediaType mediaType, int position, long lastPlayedTime) { this(source, mediaType); this.position = position; + this.lastPlayedTime = lastPlayedTime; } @Override @@ -51,6 +54,7 @@ public class ExternalMedia implements Playable { dest.writeString(source); dest.writeString(mediaType.toString()); dest.writeInt(position); + dest.writeLong(lastPlayedTime); } @Override @@ -58,6 +62,7 @@ public class ExternalMedia implements Playable { prefEditor.putString(PREF_SOURCE_URL, source); prefEditor.putString(PREF_MEDIA_TYPE, mediaType.toString()); prefEditor.putInt(PREF_POSITION, position); + prefEditor.putLong(PREF_LAST_PLAYED_TIME, lastPlayedTime); } @Override @@ -145,6 +150,11 @@ public class ExternalMedia implements Playable { } @Override + public long getLastPlayedTime() { + return lastPlayedTime; + } + + @Override public MediaType getMediaType() { return mediaType; } @@ -170,10 +180,12 @@ public class ExternalMedia implements Playable { } @Override - public void saveCurrentPosition(SharedPreferences pref, int newPosition) { + public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp) { SharedPreferences.Editor editor = pref.edit(); editor.putInt(PREF_POSITION, newPosition); + editor.putLong(PREF_LAST_PLAYED_TIME, timestamp); position = newPosition; + lastPlayedTime = timestamp; editor.commit(); } @@ -188,6 +200,11 @@ public class ExternalMedia implements Playable { } @Override + public void setLastPlayedTime(long lastPlayedTime) { + this.lastPlayedTime = lastPlayedTime; + } + + @Override public void onPlaybackStart() { } @@ -215,8 +232,12 @@ public class ExternalMedia implements Playable { if (in.dataAvail() > 0) { position = in.readInt(); } - ExternalMedia extMedia = new ExternalMedia(source, type, position); - return extMedia; + long lastPlayedTime = 0; + if (in.dataAvail() > 0) { + lastPlayedTime = in.readLong(); + } + + return new ExternalMedia(source, type, position, lastPlayedTime); } public ExternalMedia[] newArray(int size) { 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 6e306d30a..86ec4fbd0 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 @@ -82,6 +82,12 @@ public interface Playable extends Parcelable, public int getPosition(); /** + * Returns last time (in ms) when this playable was played or 0 + * if last played time is unknown. + */ + public long getLastPlayedTime(); + + /** * Returns the type of media. This method should return the correct value * BEFORE loadMetadata() is called. */ @@ -115,14 +121,23 @@ public interface Playable extends Parcelable, * Saves the current position of this object. Implementations can use the * provided SharedPreference to save this information and retrieve it later * via PlayableUtils.createInstanceFromPreferences. + * + * @param pref shared prefs that might be used to store this object + * @param newPosition new playback position in ms + * @param timestamp current time in ms */ - public void saveCurrentPosition(SharedPreferences pref, int newPosition); + public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp); public void setPosition(int newPosition); public void setDuration(int newDuration); /** + * @param lastPlayedTimestamp timestamp in ms + */ + public void setLastPlayedTime(long lastPlayedTimestamp); + + /** * Is called by the PlaybackService when playback starts. */ public void onPlaybackStart(); @@ -159,28 +174,42 @@ public interface Playable extends Parcelable, */ public static Playable createInstanceFromPreferences(Context context, int type, SharedPreferences pref) { + Playable result = null; // ADD new Playable types here: switch (type) { case FeedMedia.PLAYABLE_TYPE_FEEDMEDIA: - long mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1); - if (mediaId != -1) { - return DBReader.getFeedMedia(mediaId); - } + result = createFeedMediaInstance(pref); break; case ExternalMedia.PLAYABLE_TYPE_EXTERNAL_MEDIA: - String source = pref.getString(ExternalMedia.PREF_SOURCE_URL, - null); - String mediaType = pref.getString( - ExternalMedia.PREF_MEDIA_TYPE, null); - if (source != null && mediaType != null) { - int position = pref.getInt(ExternalMedia.PREF_POSITION, 0); - return new ExternalMedia(source, - MediaType.valueOf(mediaType), position); - } + result = createExternalMediaInstance(pref); break; } - Log.e(TAG, "Could not restore Playable object from preferences"); - return null; + if (result == null) { + Log.e(TAG, "Could not restore Playable object from preferences"); + } + return result; + } + + private static Playable createFeedMediaInstance(SharedPreferences pref) { + Playable result = null; + long mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1); + if (mediaId != -1) { + result = DBReader.getFeedMedia(mediaId); + } + return result; + } + + private static Playable createExternalMediaInstance(SharedPreferences pref) { + Playable result = null; + String source = pref.getString(ExternalMedia.PREF_SOURCE_URL, null); + String mediaType = pref.getString(ExternalMedia.PREF_MEDIA_TYPE, null); + if (source != null && mediaType != null) { + int position = pref.getInt(ExternalMedia.PREF_POSITION, 0); + long lastPlayedTime = pref.getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, 0); + result = new ExternalMedia(source, MediaType.valueOf(mediaType), + position, lastPlayedTime); + } + return result; } } |