summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/util
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2013-08-04 15:50:57 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2013-08-04 15:50:57 +0200
commit9f36cecf4bbcd4a8fe1d199c25758096b22e08f6 (patch)
treebed2c03e6190f4a4313d603ecaf37b7006ffa670 /src/de/danoeh/antennapod/util
parente28229a29c1f60657c82f78507120b215167f039 (diff)
downloadAntennaPod-9f36cecf4bbcd4a8fe1d199c25758096b22e08f6.zip
Ported PlaybackController to DB*-classes
Diffstat (limited to 'src/de/danoeh/antennapod/util')
-rw-r--r--src/de/danoeh/antennapod/util/playback/Playable.java521
-rw-r--r--src/de/danoeh/antennapod/util/playback/PlaybackController.java1333
2 files changed, 944 insertions, 910 deletions
diff --git a/src/de/danoeh/antennapod/util/playback/Playable.java b/src/de/danoeh/antennapod/util/playback/Playable.java
index 0404379e2..39c6c6b83 100644
--- a/src/de/danoeh/antennapod/util/playback/Playable.java
+++ b/src/de/danoeh/antennapod/util/playback/Playable.java
@@ -4,6 +4,8 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
+import android.content.Context;
+import de.danoeh.antennapod.storage.DBReader;
import org.apache.commons.io.IOUtils;
import android.content.SharedPreferences;
@@ -13,261 +15,274 @@ import android.util.Log;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.Feed;
-import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
-/** Interface for objects that can be played by the PlaybackService. */
+/**
+ * Interface for objects that can be played by the PlaybackService.
+ */
public interface Playable extends Parcelable,
- ImageLoader.ImageWorkerTaskResource {
-
- /**
- * Save information about the playable in a preference so that it can be
- * restored later via PlayableUtils.createInstanceFromPreferences.
- * Implementations must NOT call commit() after they have written the values
- * to the preferences file.
- */
- public void writeToPreferences(SharedPreferences.Editor prefEditor);
-
- /**
- * This method is called from a separate thread by the PlaybackService.
- * Playable objects should load their metadata in this method. This method
- * should execute as quickly as possible and NOT load chapter marks if no
- * local file is available.
- */
- public void loadMetadata() throws PlayableException;
-
- /**
- * This method is called from a separate thread by the PlaybackService.
- * Playable objects should load their chapter marks in this method if no
- * local file was available when loadMetadata() was called.
- */
- public void loadChapterMarks();
-
- /** Returns the title of the episode that this playable represents */
- public String getEpisodeTitle();
-
- /**
- * Loads shownotes. If the shownotes have to be loaded from a file or from a
- * database, it should be done in a separate thread. After the shownotes
- * have been loaded, callback.onShownotesLoaded should be called.
- */
- public void loadShownotes(ShownoteLoaderCallback callback);
-
- /**
- * Returns a list of chapter marks or null if this Playable has no chapters.
- */
- public List<Chapter> getChapters();
-
- /** Returns a link to a website that is meant to be shown in a browser */
- public String getWebsiteLink();
-
- public String getPaymentLink();
-
- /** Returns the title of the feed this Playable belongs to. */
- public String getFeedTitle();
-
- /**
- * Returns a unique identifier, for example a file url or an ID from a
- * database.
- */
- public Object getIdentifier();
-
- /** Return duration of object or 0 if duration is unknown. */
- public int getDuration();
-
- /** Return position of object or 0 if position is unknown. */
- public int getPosition();
-
- /**
- * Returns the type of media. This method should return the correct value
- * BEFORE loadMetadata() is called.
- */
- public MediaType getMediaType();
-
- /**
- * Returns an url to a local file that can be played or null if this file
- * does not exist.
- */
- public String getLocalMediaUrl();
-
- /**
- * Returns an url to a file that can be streamed by the player or null if
- * this url is not known.
- */
- public String getStreamUrl();
-
- /**
- * Returns true if a local file that can be played is available. getFileUrl
- * MUST return a non-null string if this method returns true.
- */
- public boolean localFileAvailable();
-
- /**
- * Returns true if a streamable file is available. getStreamUrl MUST return
- * a non-null string if this method returns true.
- */
- public boolean streamAvailable();
-
- /**
- * Saves the current position of this object. Implementations can use the
- * provided SharedPreference to save this information and retrieve it later
- * via PlayableUtils.createInstanceFromPreferences.
- */
- public void saveCurrentPosition(SharedPreferences pref, int newPosition);
-
- public void setPosition(int newPosition);
-
- public void setDuration(int newDuration);
-
- /** Is called by the PlaybackService when playback starts. */
- public void onPlaybackStart();
-
- /** Is called by the PlaybackService when playback is completed. */
- public void onPlaybackCompleted();
-
- /**
- * Returns an integer that must be unique among all Playable classes. The
- * return value is later used by PlayableUtils to determine the type of the
- * Playable object that is restored.
- */
- public int getPlayableType();
-
- public void setChapters(List<Chapter> chapters);
-
- /** Provides utility methods for Playable objects. */
- public static class PlayableUtils {
- private static final String TAG = "PlayableUtils";
-
- /**
- * Restores a playable object from a sharedPreferences file.
- *
- * @param type
- * An integer that represents the type of the Playable object
- * that is restored.
- * @param pref
- * The SharedPreferences file from which the Playable object
- * is restored
- * @return The restored Playable object
- */
- public static Playable createInstanceFromPreferences(int type,
- SharedPreferences pref) {
- // ADD new Playable types here:
- switch (type) {
- case FeedMedia.PLAYABLE_TYPE_FEEDMEDIA:
- long feedId = pref.getLong(FeedMedia.PREF_FEED_ID, -1);
- long mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1);
- if (feedId != -1 && mediaId != -1) {
- Feed feed = FeedManager.getInstance().getFeed(feedId);
- if (feed != null) {
- return FeedManager.getInstance().getFeedMedia(mediaId,
- feed);
- }
- }
- 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);
- }
- break;
- }
- Log.e(TAG, "Could not restore Playable object from preferences");
- return null;
- }
- }
-
- public static class PlayableException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public PlayableException() {
- super();
- }
-
- public PlayableException(String detailMessage, Throwable throwable) {
- super(detailMessage, throwable);
- }
-
- public PlayableException(String detailMessage) {
- super(detailMessage);
- }
-
- public PlayableException(Throwable throwable) {
- super(throwable);
- }
-
- }
-
- public static interface ShownoteLoaderCallback {
- void onShownotesLoaded(String shownotes);
- }
-
- /** Uses local file as image resource if it is available. */
- public static class DefaultPlayableImageLoader implements
- ImageLoader.ImageWorkerTaskResource {
- private Playable playable;
-
- public DefaultPlayableImageLoader(Playable playable) {
- if (playable == null) {
- throw new IllegalArgumentException("Playable must not be null");
- }
- this.playable = playable;
- }
-
- @Override
- public InputStream openImageInputStream() {
- if (playable.localFileAvailable()
- && playable.getLocalMediaUrl() != null) {
- MediaMetadataRetriever mmr = new MediaMetadataRetriever();
- try {
- mmr.setDataSource(playable.getLocalMediaUrl());
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- return null;
- }
- byte[] imgData = mmr.getEmbeddedPicture();
- if (imgData != null) {
- return new PublicByteArrayInputStream(imgData);
-
- }
- }
- return null;
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- return playable.getLocalMediaUrl();
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- if (input instanceof PublicByteArrayInputStream) {
- IOUtils.closeQuietly(input);
- byte[] imgData = ((PublicByteArrayInputStream) input)
- .getByteArray();
- if (imgData != null) {
- ByteArrayInputStream out = new ByteArrayInputStream(imgData);
- return out;
- }
-
- }
- return null;
- }
-
- private static class PublicByteArrayInputStream extends
- ByteArrayInputStream {
- public PublicByteArrayInputStream(byte[] buf) {
- super(buf);
- }
-
- public byte[] getByteArray() {
- return buf;
- }
- }
- }
+ ImageLoader.ImageWorkerTaskResource {
+
+ /**
+ * Save information about the playable in a preference so that it can be
+ * restored later via PlayableUtils.createInstanceFromPreferences.
+ * Implementations must NOT call commit() after they have written the values
+ * to the preferences file.
+ */
+ public void writeToPreferences(SharedPreferences.Editor prefEditor);
+
+ /**
+ * This method is called from a separate thread by the PlaybackService.
+ * Playable objects should load their metadata in this method. This method
+ * should execute as quickly as possible and NOT load chapter marks if no
+ * local file is available.
+ */
+ public void loadMetadata() throws PlayableException;
+
+ /**
+ * This method is called from a separate thread by the PlaybackService.
+ * Playable objects should load their chapter marks in this method if no
+ * local file was available when loadMetadata() was called.
+ */
+ public void loadChapterMarks();
+
+ /**
+ * Returns the title of the episode that this playable represents
+ */
+ public String getEpisodeTitle();
+
+ /**
+ * Loads shownotes. If the shownotes have to be loaded from a file or from a
+ * database, it should be done in a separate thread. After the shownotes
+ * have been loaded, callback.onShownotesLoaded should be called.
+ */
+ public void loadShownotes(ShownoteLoaderCallback callback);
+
+ /**
+ * Returns a list of chapter marks or null if this Playable has no chapters.
+ */
+ public List<Chapter> getChapters();
+
+ /**
+ * Returns a link to a website that is meant to be shown in a browser
+ */
+ public String getWebsiteLink();
+
+ public String getPaymentLink();
+
+ /**
+ * Returns the title of the feed this Playable belongs to.
+ */
+ public String getFeedTitle();
+
+ /**
+ * Returns a unique identifier, for example a file url or an ID from a
+ * database.
+ */
+ public Object getIdentifier();
+
+ /**
+ * Return duration of object or 0 if duration is unknown.
+ */
+ public int getDuration();
+
+ /**
+ * Return position of object or 0 if position is unknown.
+ */
+ public int getPosition();
+
+ /**
+ * Returns the type of media. This method should return the correct value
+ * BEFORE loadMetadata() is called.
+ */
+ public MediaType getMediaType();
+
+ /**
+ * Returns an url to a local file that can be played or null if this file
+ * does not exist.
+ */
+ public String getLocalMediaUrl();
+
+ /**
+ * Returns an url to a file that can be streamed by the player or null if
+ * this url is not known.
+ */
+ public String getStreamUrl();
+
+ /**
+ * Returns true if a local file that can be played is available. getFileUrl
+ * MUST return a non-null string if this method returns true.
+ */
+ public boolean localFileAvailable();
+
+ /**
+ * Returns true if a streamable file is available. getStreamUrl MUST return
+ * a non-null string if this method returns true.
+ */
+ public boolean streamAvailable();
+
+ /**
+ * Saves the current position of this object. Implementations can use the
+ * provided SharedPreference to save this information and retrieve it later
+ * via PlayableUtils.createInstanceFromPreferences.
+ */
+ public void saveCurrentPosition(SharedPreferences pref, int newPosition);
+
+ public void setPosition(int newPosition);
+
+ public void setDuration(int newDuration);
+
+ /**
+ * Is called by the PlaybackService when playback starts.
+ */
+ public void onPlaybackStart();
+
+ /**
+ * Is called by the PlaybackService when playback is completed.
+ */
+ public void onPlaybackCompleted();
+
+ /**
+ * Returns an integer that must be unique among all Playable classes. The
+ * return value is later used by PlayableUtils to determine the type of the
+ * Playable object that is restored.
+ */
+ public int getPlayableType();
+
+ public void setChapters(List<Chapter> chapters);
+
+ /**
+ * Provides utility methods for Playable objects.
+ */
+ public static class PlayableUtils {
+ private static final String TAG = "PlayableUtils";
+
+ /**
+ * Restores a playable object from a sharedPreferences file. This method might load data from the database,
+ * depending on the type of playable that was restored.
+ *
+ * @param type An integer that represents the type of the Playable object
+ * that is restored.
+ * @param pref The SharedPreferences file from which the Playable object
+ * is restored
+ * @return The restored Playable object
+ */
+ public static Playable createInstanceFromPreferences(Context context, int type,
+ SharedPreferences pref) {
+ // 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(context, mediaId);
+ }
+ 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);
+ }
+ break;
+ }
+ Log.e(TAG, "Could not restore Playable object from preferences");
+ return null;
+ }
+ }
+
+ public static class PlayableException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public PlayableException() {
+ super();
+ }
+
+ public PlayableException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ public PlayableException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ public PlayableException(Throwable throwable) {
+ super(throwable);
+ }
+
+ }
+
+ public static interface ShownoteLoaderCallback {
+ void onShownotesLoaded(String shownotes);
+ }
+
+ /**
+ * Uses local file as image resource if it is available.
+ */
+ public static class DefaultPlayableImageLoader implements
+ ImageLoader.ImageWorkerTaskResource {
+ private Playable playable;
+
+ public DefaultPlayableImageLoader(Playable playable) {
+ if (playable == null) {
+ throw new IllegalArgumentException("Playable must not be null");
+ }
+ this.playable = playable;
+ }
+
+ @Override
+ public InputStream openImageInputStream() {
+ if (playable.localFileAvailable()
+ && playable.getLocalMediaUrl() != null) {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ try {
+ mmr.setDataSource(playable.getLocalMediaUrl());
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ return null;
+ }
+ byte[] imgData = mmr.getEmbeddedPicture();
+ if (imgData != null) {
+ return new PublicByteArrayInputStream(imgData);
+
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getImageLoaderCacheKey() {
+ return playable.getLocalMediaUrl();
+ }
+
+ @Override
+ public InputStream reopenImageInputStream(InputStream input) {
+ if (input instanceof PublicByteArrayInputStream) {
+ IOUtils.closeQuietly(input);
+ byte[] imgData = ((PublicByteArrayInputStream) input)
+ .getByteArray();
+ if (imgData != null) {
+ ByteArrayInputStream out = new ByteArrayInputStream(imgData);
+ return out;
+ }
+
+ }
+ return null;
+ }
+
+ private static class PublicByteArrayInputStream extends
+ ByteArrayInputStream {
+ public PublicByteArrayInputStream(byte[] buf) {
+ super(buf);
+ }
+
+ public byte[] getByteArray() {
+ return buf;
+ }
+ }
+ }
}
diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
index 1d7057b10..847e08b4a 100644
--- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java
+++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
@@ -16,6 +16,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
+import android.os.AsyncTask;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
@@ -28,7 +29,6 @@ import android.widget.TextView;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.Chapter;
-import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
import de.danoeh.antennapod.service.PlaybackService;
@@ -42,661 +42,680 @@ import de.danoeh.antennapod.util.playback.Playable.PlayableUtils;
* control playback instead of communicating with the PlaybackService directly.
*/
public abstract class PlaybackController {
- private static final String TAG = "PlaybackController";
-
- static final int DEFAULT_SEEK_DELTA = 30000;
- public static final int INVALID_TIME = -1;
-
- private Activity activity;
-
- private PlaybackService playbackService;
- private Playable media;
- private PlayerStatus status;
-
- private ScheduledThreadPoolExecutor schedExecutor;
- private static final int SCHED_EX_POOLSIZE = 1;
-
- protected MediaPositionObserver positionObserver;
- protected ScheduledFuture positionObserverFuture;
-
- private boolean mediaInfoLoaded = false;
- private boolean released = false;
-
- /**
- * True if controller should reinit playback service if 'pause' button is
- * pressed.
- */
- private boolean reinitOnPause;
-
- public PlaybackController(Activity activity, boolean reinitOnPause) {
- this.activity = activity;
- this.reinitOnPause = reinitOnPause;
- schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOLSIZE,
- new ThreadFactory() {
-
- @Override
- public Thread newThread(Runnable r) {
- Thread t = new Thread(r);
- t.setPriority(Thread.MIN_PRIORITY);
- return t;
- }
- }, new RejectedExecutionHandler() {
-
- @Override
- public void rejectedExecution(Runnable r,
- ThreadPoolExecutor executor) {
- Log.w(TAG,
- "Rejected execution of runnable in schedExecutor");
- }
- });
- }
-
- /**
- * Creates a new connection to the playbackService. Should be called in the
- * activity's onResume() method.
- */
- public void init() {
- activity.registerReceiver(statusUpdate, new IntentFilter(
- PlaybackService.ACTION_PLAYER_STATUS_CHANGED));
-
- activity.registerReceiver(notificationReceiver, new IntentFilter(
- PlaybackService.ACTION_PLAYER_NOTIFICATION));
-
- activity.registerReceiver(shutdownReceiver, new IntentFilter(
- PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
-
- if (!released) {
- bindToService();
- } else {
- throw new IllegalStateException(
- "Can't call init() after release() has been called");
- }
- }
-
- /**
- * Should be called if the PlaybackController is no longer needed, for
- * example in the activity's onStop() method.
- */
- public void release() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Releasing PlaybackController");
-
- try {
- activity.unregisterReceiver(statusUpdate);
- } catch (IllegalArgumentException e) {
- // ignore
- }
-
- try {
- activity.unregisterReceiver(notificationReceiver);
- } catch (IllegalArgumentException e) {
- // ignore
- }
-
- try {
- activity.unbindService(mConnection);
- } catch (IllegalArgumentException e) {
- // ignore
- }
-
- try {
- activity.unregisterReceiver(shutdownReceiver);
- } catch (IllegalArgumentException e) {
- // ignore
- }
- cancelPositionObserver();
- schedExecutor.shutdownNow();
- media = null;
- released = true;
-
- }
-
- /** Should be called in the activity's onPause() method. */
- public void pause() {
- mediaInfoLoaded = false;
- if (playbackService != null && playbackService.isPlayingVideo()) {
- playbackService.pause(true, true);
- }
- }
-
- /**
- * Tries to establish a connection to the PlaybackService. If it isn't
- * running, the PlaybackService will be started with the last played media
- * as the arguments of the launch intent.
- */
- private void bindToService() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Trying to connect to service");
- Intent serviceIntent = getPlayLastPlayedMediaIntent();
- boolean bound = false;
- if (!PlaybackService.isRunning) {
- if (serviceIntent != null) {
- activity.startService(serviceIntent);
- bound = activity.bindService(serviceIntent, mConnection, 0);
- } else {
- status = PlayerStatus.STOPPED;
- setupGUI();
- handleStatus();
- }
- } else {
- if (AppConfig.DEBUG)
- Log.d(TAG,
- "PlaybackService is running, trying to connect without start command.");
- bound = activity.bindService(new Intent(activity,
- PlaybackService.class), mConnection, 0);
- }
- if (AppConfig.DEBUG)
- Log.d(TAG, "Result for service binding: " + bound);
- }
-
- /**
- * Returns an intent that starts the PlaybackService and plays the last
- * played media or null if no last played media could be found.
- */
- private Intent getPlayLastPlayedMediaIntent() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Trying to restore last played media");
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(activity.getApplicationContext());
- long currentlyPlayingMedia = PlaybackPreferences
- .getCurrentlyPlayingMedia();
- if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) {
- Playable media = PlayableUtils.createInstanceFromPreferences(
- (int) currentlyPlayingMedia, prefs);
- if (media != null) {
- Intent serviceIntent = new Intent(activity,
- PlaybackService.class);
- serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
- serviceIntent.putExtra(
- PlaybackService.EXTRA_START_WHEN_PREPARED, false);
- serviceIntent.putExtra(
- PlaybackService.EXTRA_PREPARE_IMMEDIATELY, false);
- boolean fileExists = media.localFileAvailable();
- boolean lastIsStream = PlaybackPreferences
- .getCurrentEpisodeIsStream();
- if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
- DBTasks.notifyMissingFeedMediaFile(
+ private static final String TAG = "PlaybackController";
+
+ static final int DEFAULT_SEEK_DELTA = 30000;
+ public static final int INVALID_TIME = -1;
+
+ private Activity activity;
+
+ private PlaybackService playbackService;
+ private Playable media;
+ private PlayerStatus status;
+
+ private ScheduledThreadPoolExecutor schedExecutor;
+ private static final int SCHED_EX_POOLSIZE = 1;
+
+ protected MediaPositionObserver positionObserver;
+ protected ScheduledFuture positionObserverFuture;
+
+ private boolean mediaInfoLoaded = false;
+ private boolean released = false;
+
+ /**
+ * True if controller should reinit playback service if 'pause' button is
+ * pressed.
+ */
+ private boolean reinitOnPause;
+
+ public PlaybackController(Activity activity, boolean reinitOnPause) {
+ this.activity = activity;
+ this.reinitOnPause = reinitOnPause;
+ schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOLSIZE,
+ new ThreadFactory() {
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r);
+ t.setPriority(Thread.MIN_PRIORITY);
+ return t;
+ }
+ }, new RejectedExecutionHandler() {
+
+ @Override
+ public void rejectedExecution(Runnable r,
+ ThreadPoolExecutor executor) {
+ Log.w(TAG,
+ "Rejected execution of runnable in schedExecutor");
+ }
+ }
+ );
+ }
+
+ /**
+ * Creates a new connection to the playbackService. Should be called in the
+ * activity's onResume() method.
+ */
+ public void init() {
+ activity.registerReceiver(statusUpdate, new IntentFilter(
+ PlaybackService.ACTION_PLAYER_STATUS_CHANGED));
+
+ activity.registerReceiver(notificationReceiver, new IntentFilter(
+ PlaybackService.ACTION_PLAYER_NOTIFICATION));
+
+ activity.registerReceiver(shutdownReceiver, new IntentFilter(
+ PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+
+ if (!released) {
+ bindToService();
+ } else {
+ throw new IllegalStateException(
+ "Can't call init() after release() has been called");
+ }
+ }
+
+ /**
+ * Should be called if the PlaybackController is no longer needed, for
+ * example in the activity's onStop() method.
+ */
+ public void release() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Releasing PlaybackController");
+
+ try {
+ activity.unregisterReceiver(statusUpdate);
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+
+ try {
+ activity.unregisterReceiver(notificationReceiver);
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+
+ try {
+ activity.unbindService(mConnection);
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+
+ try {
+ activity.unregisterReceiver(shutdownReceiver);
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ cancelPositionObserver();
+ schedExecutor.shutdownNow();
+ media = null;
+ released = true;
+
+ }
+
+ /**
+ * Should be called in the activity's onPause() method.
+ */
+ public void pause() {
+ mediaInfoLoaded = false;
+ if (playbackService != null && playbackService.isPlayingVideo()) {
+ playbackService.pause(true, true);
+ }
+ }
+
+ /**
+ * Tries to establish a connection to the PlaybackService. If it isn't
+ * running, the PlaybackService will be started with the last played media
+ * as the arguments of the launch intent.
+ */
+ private void bindToService() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Trying to connect to service");
+ AsyncTask<Void, Void, Intent> intentLoader = new AsyncTask<Void, Void, Intent>() {
+ @Override
+ protected Intent doInBackground(Void... voids) {
+ return getPlayLastPlayedMediaIntent();
+ }
+
+ @Override
+ protected void onPostExecute(Intent serviceIntent) {
+ boolean bound = false;
+ if (!PlaybackService.isRunning) {
+ if (serviceIntent != null) {
+ activity.startService(serviceIntent);
+ bound = activity.bindService(serviceIntent, mConnection, 0);
+ } else {
+ status = PlayerStatus.STOPPED;
+ setupGUI();
+ handleStatus();
+ }
+ } else {
+ if (AppConfig.DEBUG)
+ Log.d(TAG,
+ "PlaybackService is running, trying to connect without start command.");
+ bound = activity.bindService(new Intent(activity,
+ PlaybackService.class), mConnection, 0);
+ }
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Result for service binding: " + bound);
+ }
+ };
+ intentLoader.execute();
+ }
+
+ /**
+ * Returns an intent that starts the PlaybackService and plays the last
+ * played media or null if no last played media could be found.
+ */
+ private Intent getPlayLastPlayedMediaIntent() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Trying to restore last played media");
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(activity.getApplicationContext());
+ long currentlyPlayingMedia = PlaybackPreferences
+ .getCurrentlyPlayingMedia();
+ if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) {
+ Playable media = PlayableUtils.createInstanceFromPreferences(activity,
+ (int) currentlyPlayingMedia, prefs);
+ if (media != null) {
+ Intent serviceIntent = new Intent(activity,
+ PlaybackService.class);
+ serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
+ serviceIntent.putExtra(
+ PlaybackService.EXTRA_START_WHEN_PREPARED, false);
+ serviceIntent.putExtra(
+ PlaybackService.EXTRA_PREPARE_IMMEDIATELY, false);
+ boolean fileExists = media.localFileAvailable();
+ boolean lastIsStream = PlaybackPreferences
+ .getCurrentEpisodeIsStream();
+ if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
+ DBTasks.notifyMissingFeedMediaFile(
activity, (FeedMedia) media);
- }
- serviceIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
- lastIsStream || !fileExists);
- return serviceIntent;
- }
- }
- if (AppConfig.DEBUG)
- Log.d(TAG, "No last played media found");
- return null;
- }
-
- public abstract void setupGUI();
-
- private void setupPositionObserver() {
- if ((positionObserverFuture != null && positionObserverFuture
- .isCancelled())
- || (positionObserverFuture != null && positionObserverFuture
- .isDone()) || positionObserverFuture == null) {
-
- if (AppConfig.DEBUG)
- Log.d(TAG, "Setting up position observer");
- positionObserver = new MediaPositionObserver();
- positionObserverFuture = schedExecutor.scheduleWithFixedDelay(
- positionObserver, MediaPositionObserver.WAITING_INTERVALL,
- MediaPositionObserver.WAITING_INTERVALL,
- TimeUnit.MILLISECONDS);
- }
- }
-
- private void cancelPositionObserver() {
- if (positionObserverFuture != null) {
- boolean result = positionObserverFuture.cancel(true);
- if (AppConfig.DEBUG)
- Log.d(TAG, "PositionObserver cancelled. Result: " + result);
- }
- }
-
- public abstract void onPositionObserverUpdate();
-
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- playbackService = ((PlaybackService.LocalBinder) service)
- .getService();
-
- queryService();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Connection to Service established");
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- playbackService = null;
- if (AppConfig.DEBUG)
- Log.d(TAG, "Disconnected from Service");
-
- }
- };
-
- protected BroadcastReceiver statusUpdate = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Received statusUpdate Intent.");
- if (isConnectedToPlaybackService()) {
- status = playbackService.getStatus();
- handleStatus();
- } else {
- Log.w(TAG,
- "Couldn't receive status update: playbackService was null");
- bindToService();
- }
- }
- };
-
- protected BroadcastReceiver notificationReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (isConnectedToPlaybackService()) {
- int type = intent.getIntExtra(
- PlaybackService.EXTRA_NOTIFICATION_TYPE, -1);
- int code = intent.getIntExtra(
- PlaybackService.EXTRA_NOTIFICATION_CODE, -1);
- if (code != -1 && type != -1) {
- switch (type) {
- case PlaybackService.NOTIFICATION_TYPE_ERROR:
- handleError(code);
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_UPDATE:
- float progress = ((float) code) / 100;
- onBufferUpdate(progress);
- break;
- case PlaybackService.NOTIFICATION_TYPE_RELOAD:
- cancelPositionObserver();
- mediaInfoLoaded = false;
- onReloadNotification(intent.getIntExtra(
- PlaybackService.EXTRA_NOTIFICATION_CODE, -1));
- queryService();
-
- break;
- case PlaybackService.NOTIFICATION_TYPE_SLEEPTIMER_UPDATE:
- onSleepTimerUpdate();
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_START:
- onBufferStart();
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_END:
- onBufferEnd();
- break;
- case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END:
- onPlaybackEnd();
- break;
- }
-
- } else {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Bad arguments. Won't handle intent");
- }
- } else {
- bindToService();
- }
- }
-
- };
-
- private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (isConnectedToPlaybackService()) {
- if (intent.getAction().equals(
- PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- release();
- onShutdownNotification();
- }
- }
- }
- };
-
- public abstract void onShutdownNotification();
-
- /** Called when the currently displayed information should be refreshed. */
- public abstract void onReloadNotification(int code);
-
- public abstract void onBufferStart();
-
- public abstract void onBufferEnd();
-
- public abstract void onBufferUpdate(float progress);
-
- public abstract void onSleepTimerUpdate();
-
- public abstract void handleError(int code);
-
- public abstract void onPlaybackEnd();
-
- /**
- * Is called whenever the PlaybackService changes it's status. This method
- * should be used to update the GUI or start/cancel background threads.
- */
- private void handleStatus() {
- TypedArray res = activity.obtainStyledAttributes(new int[] {
- R.attr.av_play, R.attr.av_pause });
- final int playResource = res.getResourceId(0, R.drawable.av_play);
- final int pauseResource = res.getResourceId(1, R.drawable.av_pause);
- res.recycle();
-
- switch (status) {
-
- case ERROR:
- postStatusMsg(R.string.player_error_msg);
- break;
- case PAUSED:
- clearStatusMsg();
- checkMediaInfoLoaded();
- cancelPositionObserver();
- updatePlayButtonAppearance(playResource);
- break;
- case PLAYING:
- clearStatusMsg();
- checkMediaInfoLoaded();
- setupPositionObserver();
- updatePlayButtonAppearance(pauseResource);
- break;
- case PREPARING:
- postStatusMsg(R.string.player_preparing_msg);
- checkMediaInfoLoaded();
- if (playbackService != null) {
- if (playbackService.isStartWhenPrepared()) {
- updatePlayButtonAppearance(pauseResource);
- } else {
- updatePlayButtonAppearance(playResource);
- }
- }
- break;
- case STOPPED:
- postStatusMsg(R.string.player_stopped_msg);
- break;
- case PREPARED:
- checkMediaInfoLoaded();
- postStatusMsg(R.string.player_ready_msg);
- updatePlayButtonAppearance(playResource);
- break;
- case SEEKING:
- postStatusMsg(R.string.player_seeking_msg);
- break;
- case AWAITING_VIDEO_SURFACE:
- onAwaitingVideoSurface();
- break;
- case INITIALIZED:
- checkMediaInfoLoaded();
- clearStatusMsg();
- updatePlayButtonAppearance(playResource);
- break;
- }
- }
-
- private void checkMediaInfoLoaded() {
- if (!mediaInfoLoaded) {
- loadMediaInfo();
- }
- mediaInfoLoaded = true;
- }
-
- private void updatePlayButtonAppearance(int resource) {
- ImageButton butPlay = getPlayButton();
- butPlay.setImageResource(resource);
- }
-
- public abstract ImageButton getPlayButton();
-
- public abstract void postStatusMsg(int msg);
-
- public abstract void clearStatusMsg();
-
- public abstract void loadMediaInfo();
-
- public abstract void onAwaitingVideoSurface();
-
- /**
- * Called when connection to playback service has been established or
- * information has to be refreshed
- */
- void queryService() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Querying service info");
- if (playbackService != null) {
- status = playbackService.getStatus();
- media = playbackService.getMedia();
- if (media == null) {
- Log.w(TAG,
- "PlaybackService has no media object. Trying to restore last played media.");
- Intent serviceIntent = getPlayLastPlayedMediaIntent();
- if (serviceIntent != null) {
- activity.startService(serviceIntent);
- }
- }
- onServiceQueried();
-
- setupGUI();
- handleStatus();
- // make sure that new media is loaded if it's available
- mediaInfoLoaded = false;
-
- } else {
- Log.e(TAG,
- "queryService() was called without an existing connection to playbackservice");
- }
- }
-
- public abstract void onServiceQueried();
-
- /**
- * Should be used by classes which implement the OnSeekBarChanged interface.
- */
- public float onSeekBarProgressChanged(SeekBar seekBar, int progress,
- boolean fromUser, TextView txtvPosition) {
- if (fromUser && playbackService != null) {
- float prog = progress / ((float) seekBar.getMax());
- int duration = media.getDuration();
- txtvPosition.setText(Converter
- .getDurationStringLong((int) (prog * duration)));
- return prog;
- }
- return 0;
-
- }
-
- /**
- * Should be used by classes which implement the OnSeekBarChanged interface.
- */
- public void onSeekBarStartTrackingTouch(SeekBar seekBar) {
- // interrupt position Observer, restart later
- cancelPositionObserver();
- }
-
- /**
- * Should be used by classes which implement the OnSeekBarChanged interface.
- */
- public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) {
- if (playbackService != null) {
- playbackService.seek((int) (prog * media.getDuration()));
- setupPositionObserver();
- }
- }
-
- public OnClickListener newOnPlayButtonClickListener() {
- return new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (playbackService != null) {
- switch (status) {
- case PLAYING:
- playbackService.pause(true, reinitOnPause);
- break;
- case PAUSED:
- case PREPARED:
- playbackService.play();
- break;
- case PREPARING:
- playbackService.setStartWhenPrepared(!playbackService
- .isStartWhenPrepared());
- if (reinitOnPause
- && playbackService.isStartWhenPrepared() == false) {
- playbackService.reinit();
- }
- break;
- case INITIALIZED:
- playbackService.setStartWhenPrepared(true);
- playbackService.prepare();
- break;
- }
- } else {
- Log.w(TAG,
- "Play/Pause button was pressed, but playbackservice was null!");
- }
- }
-
- };
- }
-
- public OnClickListener newOnRevButtonClickListener() {
- return new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (status == PlayerStatus.PLAYING) {
- playbackService.seekDelta(-DEFAULT_SEEK_DELTA);
- }
- }
- };
- }
-
- public OnClickListener newOnFFButtonClickListener() {
- return new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (status == PlayerStatus.PLAYING) {
- playbackService.seekDelta(DEFAULT_SEEK_DELTA);
- }
- }
- };
- }
-
- public boolean serviceAvailable() {
- return playbackService != null;
- }
-
- public int getPosition() {
- if (playbackService != null) {
- return playbackService.getCurrentPositionSafe();
- } else {
- return PlaybackService.INVALID_TIME;
- }
- }
-
- public int getDuration() {
- if (playbackService != null) {
- return playbackService.getDurationSafe();
- } else {
- return PlaybackService.INVALID_TIME;
- }
- }
-
- public Playable getMedia() {
- return media;
- }
-
- public boolean sleepTimerActive() {
- return playbackService != null && playbackService.sleepTimerActive();
- }
-
- public boolean sleepTimerNotActive() {
- return playbackService != null && !playbackService.sleepTimerActive();
- }
-
- public void disableSleepTimer() {
- if (playbackService != null) {
- playbackService.disableSleepTimer();
- }
- }
-
- public long getSleepTimerTimeLeft() {
- if (playbackService != null) {
- return playbackService.getSleepTimerTimeLeft();
- } else {
- return INVALID_TIME;
- }
- }
-
- public void setSleepTimer(long time) {
- if (playbackService != null) {
- playbackService.setSleepTimer(time);
- }
- }
-
- public void seekToChapter(Chapter chapter) {
- if (playbackService != null) {
- playbackService.seekToChapter(chapter);
- }
- }
-
- public void setVideoSurface(SurfaceHolder holder) {
- if (playbackService != null) {
- playbackService.setVideoSurface(holder);
- }
- }
-
- public PlayerStatus getStatus() {
- return status;
- }
-
- public boolean isPlayingVideo() {
- if (playbackService != null) {
- return PlaybackService.isPlayingVideo();
- }
- return false;
- }
-
- /**
- * Returns true if PlaybackController can communicate with the playback
- * service.
- */
- public boolean isConnectedToPlaybackService() {
- return playbackService != null;
- }
-
- public void notifyVideoSurfaceAbandoned() {
- if (playbackService != null) {
- playbackService.notifyVideoSurfaceAbandoned();
- }
- }
-
- /** Move service into INITIALIZED state if it's paused to save bandwidth */
- public void reinitServiceIfPaused() {
- if (playbackService != null
- && playbackService.isShouldStream()
- && (playbackService.getStatus() == PlayerStatus.PAUSED || (playbackService
- .getStatus() == PlayerStatus.PREPARING && playbackService
- .isStartWhenPrepared() == false))) {
- playbackService.reinit();
- }
- }
-
- /** Refreshes the current position of the media file that is playing. */
- public class MediaPositionObserver implements Runnable {
-
- public static final int WAITING_INTERVALL = 1000;
-
- @Override
- public void run() {
- if (playbackService != null && playbackService.getPlayer() != null
- && playbackService.getPlayer().isPlaying()) {
- activity.runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- onPositionObserverUpdate();
- }
- });
- }
- }
- }
+ }
+ serviceIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
+ lastIsStream || !fileExists);
+ return serviceIntent;
+ }
+ }
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "No last played media found");
+ return null;
+ }
+
+ public abstract void setupGUI();
+
+ private void setupPositionObserver() {
+ if ((positionObserverFuture != null && positionObserverFuture
+ .isCancelled())
+ || (positionObserverFuture != null && positionObserverFuture
+ .isDone()) || positionObserverFuture == null) {
+
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Setting up position observer");
+ positionObserver = new MediaPositionObserver();
+ positionObserverFuture = schedExecutor.scheduleWithFixedDelay(
+ positionObserver, MediaPositionObserver.WAITING_INTERVALL,
+ MediaPositionObserver.WAITING_INTERVALL,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private void cancelPositionObserver() {
+ if (positionObserverFuture != null) {
+ boolean result = positionObserverFuture.cancel(true);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "PositionObserver cancelled. Result: " + result);
+ }
+ }
+
+ public abstract void onPositionObserverUpdate();
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ playbackService = ((PlaybackService.LocalBinder) service)
+ .getService();
+
+ queryService();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Connection to Service established");
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ playbackService = null;
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Disconnected from Service");
+
+ }
+ };
+
+ protected BroadcastReceiver statusUpdate = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Received statusUpdate Intent.");
+ if (isConnectedToPlaybackService()) {
+ status = playbackService.getStatus();
+ handleStatus();
+ } else {
+ Log.w(TAG,
+ "Couldn't receive status update: playbackService was null");
+ bindToService();
+ }
+ }
+ };
+
+ protected BroadcastReceiver notificationReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isConnectedToPlaybackService()) {
+ int type = intent.getIntExtra(
+ PlaybackService.EXTRA_NOTIFICATION_TYPE, -1);
+ int code = intent.getIntExtra(
+ PlaybackService.EXTRA_NOTIFICATION_CODE, -1);
+ if (code != -1 && type != -1) {
+ switch (type) {
+ case PlaybackService.NOTIFICATION_TYPE_ERROR:
+ handleError(code);
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_BUFFER_UPDATE:
+ float progress = ((float) code) / 100;
+ onBufferUpdate(progress);
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_RELOAD:
+ cancelPositionObserver();
+ mediaInfoLoaded = false;
+ onReloadNotification(intent.getIntExtra(
+ PlaybackService.EXTRA_NOTIFICATION_CODE, -1));
+ queryService();
+
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_SLEEPTIMER_UPDATE:
+ onSleepTimerUpdate();
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_BUFFER_START:
+ onBufferStart();
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_BUFFER_END:
+ onBufferEnd();
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END:
+ onPlaybackEnd();
+ break;
+ }
+
+ } else {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Bad arguments. Won't handle intent");
+ }
+ } else {
+ bindToService();
+ }
+ }
+
+ };
+
+ private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isConnectedToPlaybackService()) {
+ if (intent.getAction().equals(
+ PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
+ release();
+ onShutdownNotification();
+ }
+ }
+ }
+ };
+
+ public abstract void onShutdownNotification();
+
+ /**
+ * Called when the currently displayed information should be refreshed.
+ */
+ public abstract void onReloadNotification(int code);
+
+ public abstract void onBufferStart();
+
+ public abstract void onBufferEnd();
+
+ public abstract void onBufferUpdate(float progress);
+
+ public abstract void onSleepTimerUpdate();
+
+ public abstract void handleError(int code);
+
+ public abstract void onPlaybackEnd();
+
+ /**
+ * Is called whenever the PlaybackService changes it's status. This method
+ * should be used to update the GUI or start/cancel background threads.
+ */
+ private void handleStatus() {
+ TypedArray res = activity.obtainStyledAttributes(new int[]{
+ R.attr.av_play, R.attr.av_pause});
+ final int playResource = res.getResourceId(0, R.drawable.av_play);
+ final int pauseResource = res.getResourceId(1, R.drawable.av_pause);
+ res.recycle();
+
+ switch (status) {
+
+ case ERROR:
+ postStatusMsg(R.string.player_error_msg);
+ break;
+ case PAUSED:
+ clearStatusMsg();
+ checkMediaInfoLoaded();
+ cancelPositionObserver();
+ updatePlayButtonAppearance(playResource);
+ break;
+ case PLAYING:
+ clearStatusMsg();
+ checkMediaInfoLoaded();
+ setupPositionObserver();
+ updatePlayButtonAppearance(pauseResource);
+ break;
+ case PREPARING:
+ postStatusMsg(R.string.player_preparing_msg);
+ checkMediaInfoLoaded();
+ if (playbackService != null) {
+ if (playbackService.isStartWhenPrepared()) {
+ updatePlayButtonAppearance(pauseResource);
+ } else {
+ updatePlayButtonAppearance(playResource);
+ }
+ }
+ break;
+ case STOPPED:
+ postStatusMsg(R.string.player_stopped_msg);
+ break;
+ case PREPARED:
+ checkMediaInfoLoaded();
+ postStatusMsg(R.string.player_ready_msg);
+ updatePlayButtonAppearance(playResource);
+ break;
+ case SEEKING:
+ postStatusMsg(R.string.player_seeking_msg);
+ break;
+ case AWAITING_VIDEO_SURFACE:
+ onAwaitingVideoSurface();
+ break;
+ case INITIALIZED:
+ checkMediaInfoLoaded();
+ clearStatusMsg();
+ updatePlayButtonAppearance(playResource);
+ break;
+ }
+ }
+
+ private void checkMediaInfoLoaded() {
+ if (!mediaInfoLoaded) {
+ loadMediaInfo();
+ }
+ mediaInfoLoaded = true;
+ }
+
+ private void updatePlayButtonAppearance(int resource) {
+ ImageButton butPlay = getPlayButton();
+ butPlay.setImageResource(resource);
+ }
+
+ public abstract ImageButton getPlayButton();
+
+ public abstract void postStatusMsg(int msg);
+
+ public abstract void clearStatusMsg();
+
+ public abstract void loadMediaInfo();
+
+ public abstract void onAwaitingVideoSurface();
+
+ /**
+ * Called when connection to playback service has been established or
+ * information has to be refreshed
+ */
+ void queryService() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Querying service info");
+ if (playbackService != null) {
+ status = playbackService.getStatus();
+ media = playbackService.getMedia();
+ if (media == null) {
+ Log.w(TAG,
+ "PlaybackService has no media object. Trying to restore last played media.");
+ Intent serviceIntent = getPlayLastPlayedMediaIntent();
+ if (serviceIntent != null) {
+ activity.startService(serviceIntent);
+ }
+ }
+ onServiceQueried();
+
+ setupGUI();
+ handleStatus();
+ // make sure that new media is loaded if it's available
+ mediaInfoLoaded = false;
+
+ } else {
+ Log.e(TAG,
+ "queryService() was called without an existing connection to playbackservice");
+ }
+ }
+
+ public abstract void onServiceQueried();
+
+ /**
+ * Should be used by classes which implement the OnSeekBarChanged interface.
+ */
+ public float onSeekBarProgressChanged(SeekBar seekBar, int progress,
+ boolean fromUser, TextView txtvPosition) {
+ if (fromUser && playbackService != null) {
+ float prog = progress / ((float) seekBar.getMax());
+ int duration = media.getDuration();
+ txtvPosition.setText(Converter
+ .getDurationStringLong((int) (prog * duration)));
+ return prog;
+ }
+ return 0;
+
+ }
+
+ /**
+ * Should be used by classes which implement the OnSeekBarChanged interface.
+ */
+ public void onSeekBarStartTrackingTouch(SeekBar seekBar) {
+ // interrupt position Observer, restart later
+ cancelPositionObserver();
+ }
+
+ /**
+ * Should be used by classes which implement the OnSeekBarChanged interface.
+ */
+ public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) {
+ if (playbackService != null) {
+ playbackService.seek((int) (prog * media.getDuration()));
+ setupPositionObserver();
+ }
+ }
+
+ public OnClickListener newOnPlayButtonClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (playbackService != null) {
+ switch (status) {
+ case PLAYING:
+ playbackService.pause(true, reinitOnPause);
+ break;
+ case PAUSED:
+ case PREPARED:
+ playbackService.play();
+ break;
+ case PREPARING:
+ playbackService.setStartWhenPrepared(!playbackService
+ .isStartWhenPrepared());
+ if (reinitOnPause
+ && playbackService.isStartWhenPrepared() == false) {
+ playbackService.reinit();
+ }
+ break;
+ case INITIALIZED:
+ playbackService.setStartWhenPrepared(true);
+ playbackService.prepare();
+ break;
+ }
+ } else {
+ Log.w(TAG,
+ "Play/Pause button was pressed, but playbackservice was null!");
+ }
+ }
+
+ };
+ }
+
+ public OnClickListener newOnRevButtonClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (status == PlayerStatus.PLAYING) {
+ playbackService.seekDelta(-DEFAULT_SEEK_DELTA);
+ }
+ }
+ };
+ }
+
+ public OnClickListener newOnFFButtonClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (status == PlayerStatus.PLAYING) {
+ playbackService.seekDelta(DEFAULT_SEEK_DELTA);
+ }
+ }
+ };
+ }
+
+ public boolean serviceAvailable() {
+ return playbackService != null;
+ }
+
+ public int getPosition() {
+ if (playbackService != null) {
+ return playbackService.getCurrentPositionSafe();
+ } else {
+ return PlaybackService.INVALID_TIME;
+ }
+ }
+
+ public int getDuration() {
+ if (playbackService != null) {
+ return playbackService.getDurationSafe();
+ } else {
+ return PlaybackService.INVALID_TIME;
+ }
+ }
+
+ public Playable getMedia() {
+ return media;
+ }
+
+ public boolean sleepTimerActive() {
+ return playbackService != null && playbackService.sleepTimerActive();
+ }
+
+ public boolean sleepTimerNotActive() {
+ return playbackService != null && !playbackService.sleepTimerActive();
+ }
+
+ public void disableSleepTimer() {
+ if (playbackService != null) {
+ playbackService.disableSleepTimer();
+ }
+ }
+
+ public long getSleepTimerTimeLeft() {
+ if (playbackService != null) {
+ return playbackService.getSleepTimerTimeLeft();
+ } else {
+ return INVALID_TIME;
+ }
+ }
+
+ public void setSleepTimer(long time) {
+ if (playbackService != null) {
+ playbackService.setSleepTimer(time);
+ }
+ }
+
+ public void seekToChapter(Chapter chapter) {
+ if (playbackService != null) {
+ playbackService.seekToChapter(chapter);
+ }
+ }
+
+ public void setVideoSurface(SurfaceHolder holder) {
+ if (playbackService != null) {
+ playbackService.setVideoSurface(holder);
+ }
+ }
+
+ public PlayerStatus getStatus() {
+ return status;
+ }
+
+ public boolean isPlayingVideo() {
+ if (playbackService != null) {
+ return PlaybackService.isPlayingVideo();
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if PlaybackController can communicate with the playback
+ * service.
+ */
+ public boolean isConnectedToPlaybackService() {
+ return playbackService != null;
+ }
+
+ public void notifyVideoSurfaceAbandoned() {
+ if (playbackService != null) {
+ playbackService.notifyVideoSurfaceAbandoned();
+ }
+ }
+
+ /**
+ * Move service into INITIALIZED state if it's paused to save bandwidth
+ */
+ public void reinitServiceIfPaused() {
+ if (playbackService != null
+ && playbackService.isShouldStream()
+ && (playbackService.getStatus() == PlayerStatus.PAUSED || (playbackService
+ .getStatus() == PlayerStatus.PREPARING && playbackService
+ .isStartWhenPrepared() == false))) {
+ playbackService.reinit();
+ }
+ }
+
+ /**
+ * Refreshes the current position of the media file that is playing.
+ */
+ public class MediaPositionObserver implements Runnable {
+
+ public static final int WAITING_INTERVALL = 1000;
+
+ @Override
+ public void run() {
+ if (playbackService != null && playbackService.getPlayer() != null
+ && playbackService.getPlayer().isPlaying()) {
+ activity.runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ onPositionObserverUpdate();
+ }
+ });
+ }
+ }
+ }
}