diff options
Diffstat (limited to 'src/de/danoeh/antennapod/util')
15 files changed, 331 insertions, 23 deletions
diff --git a/src/de/danoeh/antennapod/util/ChapterUtils.java b/src/de/danoeh/antennapod/util/ChapterUtils.java index ac8149119..521bfebea 100644 --- a/src/de/danoeh/antennapod/util/ChapterUtils.java +++ b/src/de/danoeh/antennapod/util/ChapterUtils.java @@ -35,9 +35,9 @@ public class ChapterUtils { * chapters. */ public static void readID3ChaptersFromPlayableStreamUrl(Playable p) { - if (AppConfig.DEBUG) - Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle()); if (p != null && p.getStreamUrl() != null) { + if (AppConfig.DEBUG) + Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle()); InputStream in = null; try { URL url = new URL(p.getStreamUrl()); @@ -86,9 +86,9 @@ public class ChapterUtils { * chapters. */ public static void readID3ChaptersFromPlayableFileUrl(Playable p) { - if (AppConfig.DEBUG) - Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle()); if (p != null && p.localFileAvailable() && p.getLocalMediaUrl() != null) { + if (AppConfig.DEBUG) + Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle()); File source = new File(p.getLocalMediaUrl()); if (source.exists()) { ChapterReader reader = new ChapterReader(); diff --git a/src/de/danoeh/antennapod/util/DuckType.java b/src/de/danoeh/antennapod/util/DuckType.java new file mode 100644 index 000000000..0dfc01508 --- /dev/null +++ b/src/de/danoeh/antennapod/util/DuckType.java @@ -0,0 +1,115 @@ +/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */ + +package de.danoeh.antennapod.util; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Allows "duck typing" or dynamic invocation based on method signature rather + * than type hierarchy. In other words, rather than checking whether something + * IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck. + * + * To use first use the coerce static method to indicate the object you want to + * do Duck Typing for, then specify an interface to the to method which you want + * to coerce the type to, e.g: + * + * public interface Foo { void aMethod(); } class Bar { ... public void + * aMethod() { ... } ... } Bar bar = ...; Foo foo = + * DuckType.coerce(bar).to(Foo.class); foo.aMethod(); + * + * + */ +public class DuckType { + + private final Object objectToCoerce; + + private DuckType(Object objectToCoerce) { + this.objectToCoerce = objectToCoerce; + } + + private class CoercedProxy implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method delegateMethod = findMethodBySignature(method); + assert delegateMethod != null; + return delegateMethod.invoke(DuckType.this.objectToCoerce, args); + } + } + + /** + * Specify the duck typed object to coerce. + * + * @param object + * the object to coerce + * @return + */ + public static DuckType coerce(Object object) { + return new DuckType(object); + } + + /** + * Coerce the Duck Typed object to the given interface providing it + * implements all the necessary methods. + * + * @param + * @param iface + * @return an instance of the given interface that wraps the duck typed + * class + * @throws ClassCastException + * if the object being coerced does not implement all the + * methods in the given interface. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public <T> T to(Class iface) { + assert iface.isInterface() : "cannot coerce object to a class, must be an interface"; + if (isA(iface)) { + return (T) iface.cast(objectToCoerce); + } + if (quacksLikeA(iface)) { + return generateProxy(iface); + } + throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface); + } + + @SuppressWarnings("rawtypes") + private boolean isA(Class iface) { + return objectToCoerce.getClass().isInstance(iface); + } + + /** + * Determine whether the duck typed object can be used with the given + * interface. + * + * @param Type + * of the interface to check. + * @param iface + * Interface class to check + * @return true if the object will support all the methods in the interface, + * false otherwise. + */ + @SuppressWarnings("rawtypes") + public boolean quacksLikeA(Class iface) { + for (Method method : iface.getMethods()) { + if (findMethodBySignature(method) == null) { + return false; + } + } + return true; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private <T> T generateProxy(Class iface) { + return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy()); + } + + private Method findMethodBySignature(Method method) { + try { + return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes()); + } catch (NoSuchMethodException e) { + return null; + } + } + +}
\ No newline at end of file diff --git a/src/de/danoeh/antennapod/util/LangUtils.java b/src/de/danoeh/antennapod/util/LangUtils.java index 53f8de773..e6e1d8399 100644 --- a/src/de/danoeh/antennapod/util/LangUtils.java +++ b/src/de/danoeh/antennapod/util/LangUtils.java @@ -1,8 +1,11 @@ package de.danoeh.antennapod.util; +import java.nio.charset.Charset; import java.util.HashMap; public class LangUtils { + public static final Charset UTF_8 = Charset.forName("UTF-8"); + private static HashMap<String, String> languages; static { languages = new HashMap<String, String>(); diff --git a/src/de/danoeh/antennapod/util/QueueAccess.java b/src/de/danoeh/antennapod/util/QueueAccess.java index ce9d11429..7a1c7fef2 100644 --- a/src/de/danoeh/antennapod/util/QueueAccess.java +++ b/src/de/danoeh/antennapod/util/QueueAccess.java @@ -51,9 +51,8 @@ public abstract class QueueAccess { if (items == null) { return false; } - Iterator<FeedItem> it = items.iterator(); - for (FeedItem i = it.next(); it.hasNext(); i = it.next()) { - if (i.getId() == id) { + for (FeedItem item : items) { + if (item.getId() == id) { return true; } } @@ -63,8 +62,10 @@ public abstract class QueueAccess { @Override public boolean remove(long id) { Iterator<FeedItem> it = items.iterator(); - for (FeedItem i = it.next(); it.hasNext(); i = it.next()) { - if (i.getId() == id) { + FeedItem item; + while (it.hasNext()) { + item = it.next(); + if (item.getId() == id) { it.remove(); return true; } diff --git a/src/de/danoeh/antennapod/util/URLChecker.java b/src/de/danoeh/antennapod/util/URLChecker.java index 6d9b8ff03..13668d4a9 100644 --- a/src/de/danoeh/antennapod/util/URLChecker.java +++ b/src/de/danoeh/antennapod/util/URLChecker.java @@ -19,13 +19,12 @@ public final class URLChecker { * */ public static String prepareURL(String url) { StringBuilder builder = new StringBuilder(); - url = url.trim(); - if (!url.startsWith("http")) { + if (url.startsWith("feed://")) { + if (AppConfig.DEBUG) Log.d(TAG, "Replacing feed:// with http://"); + url = url.replace("feed://", "http://"); + } else if (!(url.startsWith("http://") || url.startsWith("https://"))) { + if (AppConfig.DEBUG) Log.d(TAG, "Adding http:// at the beginning of the URL"); builder.append("http://"); - if (AppConfig.DEBUG) Log.d(TAG, "Missing http; appending"); - } else if (url.startsWith("https")) { - if (AppConfig.DEBUG) Log.d(TAG, "Replacing https with http"); - url = url.replaceFirst("https", "http"); } builder.append(url); diff --git a/src/de/danoeh/antennapod/util/comparator/DownloadStatusComparator.java b/src/de/danoeh/antennapod/util/comparator/DownloadStatusComparator.java index 2cfe52364..d0561252f 100644 --- a/src/de/danoeh/antennapod/util/comparator/DownloadStatusComparator.java +++ b/src/de/danoeh/antennapod/util/comparator/DownloadStatusComparator.java @@ -9,8 +9,7 @@ public class DownloadStatusComparator implements Comparator<DownloadStatus> { @Override public int compare(DownloadStatus lhs, DownloadStatus rhs) { - return -lhs.getCompletionDate().compareTo(rhs.getCompletionDate()); - + return rhs.getCompletionDate().compareTo(lhs.getCompletionDate()); } } diff --git a/src/de/danoeh/antennapod/util/comparator/FeedItemPubdateComparator.java b/src/de/danoeh/antennapod/util/comparator/FeedItemPubdateComparator.java index b9ee6c07e..c95c0833c 100644 --- a/src/de/danoeh/antennapod/util/comparator/FeedItemPubdateComparator.java +++ b/src/de/danoeh/antennapod/util/comparator/FeedItemPubdateComparator.java @@ -13,7 +13,7 @@ public class FeedItemPubdateComparator implements Comparator<FeedItem> { }*/ @Override public int compare(FeedItem lhs, FeedItem rhs) { - return -lhs.getPubDate().compareTo(rhs.getPubDate()); + return rhs.getPubDate().compareTo(lhs.getPubDate()); } } diff --git a/src/de/danoeh/antennapod/util/comparator/PlaybackCompletionDateComparator.java b/src/de/danoeh/antennapod/util/comparator/PlaybackCompletionDateComparator.java index 2d0ce75ca..434a5a956 100644 --- a/src/de/danoeh/antennapod/util/comparator/PlaybackCompletionDateComparator.java +++ b/src/de/danoeh/antennapod/util/comparator/PlaybackCompletionDateComparator.java @@ -11,8 +11,8 @@ public class PlaybackCompletionDateComparator implements Comparator<FeedItem> { && lhs.getMedia().getPlaybackCompletionDate() != null && rhs.getMedia() != null && rhs.getMedia().getPlaybackCompletionDate() != null) { - return -lhs.getMedia().getPlaybackCompletionDate() - .compareTo(rhs.getMedia().getPlaybackCompletionDate()); + return rhs.getMedia().getPlaybackCompletionDate() + .compareTo(lhs.getMedia().getPlaybackCompletionDate()); } return 0; } diff --git a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java index aa029f672..fdd2011ae 100644 --- a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java +++ b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java @@ -51,10 +51,13 @@ public class FeedItemMenuHandler { * parameter should be set to false if the menu space is limited. * @param queueAccess * Used for testing if the queue contains the selected item - * @return Always returns true + * @return Returns true if selectedItem is not null. * */ public static boolean onPrepareMenu(MenuInterface mi, FeedItem selectedItem, boolean showExtendedMenu, QueueAccess queueAccess) { + if (selectedItem == null) { + return false; + } DownloadRequester requester = DownloadRequester.getInstance(); boolean hasMedia = selectedItem.getMedia() != null; boolean downloaded = hasMedia && selectedItem.getMedia().isDownloaded(); diff --git a/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java b/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java index 843607617..446e024d9 100644 --- a/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java +++ b/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java @@ -30,6 +30,10 @@ public class FeedMenuHandler { } public static boolean onPrepareOptionsMenu(Menu menu, Feed selectedFeed) { + if (selectedFeed == null) { + return false; + } + if (AppConfig.DEBUG) Log.d(TAG, "Preparing options menu"); menu.findItem(R.id.mark_all_read_item).setVisible( diff --git a/src/de/danoeh/antennapod/util/playback/AudioPlayer.java b/src/de/danoeh/antennapod/util/playback/AudioPlayer.java new file mode 100644 index 000000000..68d31324d --- /dev/null +++ b/src/de/danoeh/antennapod/util/playback/AudioPlayer.java @@ -0,0 +1,30 @@ +package de.danoeh.antennapod.util.playback; + +import android.content.Context; +import android.util.Log; +import android.view.SurfaceHolder; + +import com.aocate.media.MediaPlayer; + +public class AudioPlayer extends MediaPlayer implements IPlayer { + private static final String TAG = "AudioPlayer"; + + public AudioPlayer(Context context) { + super(context); + } + + @Override + public void setScreenOnWhilePlaying(boolean screenOn) { + Log.e(TAG, "Setting screen on while playing not supported in Audio Player"); + throw new UnsupportedOperationException("Setting screen on while playing not supported in Audio Player"); + + } + + @Override + public void setDisplay(SurfaceHolder sh) { + if (sh != null) { + Log.e(TAG, "Setting display not supported in Audio Player"); + throw new UnsupportedOperationException("Setting display not supported in Audio Player"); + } + } +} diff --git a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java index 1ada0ec03..e937ee437 100644 --- a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java +++ b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java @@ -25,7 +25,6 @@ public class ExternalMedia implements Playable { private String episodeTitle; private String feedTitle; - private String shownotes; private MediaType mediaType = MediaType.AUDIO; private List<Chapter> chapters; private int duration; @@ -80,8 +79,13 @@ public class ExternalMedia implements Playable { .extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); feedTitle = mmr .extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM); - duration = Integer.parseInt(mmr + try { + duration = Integer.parseInt(mmr .extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)); + } catch (NumberFormatException e) { + e.printStackTrace(); + throw new PlayableException("NumberFormatException when reading duration of media file"); + } ChapterUtils.loadChaptersFromFileUrl(this); } diff --git a/src/de/danoeh/antennapod/util/playback/IPlayer.java b/src/de/danoeh/antennapod/util/playback/IPlayer.java new file mode 100644 index 000000000..ca9b36358 --- /dev/null +++ b/src/de/danoeh/antennapod/util/playback/IPlayer.java @@ -0,0 +1,64 @@ +package de.danoeh.antennapod.util.playback; + +import java.io.IOException; + +import android.view.SurfaceHolder; + +public interface IPlayer { + boolean canSetPitch(); + + boolean canSetSpeed(); + + float getCurrentPitchStepsAdjustment(); + + int getCurrentPosition(); + + float getCurrentSpeedMultiplier(); + + int getDuration(); + + float getMaxSpeedMultiplier(); + + float getMinSpeedMultiplier(); + + boolean isLooping(); + + boolean isPlaying(); + + void pause(); + + void prepare() throws IllegalStateException, IOException; + + void prepareAsync(); + + void release(); + + void reset(); + + void seekTo(int msec); + + void setAudioStreamType(int streamtype); + + void setScreenOnWhilePlaying(boolean screenOn); + + void setDataSource(String path) throws IllegalStateException, IOException, + IllegalArgumentException, SecurityException; + + void setDisplay(SurfaceHolder sh); + + void setEnableSpeedAdjustment(boolean enableSpeedAdjustment); + + void setLooping(boolean looping); + + void setPitchStepsAdjustment(float pitchSteps); + + void setPlaybackPitch(float f); + + void setPlaybackSpeed(float f); + + void setVolume(float left, float right); + + void start(); + + void stop(); +} diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java index 5a5b43a6e..f5d1847b3 100644 --- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java +++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java @@ -342,6 +342,9 @@ public abstract class PlaybackController { case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END: onPlaybackEnd(); break; + case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE: + onPlaybackSpeedChange(); + break; } } else { @@ -369,6 +372,8 @@ public abstract class PlaybackController { } }; + public abstract void onPlaybackSpeedChange(); + public abstract void onShutdownNotification(); /** @@ -663,6 +668,24 @@ public abstract class PlaybackController { return status; } + public boolean canSetPlaybackSpeed() { + return playbackService != null && playbackService.canSetSpeed(); + } + + public void setPlaybackSpeed(float speed) { + if (playbackService != null) { + playbackService.setSpeed(speed); + } + } + + public float getCurrentPlaybackSpeedMultiplier() { + if (canSetPlaybackSpeed()) { + return playbackService.getCurrentPlaybackSpeed(); + } else { + return -1; + } + } + public boolean isPlayingVideo() { if (playbackService != null) { return PlaybackService.isPlayingVideo(); @@ -670,6 +693,7 @@ public abstract class PlaybackController { return false; } + /** * Returns true if PlaybackController can communicate with the playback * service. diff --git a/src/de/danoeh/antennapod/util/playback/VideoPlayer.java b/src/de/danoeh/antennapod/util/playback/VideoPlayer.java new file mode 100644 index 000000000..f0a50542c --- /dev/null +++ b/src/de/danoeh/antennapod/util/playback/VideoPlayer.java @@ -0,0 +1,62 @@ +package de.danoeh.antennapod.util.playback; + +import android.media.MediaPlayer; +import android.util.Log; + +public class VideoPlayer extends MediaPlayer implements IPlayer { + private static final String TAG = "VideoPlayer"; + + @Override + public boolean canSetPitch() { + return false; + } + + @Override + public boolean canSetSpeed() { + return false; + } + + @Override + public float getCurrentPitchStepsAdjustment() { + return 1; + } + + @Override + public float getCurrentSpeedMultiplier() { + return 1; + } + + @Override + public float getMaxSpeedMultiplier() { + return 1; + } + + @Override + public float getMinSpeedMultiplier() { + return 1; + } + + @Override + public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) throws UnsupportedOperationException { + Log.e(TAG, "Setting enable speed adjustment unsupported in video player"); + throw new UnsupportedOperationException("Setting enable speed adjustment unsupported in video player"); + } + + @Override + public void setPitchStepsAdjustment(float pitchSteps) { + Log.e(TAG, "Setting pitch steps adjustment unsupported in video player"); + throw new UnsupportedOperationException("Setting pitch steps adjustment unsupported in video player"); + } + + @Override + public void setPlaybackPitch(float f) { + Log.e(TAG, "Setting playback pitch unsupported in video player"); + throw new UnsupportedOperationException("Setting playback pitch unsupported in video player"); + } + + @Override + public void setPlaybackSpeed(float f) { + Log.e(TAG, "Setting playback speed unsupported in video player"); + throw new UnsupportedOperationException("Setting playback speed unsupported in video player"); + } +} |