diff options
17 files changed, 308 insertions, 51 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java new file mode 100644 index 000000000..80dded59f --- /dev/null +++ b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java @@ -0,0 +1,49 @@ +package de.test.antennapod.entities; + +import android.annotation.SuppressLint; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.test.InstrumentationTestCase; + +import de.danoeh.antennapod.core.feed.MediaType; +import de.danoeh.antennapod.core.util.playback.ExternalMedia; + +/** + * Tests for {@link ExternalMedia} entity. + */ +public class ExternalMediaTest extends InstrumentationTestCase { + + private static final int NOT_SET = -1; + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + clearSharedPrefs(); + } + + @SuppressLint("CommitPrefEdits") + private void clearSharedPrefs() { + SharedPreferences prefs = getDefaultSharedPrefs(); + SharedPreferences.Editor editor = prefs.edit(); + editor.clear(); + editor.commit(); + } + + private SharedPreferences getDefaultSharedPrefs() { + return PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getTargetContext()); + } + + public void testSaveCurrentPositionUpdatesPreferences() { + final int POSITION = 50; + final int LAST_PLAYED_TIME = 1650; + + assertEquals(NOT_SET, getDefaultSharedPrefs().getInt(ExternalMedia.PREF_POSITION, NOT_SET)); + assertEquals(NOT_SET, getDefaultSharedPrefs().getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, NOT_SET)); + + ExternalMedia media = new ExternalMedia("source", MediaType.AUDIO); + media.saveCurrentPosition(getDefaultSharedPrefs(), POSITION, LAST_PLAYED_TIME); + + assertEquals(POSITION, getDefaultSharedPrefs().getInt(ExternalMedia.PREF_POSITION, NOT_SET)); + assertEquals(LAST_PLAYED_TIME, getDefaultSharedPrefs().getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, NOT_SET)); + } +} diff --git a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java index 13a0e3f78..5836bb699 100644 --- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java +++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java @@ -173,7 +173,7 @@ public class FeedHandlerTest extends InstrumentationTestCase { feed.getItems().add(item); if (withFeedMedia) { item.setMedia(new FeedMedia(0, item, 4711, 0, 1024*1024, "audio/mp3", null, "http://example.com/media-" + i, - false, null, 0)); + false, null, 0, 0)); } } diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java index 96bcd6452..d7a170c17 100644 --- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java +++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java @@ -121,7 +121,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase { f.setItems(new ArrayList<>()); FeedItem i = new FeedItem(0, "t", "i", "l", new Date(), FeedItem.UNPLAYED, f); f.getItems().add(i); - FeedMedia media = new FeedMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0); + FeedMedia media = new FeedMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0, 0); i.setMedia(media); PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java index 7b84c288a..afdaeead0 100644 --- a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java +++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java @@ -117,7 +117,7 @@ public class DBCleanupTests extends InstrumentationTestCase { File f = new File(destFolder, "file " + i); assertTrue(f.createNewFile()); files.add(f); - item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, playbackCompletionDate, 0)); + item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, playbackCompletionDate, 0, 0)); items.add(item); } diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java index 38bc0c380..18a8d63d1 100644 --- a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java +++ b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java @@ -93,7 +93,7 @@ public class DBNullCleanupAlgorithmTest extends InstrumentationTestCase { assertTrue(f.createNewFile()); files.add(f); item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, - new Date(NUM_ITEMS - i), 0)); + new Date(NUM_ITEMS - i), 0, 0)); items.add(item); } diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java index 585e27e0b..f5240b873 100644 --- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java +++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java @@ -59,6 +59,37 @@ public class DBWriterTest extends InstrumentationTestCase { adapter.close(); } + public void testSetFeedMediaPlaybackInformation() throws IOException, ExecutionException, InterruptedException { + final int POSITION = 50; + final long LAST_PLAYED_TIME = 1000; + final int PLAYED_DURATION = 60; + final int DURATION = 100; + + Feed feed = new Feed("url", new Date(), "title"); + List<FeedItem> items = new ArrayList<>(); + feed.setItems(items); + FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, feed); + items.add(item); + FeedMedia media = new FeedMedia(0, item, DURATION, 1, 1, "mime_type", "dummy path", "download_url", true, null, 0, 0); + item.setMedia(media); + + DBWriter.setFeedItem(item).get(); + + media.setPosition(POSITION); + media.setLastPlayedTime(LAST_PLAYED_TIME); + media.setPlayedDuration(PLAYED_DURATION); + + DBWriter.setFeedMediaPlaybackInformation(item.getMedia()).get(); + + FeedItem itemFromDb = DBReader.getFeedItem(item.getId()); + FeedMedia mediaFromDb = itemFromDb.getMedia(); + + assertEquals(POSITION, mediaFromDb.getPosition()); + assertEquals(LAST_PLAYED_TIME, mediaFromDb.getLastPlayedTime()); + assertEquals(PLAYED_DURATION, mediaFromDb.getPlayedDuration()); + assertEquals(DURATION, mediaFromDb.getDuration()); + } + public void testDeleteFeedMediaOfItemFileExists() throws IOException, ExecutionException, InterruptedException { File dest = new File(getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER), "testFile"); @@ -69,7 +100,7 @@ public class DBWriterTest extends InstrumentationTestCase { feed.setItems(items); FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, feed); - FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", true, null, 0); + FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", true, null, 0, 0); item.setMedia(media); items.add(item); @@ -113,7 +144,7 @@ public class DBWriterTest extends InstrumentationTestCase { assertTrue(enc.createNewFile()); itemFiles.add(enc); - FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0); + FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0, 0); item.setMedia(media); item.setChapters(new ArrayList<Chapter>()); @@ -180,7 +211,7 @@ public class DBWriterTest extends InstrumentationTestCase { assertTrue(enc.createNewFile()); itemFiles.add(enc); - FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0); + FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0, 0); item.setMedia(media); } @@ -386,7 +417,7 @@ public class DBWriterTest extends InstrumentationTestCase { File enc = new File(destFolder, "file " + i); itemFiles.add(enc); - FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0); + FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0); item.setMedia(media); } @@ -458,7 +489,7 @@ public class DBWriterTest extends InstrumentationTestCase { File enc = new File(destFolder, "file " + i); itemFiles.add(enc); - FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0); + FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0); item.setMedia(media); } @@ -499,7 +530,7 @@ public class DBWriterTest extends InstrumentationTestCase { Feed feed = new Feed("url", new Date(), "title"); feed.setItems(new ArrayList<FeedItem>()); FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed); - FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0); + FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0, 0); feed.getItems().add(item); item.setMedia(media); PodDBAdapter adapter = PodDBAdapter.getInstance(); @@ -796,5 +827,4 @@ public class DBWriterTest extends InstrumentationTestCase { assertTrue(item.isPlayed()); } } - } diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java index 973426841..a8f62706b 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java @@ -152,7 +152,7 @@ public class UITestUtils { items.add(item); File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3"); - item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0)); + item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0, 0)); } feed.setItems(items); diff --git a/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java b/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java new file mode 100644 index 000000000..445d659ac --- /dev/null +++ b/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java @@ -0,0 +1,51 @@ +package de.test.antennapod.util; + +import junit.framework.*; + +import de.danoeh.antennapod.core.util.*; + +/** + * Tests for {@link RewindAfterPauseUtils}. + */ +public class RewindAfterPauseUtilTest extends TestCase { + + public void testCalculatePositionWithRewindNoRewind() { + final int ORIGINAL_POSITION = 10000; + long lastPlayed = System.currentTimeMillis(); + int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed); + + assertEquals(ORIGINAL_POSITION, position); + } + + public void testCalculatePositionWithRewindSmallRewind() { + final int ORIGINAL_POSITION = 10000; + long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_SHORT_REWIND - 1000; + int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed); + + assertEquals(ORIGINAL_POSITION - RewindAfterPauseUtils.SHORT_REWIND, position); + } + + public void testCalculatePositionWithRewindMediumRewind() { + final int ORIGINAL_POSITION = 10000; + long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_MEDIUM_REWIND - 1000; + int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed); + + assertEquals(ORIGINAL_POSITION - RewindAfterPauseUtils.MEDIUM_REWIND, position); + } + + public void testCalculatePositionWithRewindLongRewind() { + final int ORIGINAL_POSITION = 10000; + long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_LONG_REWIND - 1000; + int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed); + + assertEquals(ORIGINAL_POSITION - RewindAfterPauseUtils.LONG_REWIND, position); + } + + public void testCalculatePositionWithRewindNegativeNumber() { + final int ORIGINAL_POSITION = 100; + long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_LONG_REWIND - 1000; + int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed); + + assertEquals(0, position); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index feb0681f2..e15f4c85e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.danoeh.antennapod" - android:versionCode="1040001" - android:versionName="1.4.0.1"> + android:versionCode="1040002" + android:versionName="1.4.0.2"> <!-- Version code schema: "1.2.3-SNAPSHOT" -> 1020300 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; } } |