Merge pull request #1260 from stevomit/rewind-after-pause

Rewind after pause feature
This commit is contained in:
Tom Hennen 2015-10-17 11:50:14 -04:00
commit bcef62d11e
17 changed files with 308 additions and 51 deletions

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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());
}
}
}

View File

@ -156,7 +156,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);

View File

@ -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 = 30000;
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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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()
);
}
}

View File

@ -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;

View File

@ -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.
*/

View File

@ -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());
}
}

View File

@ -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(1);
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(3);
public static final long MEDIUM_REWIND = TimeUnit.SECONDS.toMillis(10);
public static final long LONG_REWIND = TimeUnit.SECONDS.toMillis(20);
/**
* @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;
}
}
}

View File

@ -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
@ -144,6 +149,11 @@ public class ExternalMedia implements Playable {
return position;
}
@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();
}
@ -187,6 +199,11 @@ public class ExternalMedia implements Playable {
duration = newDuration;
}
@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) {

View File

@ -81,6 +81,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,13 +121,22 @@ 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.
*/
@ -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;
}
}