diff options
author | daniel oeh <daniel.oeh@gmail.com> | 2013-02-27 22:05:13 +0100 |
---|---|---|
committer | daniel oeh <daniel.oeh@gmail.com> | 2013-02-27 22:05:13 +0100 |
commit | dcbf334bad1533debe0141812b3efabbde8d7d18 (patch) | |
tree | 10f8cec67267c8505abe851e3c3d748d26814e0a /src/de | |
parent | cc741d66434f72d22c2952785daff28369d0ef9b (diff) | |
download | AntennaPod-dcbf334bad1533debe0141812b3efabbde8d7d18.zip |
Added support for playing local external media files
Diffstat (limited to 'src/de')
9 files changed, 372 insertions, 83 deletions
diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index c6fa5c79f..3e38d7b5c 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -1,5 +1,7 @@ package de.danoeh.antennapod.activity; +import java.io.File; + import android.content.Intent; import android.content.res.TypedArray; import android.os.Bundle; @@ -22,10 +24,12 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.ChapterListAdapter; import de.danoeh.antennapod.asynctask.ImageLoader; import de.danoeh.antennapod.feed.Chapter; +import de.danoeh.antennapod.feed.MediaType; import de.danoeh.antennapod.feed.SimpleChapter; import de.danoeh.antennapod.fragment.CoverFragment; import de.danoeh.antennapod.fragment.ItemDescriptionFragment; import de.danoeh.antennapod.service.PlaybackService; +import de.danoeh.antennapod.util.playback.ExternalMedia; import de.danoeh.antennapod.util.playback.Playable; /** Activity for playing audio files. */ @@ -71,10 +75,32 @@ public class AudioplayerActivity extends MediaplayerActivity { protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); super.onCreate(savedInstanceState); + getSupportActionBar().setDisplayShowTitleEnabled(false); detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS]; } @Override + protected void onResume() { + super.onResume(); + if (getIntent().getAction() != null + && getIntent().getAction().equals(Intent.ACTION_VIEW)) { + Intent intent = getIntent(); + if (AppConfig.DEBUG) + Log.d(TAG, "Received VIEW intent: " + + intent.getData().getPath()); + ExternalMedia media = new ExternalMedia(intent.getData().getPath(), MediaType.AUDIO); + Intent launchIntent = new Intent(this, PlaybackService.class); + launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media); + launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, + true); + launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false); + launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, + true); + startService(launchIntent); + } + } + + @Override protected void onAwaitingVideoSurface() { startActivity(new Intent(this, VideoplayerActivity.class)); } @@ -193,8 +219,7 @@ public class AudioplayerActivity extends MediaplayerActivity { @Override public void run() { ImageLoader.getInstance().loadThumbnailBitmap( - media.getImageFileUrl(), - butNavLeft); + media.getImageFileUrl(), butNavLeft); } }); butNavRight.setImageDrawable(drawables.getDrawable(0)); diff --git a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java index 89a38466b..c217a4628 100644 --- a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -238,78 +238,79 @@ public abstract class MediaplayerActivity extends SherlockFragmentActivity @Override public boolean onOptionsItemSelected(MenuItem item) { Playable media = controller.getMedia(); - if (media == null) { - return false; - } - - switch (item.getItemId()) { - case android.R.id.home: + if (item.getItemId() == android.R.id.home) { Intent intent = new Intent(MediaplayerActivity.this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); - break; - case R.id.disable_sleeptimer_item: - if (controller.serviceAvailable()) { - AlertDialog.Builder stDialog = new AlertDialog.Builder(this); - stDialog.setTitle(R.string.sleep_timer_label); - stDialog.setMessage(getString(R.string.time_left_label) - + Converter.getDurationStringLong((int) controller - .getSleepTimerTimeLeft())); - stDialog.setPositiveButton(R.string.disable_sleeptimer_label, - new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - dialog.dismiss(); - controller.disableSleepTimer(); - } - }); - stDialog.setNegativeButton(R.string.cancel_label, - new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - dialog.dismiss(); - } - }); - stDialog.create().show(); - } - break; - case R.id.set_sleeptimer_item: - if (controller.serviceAvailable()) { - TimeDialog td = new TimeDialog(this, - R.string.set_sleeptimer_label, - R.string.set_sleeptimer_label) { - - @Override - public void onTimeEntered(long millis) { - controller.setSleepTimer(millis); - } - }; - td.show(); + return true; + } else if (media != null) { + switch (item.getItemId()) { + case R.id.disable_sleeptimer_item: + if (controller.serviceAvailable()) { + AlertDialog.Builder stDialog = new AlertDialog.Builder(this); + stDialog.setTitle(R.string.sleep_timer_label); + stDialog.setMessage(getString(R.string.time_left_label) + + Converter.getDurationStringLong((int) controller + .getSleepTimerTimeLeft())); + stDialog.setPositiveButton( + R.string.disable_sleeptimer_label, + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, + int which) { + dialog.dismiss(); + controller.disableSleepTimer(); + } + }); + stDialog.setNegativeButton(R.string.cancel_label, + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, + int which) { + dialog.dismiss(); + } + }); + stDialog.create().show(); + } + break; + case R.id.set_sleeptimer_item: + if (controller.serviceAvailable()) { + TimeDialog td = new TimeDialog(this, + R.string.set_sleeptimer_label, + R.string.set_sleeptimer_label) { + + @Override + public void onTimeEntered(long millis) { + controller.setSleepTimer(millis); + } + }; + td.show(); + break; + + } + case R.id.visit_website_item: + Uri uri = Uri.parse(media.getWebsiteLink()); + startActivity(new Intent(Intent.ACTION_VIEW, uri)); + break; + case R.id.support_item: + new FlattrClickWorker(this, media.getPaymentLink()) + .executeAsync(); + break; + case R.id.share_link_item: + ShareUtils.shareLink(this, media.getWebsiteLink()); break; - - } - case R.id.visit_website_item: - Uri uri = Uri.parse(media.getWebsiteLink()); - startActivity(new Intent(Intent.ACTION_VIEW, uri)); - break; - case R.id.support_item: - new FlattrClickWorker(this, media.getPaymentLink()) - .executeAsync(); - break; - case R.id.share_link_item: - ShareUtils.shareLink(this, media.getWebsiteLink()); - break; default: return false; - + + } + return true; + } else { + return false; } - return true; } @Override diff --git a/src/de/danoeh/antennapod/feed/FeedMedia.java b/src/de/danoeh/antennapod/feed/FeedMedia.java index 40b220fd9..64af3e288 100644 --- a/src/de/danoeh/antennapod/feed/FeedMedia.java +++ b/src/de/danoeh/antennapod/feed/FeedMedia.java @@ -187,7 +187,7 @@ public class FeedMedia extends FeedFile implements Playable { @Override public void loadMetadata() throws PlayableException { if (getChapters() == null) { - ChapterUtils.loadChapters(this); + ChapterUtils.loadChaptersFromStreamUrl(this); } } diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java index 7e2158297..603d05421 100644 --- a/src/de/danoeh/antennapod/service/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/PlaybackService.java @@ -185,8 +185,7 @@ public class PlaybackService extends Service { * Same as getPlayerActivityIntent(context), but here the type of activity * depends on the FeedMedia that is provided as an argument. */ - public static Intent getPlayerActivityIntent(Context context, - Playable media) { + public static Intent getPlayerActivityIntent(Context context, Playable media) { MediaType mt = media.getMediaType(); if (mt == MediaType.VIDEO) { return new Intent(context, VideoplayerActivity.class); @@ -539,17 +538,23 @@ public class PlaybackService extends Service { } else if (media.localFileAvailable()) { player.setDataSource(media.getFileUrl()); } + + if (prepareImmediately) { + setStatus(PlayerStatus.PREPARING); + player.prepareAsync(); + } else { + setStatus(PlayerStatus.INITIALIZED); + } } catch (IOException e) { e.printStackTrace(); - } - if (prepareImmediately) { - setStatus(PlayerStatus.PREPARING); - player.prepareAsync(); - } else { - setStatus(PlayerStatus.INITIALIZED); + media = null; + setStatus(PlayerStatus.ERROR); + sendBroadcast(new Intent( + ACTION_SHUTDOWN_PLAYBACK_SERVICE)); } } else { Log.e(TAG, "InitTask could not load metadata"); + media = null; setStatus(PlayerStatus.ERROR); sendBroadcast(new Intent( ACTION_SHUTDOWN_PLAYBACK_SERVICE)); @@ -801,7 +806,11 @@ public class PlaybackService extends Service { public void stop() { if (AppConfig.DEBUG) Log.d(TAG, "Stopping playback"); - player.stop(); + if (status == PlayerStatus.PREPARED || status == PlayerStatus.PAUSED + || status == PlayerStatus.STOPPED + || status == PlayerStatus.PLAYING) { + player.stop(); + } setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING); stopSelf(); } @@ -1346,7 +1355,7 @@ public class PlaybackService extends Service { throw new IllegalArgumentException("Playable must not be null"); } playable = params[0]; - + try { playable.loadMetadata(); } catch (PlayableException e) { diff --git a/src/de/danoeh/antennapod/service/PlayerWidgetService.java b/src/de/danoeh/antennapod/service/PlayerWidgetService.java index 61bfb4887..475af9655 100644 --- a/src/de/danoeh/antennapod/service/PlayerWidgetService.java +++ b/src/de/danoeh/antennapod/service/PlayerWidgetService.java @@ -83,7 +83,7 @@ public class PlayerWidgetService extends Service { PlaybackService.getPlayerActivityIntent(this), 0); views.setOnClickPendingIntent(R.id.layout_left, startMediaplayer); - if (playbackService != null) { + if (playbackService != null && playbackService.getMedia() != null) { Playable media = playbackService.getMedia(); PlayerStatus status = playbackService.getStatus(); diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index a48e24748..986491fb5 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -831,12 +831,9 @@ public class DownloadService extends Service { } finally { mediaplayer.release(); } - + if (media.getItem().getChapters() == null) { - ChapterUtils.readID3ChaptersFromPlayableFileUrl(media); - if (media.getItem().getChapters() == null) { - ChapterUtils.readOggChaptersFromPlayableFileUrl(media); - } + ChapterUtils.loadChaptersFromFileUrl(media); if (media.getItem().getChapters() != null) { chaptersRead = true; } diff --git a/src/de/danoeh/antennapod/util/ChapterUtils.java b/src/de/danoeh/antennapod/util/ChapterUtils.java index 1c2210481..e60e971ab 100644 --- a/src/de/danoeh/antennapod/util/ChapterUtils.java +++ b/src/de/danoeh/antennapod/util/ChapterUtils.java @@ -242,7 +242,7 @@ public class ChapterUtils { } } - public static void loadChapters(Playable media) { + public static void loadChaptersFromStreamUrl(Playable media) { if (AppConfig.DEBUG) Log.d(TAG, "Starting chapterLoader thread"); ChapterUtils.readID3ChaptersFromPlayableStreamUrl(media); @@ -253,4 +253,15 @@ public class ChapterUtils { if (AppConfig.DEBUG) Log.d(TAG, "ChapterLoaderThread has finished"); } + + public static void loadChaptersFromFileUrl(Playable media) { + if (media.localFileAvailable()) { + ChapterUtils.readID3ChaptersFromPlayableFileUrl(media); + if (media.getChapters() == null) { + ChapterUtils.readOggChaptersFromPlayableFileUrl(media); + } + } else { + Log.e(TAG, "Could not load chapters from file url: local file not available"); + } + } } diff --git a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java new file mode 100644 index 000000000..4446d1221 --- /dev/null +++ b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java @@ -0,0 +1,238 @@ +package de.danoeh.antennapod.util.playback; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import org.apache.commons.io.IOUtils; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.media.MediaMetadataRetriever; +import android.os.Parcel; +import android.os.Parcelable; +import de.danoeh.antennapod.PodcastApp; +import de.danoeh.antennapod.feed.Chapter; +import de.danoeh.antennapod.feed.MediaType; +import de.danoeh.antennapod.util.ChapterUtils; +import de.danoeh.antennapod.util.FileNameGenerator; + +/** Represents a media file that is stored on the local storage device. */ +public class ExternalMedia implements Playable { + + public static final int PLAYABLE_TYPE_EXTERNAL_MEDIA = 2; + 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"; + + private String source; + + private String episodeTitle; + private String feedTitle; + private String shownotes; + private MediaType mediaType = MediaType.AUDIO; + private List<Chapter> chapters; + private String imageUrl; + private int duration; + private int position; + + public ExternalMedia(String source, MediaType mediaType) { + super(); + this.source = source; + this.mediaType = mediaType; + } + + public ExternalMedia(String source, MediaType mediaType, int position) { + this(source, mediaType); + this.position = position; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(source); + dest.writeString(mediaType.toString()); + dest.writeInt(position); + } + + @Override + public void writeToPreferences(Editor prefEditor) { + prefEditor.putString(PREF_SOURCE_URL, source); + prefEditor.putString(PREF_MEDIA_TYPE, mediaType.toString()); + prefEditor.putInt(PREF_POSITION, position); + } + + @Override + public void loadMetadata() throws PlayableException { + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); + try { + mmr.setDataSource(source); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + throw new PlayableException("IllegalArgumentException when setting up MediaMetadataReceiver"); + } + episodeTitle = mmr + .extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); + feedTitle = mmr + .extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM); + duration = Integer.parseInt(mmr + .extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)); + ChapterUtils.loadChaptersFromFileUrl(this); + byte[] imgData = mmr.getEmbeddedPicture(); + File cacheDir = PodcastApp.getInstance().getExternalCacheDir(); + if (cacheDir != null) { + OutputStream out = null; + try { + File tmpFile = File.createTempFile( + FileNameGenerator.generateFileName(source) + "-img", + null, cacheDir); + out = new BufferedOutputStream(new FileOutputStream(tmpFile)); + IOUtils.write(imgData, out); + imageUrl = tmpFile.getAbsolutePath(); + } catch (IOException e) { + e.printStackTrace(); + throw new PlayableException("IOException during loadMetadata()"); + } finally { + IOUtils.closeQuietly(out); + } + } + } + + @Override + public String getEpisodeTitle() { + return episodeTitle; + } + + @Override + public void loadShownotes(ShownoteLoaderCallback callback) { + callback.onShownotesLoaded(null); + } + + @Override + public List<Chapter> getChapters() { + return chapters; + } + + @Override + public String getWebsiteLink() { + return null; + } + + @Override + public String getPaymentLink() { + return null; + } + + @Override + public String getFeedTitle() { + return feedTitle; + } + + @Override + public String getImageFileUrl() { + return imageUrl; + } + + @Override + public Object getIdentifier() { + return source; + } + + @Override + public int getDuration() { + return duration; + } + + @Override + public int getPosition() { + return position; + } + + @Override + public MediaType getMediaType() { + return mediaType; + } + + @Override + public String getFileUrl() { + return source; + } + + @Override + public String getStreamUrl() { + return null; + } + + @Override + public boolean localFileAvailable() { + return true; + } + + @Override + public boolean streamAvailable() { + return false; + } + + @Override + public void saveCurrentPosition(SharedPreferences pref, int newPosition) { + SharedPreferences.Editor editor = pref.edit(); + editor.putInt(PREF_POSITION, newPosition); + position = newPosition; + editor.commit(); + } + + @Override + public void setPosition(int newPosition) { + position = newPosition; + } + + @Override + public void setDuration(int newDuration) { + duration = newDuration; + } + + @Override + public void onPlaybackStart() { + + } + + @Override + public void onPlaybackCompleted() { + + } + + @Override + public int getPlayableType() { + return PLAYABLE_TYPE_EXTERNAL_MEDIA; + } + + @Override + public void setChapters(List<Chapter> chapters) { + this.chapters = chapters; + } + + public static final Parcelable.Creator<ExternalMedia> CREATOR = new Parcelable.Creator<ExternalMedia>() { + public ExternalMedia createFromParcel(Parcel in) { + String source = in.readString(); + MediaType type = MediaType.valueOf(in.readString()); + int position = 0; + if (in.dataAvail() > 0) { + position = in.readInt(); + } + ExternalMedia extMedia = new ExternalMedia(source, type, position); + return extMedia; + } + + public ExternalMedia[] newArray(int size) { + return new ExternalMedia[size]; + } + }; + +} diff --git a/src/de/danoeh/antennapod/util/playback/Playable.java b/src/de/danoeh/antennapod/util/playback/Playable.java index 67acbb692..c92b3cfef 100644 --- a/src/de/danoeh/antennapod/util/playback/Playable.java +++ b/src/de/danoeh/antennapod/util/playback/Playable.java @@ -150,6 +150,14 @@ public interface Playable extends Parcelable { } } 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; |