PlaybackService now works with the 'Playable' interface
This commit is contained in:
parent
9cd870c6ee
commit
a6b6022626
|
@ -22,11 +22,11 @@ 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.FeedMedia;
|
||||
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.Playable;
|
||||
|
||||
/** Activity for playing audio files. */
|
||||
public class AudioplayerActivity extends MediaplayerActivity {
|
||||
|
@ -101,7 +101,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Switching contentView to position " + pos);
|
||||
if (currentlyShownPosition != pos) {
|
||||
FeedMedia media = controller.getMedia();
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
FragmentTransaction ft = getSupportFragmentManager()
|
||||
.beginTransaction();
|
||||
|
@ -113,15 +113,14 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
case POS_COVER:
|
||||
if (coverFragment == null) {
|
||||
Log.i(TAG, "Using new coverfragment");
|
||||
coverFragment = CoverFragment.newInstance(media
|
||||
.getItem());
|
||||
coverFragment = CoverFragment.newInstance(media);
|
||||
}
|
||||
currentlyShownFragment = coverFragment;
|
||||
break;
|
||||
case POS_DESCR:
|
||||
if (descriptionFragment == null) {
|
||||
descriptionFragment = ItemDescriptionFragment
|
||||
.newInstance(media.getItem());
|
||||
.newInstance(media);
|
||||
}
|
||||
currentlyShownFragment = descriptionFragment;
|
||||
break;
|
||||
|
@ -140,7 +139,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
|
||||
};
|
||||
chapterFragment.setListAdapter(new ChapterListAdapter(
|
||||
AudioplayerActivity.this, 0, media.getItem()
|
||||
AudioplayerActivity.this, 0, media
|
||||
.getChapters(), media));
|
||||
}
|
||||
currentlyShownFragment = chapterFragment;
|
||||
|
@ -167,7 +166,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
private void updateNavButtonDrawable() {
|
||||
TypedArray drawables = obtainStyledAttributes(new int[] {
|
||||
R.attr.navigation_shownotes, R.attr.navigation_chapters });
|
||||
final FeedMedia media = controller.getMedia();
|
||||
final Playable media = controller.getMedia();
|
||||
if (butNavLeft != null && butNavRight != null && media != null) {
|
||||
switch (currentlyShownPosition) {
|
||||
case POS_COVER:
|
||||
|
@ -182,8 +181,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
@Override
|
||||
public void run() {
|
||||
ImageLoader.getInstance().loadThumbnailBitmap(
|
||||
media.getItem().getFeed().getImage(),
|
||||
butNavLeft);
|
||||
media.getImageFileUrl(), butNavLeft);
|
||||
}
|
||||
});
|
||||
butNavRight.setImageDrawable(drawables.getDrawable(1));
|
||||
|
@ -195,7 +193,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
@Override
|
||||
public void run() {
|
||||
ImageLoader.getInstance().loadThumbnailBitmap(
|
||||
media.getItem().getFeed().getImage(),
|
||||
media.getImageFileUrl(),
|
||||
butNavLeft);
|
||||
}
|
||||
});
|
||||
|
@ -251,11 +249,11 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
@Override
|
||||
protected void loadMediaInfo() {
|
||||
super.loadMediaInfo();
|
||||
final FeedMedia media = controller.getMedia();
|
||||
final Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
txtvTitle.setText(media.getItem().getTitle());
|
||||
txtvFeed.setText(media.getItem().getFeed().getTitle());
|
||||
if (media.getItem().getChapters() != null) {
|
||||
txtvTitle.setText(media.getEpisodeTitle());
|
||||
txtvFeed.setText(media.getFeedTitle());
|
||||
if (media.getChapters() != null) {
|
||||
butNavRight.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
butNavRight.setVisibility(View.GONE);
|
||||
|
@ -302,7 +300,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
|
|||
}
|
||||
|
||||
public interface AudioplayerContentFragment {
|
||||
public void onDataSetChanged(FeedMedia media);
|
||||
public void onDataSetChanged(Playable media);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.DialogInterface;
|
|||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.ImageButton;
|
||||
|
@ -19,18 +20,17 @@ import com.actionbarsherlock.view.MenuItem;
|
|||
|
||||
import de.danoeh.antennapod.AppConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.asynctask.FlattrClickWorker;
|
||||
import de.danoeh.antennapod.dialog.TimeDialog;
|
||||
import de.danoeh.antennapod.feed.FeedManager;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.service.PlaybackService;
|
||||
import de.danoeh.antennapod.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.util.Converter;
|
||||
import de.danoeh.antennapod.util.MediaPlayerError;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
import de.danoeh.antennapod.util.PlaybackController;
|
||||
import de.danoeh.antennapod.util.ShareUtils;
|
||||
import de.danoeh.antennapod.util.StorageUtils;
|
||||
import de.danoeh.antennapod.util.menuhandler.FeedItemMenuHandler;
|
||||
|
||||
/**
|
||||
* Provides general features which are both needed for playing audio and video
|
||||
|
@ -91,7 +91,7 @@ public abstract class MediaplayerActivity extends SherlockFragmentActivity
|
|||
|
||||
@Override
|
||||
public void onSleepTimerUpdate() {
|
||||
invalidateOptionsMenu();
|
||||
supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -133,7 +133,7 @@ public abstract class MediaplayerActivity extends SherlockFragmentActivity
|
|||
}
|
||||
|
||||
protected void onServiceQueried() {
|
||||
invalidateOptionsMenu();
|
||||
supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -219,14 +219,14 @@ public abstract class MediaplayerActivity extends SherlockFragmentActivity
|
|||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
FeedMedia media = controller.getMedia();
|
||||
Playable media = controller.getMedia();
|
||||
|
||||
menu.findItem(R.id.support_item).setVisible(
|
||||
media != null && media.getItem().getPaymentLink() != null);
|
||||
media != null && media.getPaymentLink() != null);
|
||||
menu.findItem(R.id.share_link_item).setVisible(
|
||||
media != null && media.getItem().getLink() != null);
|
||||
media != null && media.getWebsiteLink() != null);
|
||||
menu.findItem(R.id.visit_website_item).setVisible(
|
||||
media != null && media.getItem().getLink() != null);
|
||||
media != null && media.getWebsiteLink() != null);
|
||||
|
||||
boolean sleepTimerSet = controller.sleepTimerActive();
|
||||
boolean sleepTimerNotSet = controller.sleepTimerNotActive();
|
||||
|
@ -237,6 +237,11 @@ 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:
|
||||
Intent intent = new Intent(MediaplayerActivity.this,
|
||||
|
@ -289,15 +294,20 @@ public abstract class MediaplayerActivity extends SherlockFragmentActivity
|
|||
break;
|
||||
|
||||
}
|
||||
default:
|
||||
try {
|
||||
return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(),
|
||||
controller.getMedia().getItem());
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this,
|
||||
e.getMessage());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -363,7 +373,7 @@ public abstract class MediaplayerActivity extends SherlockFragmentActivity
|
|||
protected void loadMediaInfo() {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Loading media info");
|
||||
FeedMedia media = controller.getMedia();
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
txtvPosition.setText(Converter.getDurationStringLong((media
|
||||
.getPosition())));
|
||||
|
|
|
@ -19,10 +19,10 @@ import com.actionbarsherlock.view.Window;
|
|||
|
||||
import de.danoeh.antennapod.AppConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.service.PlaybackService;
|
||||
import de.danoeh.antennapod.service.PlayerStatus;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
|
||||
/** Activity for playing audio files. */
|
||||
public class VideoplayerActivity extends MediaplayerActivity implements
|
||||
|
@ -57,11 +57,11 @@ public class VideoplayerActivity extends MediaplayerActivity implements
|
|||
@Override
|
||||
protected void loadMediaInfo() {
|
||||
super.loadMediaInfo();
|
||||
FeedMedia media = controller.getMedia();
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
getSupportActionBar().setSubtitle(media.getItem().getTitle());
|
||||
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
|
||||
getSupportActionBar()
|
||||
.setTitle(media.getItem().getFeed().getTitle());
|
||||
.setTitle(media.getFeedTitle());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,20 +20,21 @@ import android.widget.ArrayAdapter;
|
|||
import android.widget.TextView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.feed.Chapter;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.util.ChapterUtils;
|
||||
import de.danoeh.antennapod.util.Converter;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
|
||||
public class ChapterListAdapter extends ArrayAdapter<Chapter> {
|
||||
|
||||
private static final String TAG = "ChapterListAdapter";
|
||||
|
||||
private List<Chapter> chapters;
|
||||
private FeedMedia media;
|
||||
private Playable media;
|
||||
|
||||
private int defaultTextColor;
|
||||
|
||||
public ChapterListAdapter(Context context, int textViewResourceId,
|
||||
List<Chapter> objects, FeedMedia media) {
|
||||
List<Chapter> objects, Playable media) {
|
||||
super(context, textViewResourceId, objects);
|
||||
this.chapters = objects;
|
||||
this.media = media;
|
||||
|
@ -122,7 +123,7 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
|
|||
|
||||
}
|
||||
});
|
||||
Chapter current = sc.getItem().getCurrentChapter();
|
||||
Chapter current = ChapterUtils.getCurrentChapter(media);
|
||||
if (current != null) {
|
||||
if (current == sc) {
|
||||
holder.title.setTextColor(convertView.getResources().getColor(
|
||||
|
|
|
@ -5,7 +5,6 @@ public abstract class Chapter extends FeedComponent {
|
|||
/** Defines starting point in milliseconds. */
|
||||
protected long start;
|
||||
protected String title;
|
||||
protected FeedItem item;
|
||||
protected String link;
|
||||
|
||||
public Chapter() {
|
||||
|
@ -20,7 +19,6 @@ public abstract class Chapter extends FeedComponent {
|
|||
super();
|
||||
this.start = start;
|
||||
this.title = title;
|
||||
this.item = item;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
|
@ -34,10 +32,6 @@ public abstract class Chapter extends FeedComponent {
|
|||
return title;
|
||||
}
|
||||
|
||||
public FeedItem getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
@ -50,10 +44,6 @@ public abstract class Chapter extends FeedComponent {
|
|||
this.title = title;
|
||||
}
|
||||
|
||||
public void setItem(FeedItem item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public void setLink(String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
|
|
@ -92,27 +92,6 @@ public class FeedItem extends FeedComponent {
|
|||
contentEncoded = null;
|
||||
}
|
||||
|
||||
/** Get the chapter that fits the position. */
|
||||
public Chapter getCurrentChapter(int position) {
|
||||
Chapter current = null;
|
||||
if (chapters != null) {
|
||||
current = chapters.get(0);
|
||||
for (Chapter sc : chapters) {
|
||||
if (sc.getStart() > position) {
|
||||
break;
|
||||
} else {
|
||||
current = sc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/** Calls getCurrentChapter with current position. */
|
||||
public Chapter getCurrentChapter() {
|
||||
return getCurrentChapter(media.getPosition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value that uniquely identifies this FeedItem. If the
|
||||
* itemIdentifier attribute is not null, it will be returned. Else it will
|
||||
|
|
|
@ -136,10 +136,7 @@ public class FeedManager {
|
|||
}
|
||||
// Start playback Service
|
||||
Intent launchIntent = new Intent(context, PlaybackService.class);
|
||||
launchIntent
|
||||
.putExtra(PlaybackService.EXTRA_MEDIA_ID, media.getId());
|
||||
launchIntent.putExtra(PlaybackService.EXTRA_FEED_ID, media
|
||||
.getItem().getFeed().getId());
|
||||
launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
|
||||
launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED,
|
||||
startWhenPrepared);
|
||||
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
package de.danoeh.antennapod.feed;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class FeedMedia extends FeedFile {
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import de.danoeh.antennapod.PodcastApp;
|
||||
import de.danoeh.antennapod.util.ChapterUtils;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
|
||||
public class FeedMedia extends FeedFile implements Playable {
|
||||
|
||||
public static final int FEEDFILETYPE_FEEDMEDIA = 2;
|
||||
public static final int PLAYABLE_TYPE_FEEDMEDIA = 1;
|
||||
|
||||
public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId";
|
||||
public static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
|
||||
|
||||
private int duration;
|
||||
private int position; // Current position in file
|
||||
|
@ -146,7 +159,7 @@ public class FeedMedia extends FeedFile {
|
|||
public boolean isInProgress() {
|
||||
return (this.position > 0);
|
||||
}
|
||||
|
||||
|
||||
public FeedImage getImage() {
|
||||
if (item != null && item.getFeed() != null) {
|
||||
return item.getFeed().getImage();
|
||||
|
@ -154,4 +167,159 @@ public class FeedMedia extends FeedFile {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(item.getFeed().getId());
|
||||
dest.writeLong(item.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToPreferences(Editor prefEditor) {
|
||||
prefEditor.putLong(PREF_FEED_ID, item.getFeed().getId());
|
||||
prefEditor.putLong(PREF_MEDIA_ID, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMetadata() throws PlayableException {
|
||||
if (getChapters() == null) {
|
||||
ChapterUtils.loadChapters(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEpisodeTitle() {
|
||||
if (getItem().getTitle() != null) {
|
||||
return getItem().getTitle();
|
||||
} else {
|
||||
return getItem().getIdentifyingValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Chapter> getChapters() {
|
||||
return getItem().getChapters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWebsiteLink() {
|
||||
return getItem().getLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeedTitle() {
|
||||
return getItem().getFeed().getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageFileUrl() {
|
||||
if (getItem().getFeed().getImage() != null) {
|
||||
return getItem().getFeed().getImage().getFile_url();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getIdentifier() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileUrl() {
|
||||
return file_url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStreamUrl() {
|
||||
return download_url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean localFileAvailable() {
|
||||
return isDownloaded() && file_url != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean streamAvailable() {
|
||||
return download_url != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveCurrentPosition(SharedPreferences pref, int newPosition) {
|
||||
position = newPosition;
|
||||
FeedManager.getInstance().setFeedMedia(PodcastApp.getInstance(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStart() {
|
||||
if (getItem().isRead() == false) {
|
||||
FeedManager.getInstance().markItemRead(PodcastApp.getInstance(),
|
||||
getItem(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackCompleted() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayableType() {
|
||||
return PLAYABLE_TYPE_FEEDMEDIA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChapters(List<Chapter> chapters) {
|
||||
getItem().setChapters(chapters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPaymentLink() {
|
||||
return getItem().getPaymentLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadShownotes(final ShownoteLoaderCallback callback) {
|
||||
if (item.getDescription() == null || item.getContentEncoded() == null) {
|
||||
FeedManager.getInstance().loadExtraInformationOfItem(
|
||||
PodcastApp.getInstance(), item,
|
||||
new FeedManager.TaskCallback<String[]>() {
|
||||
@Override
|
||||
public void onCompletion(String[] result) {
|
||||
if (result[1] != null) {
|
||||
callback.onShownotesLoaded(result[1]);
|
||||
} else {
|
||||
callback.onShownotesLoaded(result[0]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.onShownotesLoaded(item.getContentEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<FeedMedia> CREATOR = new Parcelable.Creator<FeedMedia>() {
|
||||
public FeedMedia createFromParcel(Parcel in) {
|
||||
long feedId = in.readLong();
|
||||
long itemId = in.readLong();
|
||||
FeedItem item = FeedManager.getInstance().getFeedItem(itemId,
|
||||
feedId);
|
||||
if (item != null) {
|
||||
return item.getMedia();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public FeedMedia[] newArray(int size) {
|
||||
return new FeedMedia[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,30 +13,25 @@ import de.danoeh.antennapod.AppConfig;
|
|||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
|
||||
import de.danoeh.antennapod.asynctask.ImageLoader;
|
||||
import de.danoeh.antennapod.feed.Feed;
|
||||
import de.danoeh.antennapod.feed.FeedItem;
|
||||
import de.danoeh.antennapod.feed.FeedManager;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
|
||||
/** Displays the cover and the title of a FeedItem. */
|
||||
public class CoverFragment extends SherlockFragment implements
|
||||
AudioplayerContentFragment {
|
||||
private static final String TAG = "CoverFragment";
|
||||
private static final String ARG_FEED_ID = "arg.feedId";
|
||||
private static final String ARG_FEEDITEM_ID = "arg.feedItem";
|
||||
private static final String ARG_PLAYABLE = "arg.playable";
|
||||
|
||||
private FeedMedia media;
|
||||
private Playable media;
|
||||
|
||||
private ImageView imgvCover;
|
||||
|
||||
private boolean viewCreated = false;
|
||||
|
||||
public static CoverFragment newInstance(FeedItem item) {
|
||||
public static CoverFragment newInstance(Playable item) {
|
||||
CoverFragment f = new CoverFragment();
|
||||
if (item != null) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong(ARG_FEED_ID, item.getFeed().getId());
|
||||
args.putLong(ARG_FEEDITEM_ID, item.getId());
|
||||
args.putParcelable(ARG_PLAYABLE, item);
|
||||
f.setArguments(args);
|
||||
}
|
||||
return f;
|
||||
|
@ -46,21 +41,11 @@ public class CoverFragment extends SherlockFragment implements
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
FeedManager manager = FeedManager.getInstance();
|
||||
FeedItem item = null;
|
||||
Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
long feedId = args.getLong(ARG_FEED_ID, -1);
|
||||
long itemId = args.getLong(ARG_FEEDITEM_ID, -1);
|
||||
if (feedId != -1 && itemId != -1) {
|
||||
Feed feed = manager.getFeed(feedId);
|
||||
item = manager.getFeedItem(itemId, feed);
|
||||
if (item != null) {
|
||||
media = item.getMedia();
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, TAG + " was called with invalid arguments");
|
||||
}
|
||||
media = args.getParcelable(ARG_PLAYABLE);
|
||||
} else {
|
||||
Log.e(TAG, TAG + " was called with invalid arguments");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +65,7 @@ public class CoverFragment extends SherlockFragment implements
|
|||
@Override
|
||||
public void run() {
|
||||
ImageLoader.getInstance().loadCoverBitmap(
|
||||
media.getItem().getFeed().getImage(), imgvCover);
|
||||
media.getImageFileUrl(), imgvCover);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -103,7 +88,7 @@ public class CoverFragment extends SherlockFragment implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDataSetChanged(FeedMedia media) {
|
||||
public void onDataSetChanged(Playable media) {
|
||||
this.media = media;
|
||||
if (viewCreated) {
|
||||
loadMediaInfo();
|
||||
|
|
|
@ -15,9 +15,9 @@ import com.actionbarsherlock.app.SherlockFragment;
|
|||
import de.danoeh.antennapod.AppConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.asynctask.ImageLoader;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.service.PlaybackService;
|
||||
import de.danoeh.antennapod.util.Converter;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
import de.danoeh.antennapod.util.PlaybackController;
|
||||
|
||||
/**
|
||||
|
@ -193,11 +193,11 @@ public class ExternalPlayerFragment extends SherlockFragment {
|
|||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Loading media info");
|
||||
if (controller.serviceAvailable()) {
|
||||
FeedMedia media = controller.getMedia();
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
txtvTitle.setText(media.getItem().getTitle());
|
||||
txtvTitle.setText(media.getEpisodeTitle());
|
||||
ImageLoader.getInstance().loadThumbnailBitmap(
|
||||
media.getItem().getFeed().getImage(),
|
||||
media.getImageFileUrl(),
|
||||
imgvCover,
|
||||
(int) getActivity().getResources().getDimension(
|
||||
R.dimen.external_player_height));
|
||||
|
|
|
@ -28,36 +28,48 @@ import android.widget.Toast;
|
|||
import com.actionbarsherlock.app.SherlockFragment;
|
||||
|
||||
import de.danoeh.antennapod.AppConfig;
|
||||
import de.danoeh.antennapod.PodcastApp;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.feed.Feed;
|
||||
import de.danoeh.antennapod.feed.FeedItem;
|
||||
import de.danoeh.antennapod.feed.FeedManager;
|
||||
import de.danoeh.antennapod.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
import de.danoeh.antennapod.util.ShareUtils;
|
||||
|
||||
/** Displays the description of a FeedItem in a Webview. */
|
||||
/** Displays the description of a Playable object in a Webview. */
|
||||
public class ItemDescriptionFragment extends SherlockFragment {
|
||||
|
||||
private static final String TAG = "ItemDescriptionFragment";
|
||||
private static final String ARG_PLAYABLE = "arg.playable";
|
||||
|
||||
private static final String ARG_FEED_ID = "arg.feedId";
|
||||
private static final String ARG_FEEDITEM_ID = "arg.feedItemId";
|
||||
private static final String ARG_FEED_ITEM_ID = "arg.feeditemId";
|
||||
|
||||
private WebView webvDescription;
|
||||
private Playable media;
|
||||
|
||||
private FeedItem item;
|
||||
|
||||
private AsyncTask<Void, Void, Void> webViewLoader;
|
||||
|
||||
private String descriptionRef;
|
||||
private String contentEncodedRef;
|
||||
private String shownotes;
|
||||
|
||||
/** URL that was selected via long-press. */
|
||||
private String selectedURL;
|
||||
|
||||
public static ItemDescriptionFragment newInstance(Playable media) {
|
||||
ItemDescriptionFragment f = new ItemDescriptionFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(ARG_PLAYABLE, media);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static ItemDescriptionFragment newInstance(FeedItem item) {
|
||||
ItemDescriptionFragment f = new ItemDescriptionFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putLong(ARG_FEED_ID, item.getFeed().getId());
|
||||
args.putLong(ARG_FEEDITEM_ID, item.getId());
|
||||
args.putLong(ARG_FEED_ITEM_ID, item.getId());
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
@ -75,7 +87,8 @@ public class ItemDescriptionFragment extends SherlockFragment {
|
|||
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
||||
}
|
||||
webvDescription.setBackgroundColor(getResources().getColor(R.color.black));
|
||||
webvDescription.setBackgroundColor(getResources().getColor(
|
||||
R.color.black));
|
||||
}
|
||||
webvDescription.getSettings().setUseWideViewPort(false);
|
||||
webvDescription.getSettings().setLayoutAlgorithm(
|
||||
|
@ -124,50 +137,58 @@ public class ItemDescriptionFragment extends SherlockFragment {
|
|||
super.onCreate(savedInstanceState);
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Creating fragment");
|
||||
FeedManager manager = FeedManager.getInstance();
|
||||
Bundle args = getArguments();
|
||||
long feedId = args.getLong(ARG_FEED_ID, -1);
|
||||
long itemId = args.getLong(ARG_FEEDITEM_ID, -1);
|
||||
if (feedId != -1 && itemId != -1) {
|
||||
Feed feed = manager.getFeed(feedId);
|
||||
item = manager.getFeedItem(itemId, feed);
|
||||
|
||||
} else {
|
||||
Log.e(TAG, TAG + " was called with invalid arguments");
|
||||
if (args.containsKey(ARG_PLAYABLE)) {
|
||||
media = args.getParcelable(ARG_PLAYABLE);
|
||||
} else if (args.containsKey(ARG_FEED_ID)
|
||||
&& args.containsKey(ARG_FEED_ITEM_ID)) {
|
||||
long feedId = args.getLong(ARG_FEED_ID);
|
||||
long itemId = args.getLong(ARG_FEED_ITEM_ID);
|
||||
FeedItem f = FeedManager.getInstance().getFeedItem(itemId, feedId);
|
||||
if (f != null) {
|
||||
item = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (item != null) {
|
||||
if (media != null) {
|
||||
media.loadShownotes(new Playable.ShownoteLoaderCallback() {
|
||||
|
||||
@Override
|
||||
public void onShownotesLoaded(String shownotes) {
|
||||
ItemDescriptionFragment.this.shownotes = shownotes;
|
||||
if (ItemDescriptionFragment.this.shownotes != null) {
|
||||
startLoader();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (item != null) {
|
||||
if (item.getDescription() == null
|
||||
|| item.getContentEncoded() == null) {
|
||||
Log.i(TAG, "Loading data");
|
||||
FeedManager.getInstance().loadExtraInformationOfItem(
|
||||
getActivity(), item,
|
||||
PodcastApp.getInstance(), item,
|
||||
new FeedManager.TaskCallback<String[]>() {
|
||||
@Override
|
||||
public void onCompletion(String[] result) {
|
||||
if (result == null || result.length != 2) {
|
||||
Log.e(TAG, "No description found");
|
||||
if (result[1] != null) {
|
||||
shownotes = result[1];
|
||||
} else {
|
||||
descriptionRef = result[0];
|
||||
contentEncodedRef = result[1];
|
||||
shownotes = result[0];
|
||||
}
|
||||
if (shownotes != null) {
|
||||
startLoader();
|
||||
}
|
||||
|
||||
startLoader();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
contentEncodedRef = item.getContentEncoded();
|
||||
descriptionRef = item.getDescription();
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Using cached data");
|
||||
startLoader();
|
||||
shownotes = item.getContentEncoded();
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Error in onViewCreated: Item was null");
|
||||
Log.e(TAG, "Error in onViewCreated: Item and media were null");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,8 +216,11 @@ public class ItemDescriptionFragment extends SherlockFragment {
|
|||
* */
|
||||
private String applyWebviewStyle(String textColor, String data) {
|
||||
final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> * { color: %s; font-family: Helvetica; line-height: 1.5em; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>";
|
||||
final int pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics());
|
||||
return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin, pageMargin, pageMargin, pageMargin, data);
|
||||
final int pageMargin = (int) TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 8, getResources()
|
||||
.getDisplayMetrics());
|
||||
return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin,
|
||||
pageMargin, pageMargin, pageMargin, data);
|
||||
}
|
||||
|
||||
private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
|
||||
|
@ -245,7 +269,8 @@ public class ItemDescriptionFragment extends SherlockFragment {
|
|||
.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setText(selectedURL);
|
||||
}
|
||||
Toast t = Toast.makeText(getActivity(), R.string.copied_url_msg, Toast.LENGTH_SHORT);
|
||||
Toast t = Toast.makeText(getActivity(),
|
||||
R.string.copied_url_msg, Toast.LENGTH_SHORT);
|
||||
t.show();
|
||||
break;
|
||||
default:
|
||||
|
@ -317,11 +342,7 @@ public class ItemDescriptionFragment extends SherlockFragment {
|
|||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Loading Webview");
|
||||
data = "";
|
||||
if (contentEncodedRef == null && descriptionRef != null) {
|
||||
data = descriptionRef;
|
||||
} else {
|
||||
data = StringEscapeUtils.unescapeHtml4(contentEncodedRef);
|
||||
}
|
||||
data = StringEscapeUtils.unescapeHtml4(shownotes);
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
TypedArray res = getActivity()
|
||||
|
|
|
@ -17,14 +17,14 @@ public class PlaybackPreferences implements
|
|||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final String TAG = "PlaybackPreferences";
|
||||
|
||||
/** Contains the id of the media that was played last. */
|
||||
/** Contains the type of the media that was played last. */
|
||||
public static final String PREF_LAST_PLAYED_ID = "de.danoeh.antennapod.preferences.lastPlayedId";
|
||||
|
||||
/** Contains the feed id of the last played item. */
|
||||
public static final String PREF_LAST_PLAYED_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
|
||||
|
||||
/**
|
||||
* ID of the media object that is currently being played. This preference is
|
||||
* Type of the media object that is currently being played. This preference is
|
||||
* set to NO_MEDIA_PLAYING after playback has been completed and is set as
|
||||
* soon as the 'play' button is pressed.
|
||||
*/
|
||||
|
|
|
@ -28,6 +28,7 @@ import android.media.MediaMetadataRetriever;
|
|||
import android.media.MediaPlayer;
|
||||
import android.media.RemoteControlClient;
|
||||
import android.media.RemoteControlClient.MetadataEditor;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
|
@ -41,6 +42,7 @@ import de.danoeh.antennapod.activity.AudioplayerActivity;
|
|||
import de.danoeh.antennapod.activity.VideoplayerActivity;
|
||||
import de.danoeh.antennapod.feed.Chapter;
|
||||
import de.danoeh.antennapod.feed.Feed;
|
||||
import de.danoeh.antennapod.feed.FeedComponent;
|
||||
import de.danoeh.antennapod.feed.FeedItem;
|
||||
import de.danoeh.antennapod.feed.FeedManager;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
|
@ -50,17 +52,17 @@ import de.danoeh.antennapod.preferences.UserPreferences;
|
|||
import de.danoeh.antennapod.receiver.MediaButtonReceiver;
|
||||
import de.danoeh.antennapod.receiver.PlayerWidget;
|
||||
import de.danoeh.antennapod.util.BitmapDecoder;
|
||||
import de.danoeh.antennapod.util.ChapterUtils;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
import de.danoeh.antennapod.util.Playable.PlayableException;
|
||||
import de.danoeh.antennapod.util.flattr.FlattrUtils;
|
||||
|
||||
/** Controls the MediaPlayer that plays a FeedMedia-file */
|
||||
public class PlaybackService extends Service {
|
||||
/** Logging tag */
|
||||
private static final String TAG = "PlaybackService";
|
||||
|
||||
/** Contains the id of the FeedMedia object. */
|
||||
public static final String EXTRA_MEDIA_ID = "extra.de.danoeh.antennapod.service.mediaId";
|
||||
/** Contains the id of the Feed object of the FeedMedia. */
|
||||
public static final String EXTRA_FEED_ID = "extra.de.danoeh.antennapod.service.feedId";
|
||||
/** Parcelable of type Playable. */
|
||||
public static final String EXTRA_PLAYABLE = "PlaybackService.PlayableExtra";
|
||||
/** True if media should be streamed. */
|
||||
public static final String EXTRA_SHOULD_STREAM = "extra.de.danoeh.antennapod.service.shouldStream";
|
||||
/**
|
||||
|
@ -113,8 +115,8 @@ public class PlaybackService extends Service {
|
|||
private MediaPlayer player;
|
||||
private RemoteControlClient remoteControlClient;
|
||||
|
||||
private FeedMedia media;
|
||||
private Feed feed;
|
||||
private Playable media;
|
||||
|
||||
/** True if media should be streamed (Extracted from Intent Extra) . */
|
||||
private boolean shouldStream;
|
||||
|
||||
|
@ -133,8 +135,6 @@ public class PlaybackService extends Service {
|
|||
private SleepTimer sleepTimer;
|
||||
private Future sleepTimerFuture;
|
||||
|
||||
private Thread chapterLoader;
|
||||
|
||||
private static final int SCHED_EX_POOL_SIZE = 3;
|
||||
private ScheduledThreadPoolExecutor schedExecutor;
|
||||
|
||||
|
@ -186,7 +186,7 @@ public class PlaybackService extends Service {
|
|||
* depends on the FeedMedia that is provided as an argument.
|
||||
*/
|
||||
public static Intent getPlayerActivityIntent(Context context,
|
||||
FeedMedia media) {
|
||||
Playable media) {
|
||||
MediaType mt = media.getMediaType();
|
||||
if (mt == MediaType.VIDEO) {
|
||||
return new Intent(context, VideoplayerActivity.class);
|
||||
|
@ -221,7 +221,6 @@ public class PlaybackService extends Service {
|
|||
PlaybackPreferences.PREF_AUTO_DELETE_MEDIA_PLAYBACK_COMPLETED,
|
||||
false);
|
||||
}
|
||||
editor.putLong(PlaybackPreferences.PREF_LAST_PLAYED_ID, mediaId);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
|
@ -366,26 +365,24 @@ public class PlaybackService extends Service {
|
|||
handleKeycode(keycode);
|
||||
} else {
|
||||
|
||||
long mediaId = intent.getLongExtra(EXTRA_MEDIA_ID, -1);
|
||||
long feedId = intent.getLongExtra(EXTRA_FEED_ID, -1);
|
||||
Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
|
||||
boolean playbackType = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
|
||||
true);
|
||||
if (mediaId == -1 || feedId == -1) {
|
||||
Log.e(TAG,
|
||||
"Media ID or Feed ID wasn't provided to the Service.");
|
||||
if (media == null || feed == null) {
|
||||
if (playable == null) {
|
||||
Log.e(TAG, "Playable extra wasn't sent to the service");
|
||||
if (media == null) {
|
||||
stopSelf();
|
||||
}
|
||||
// Intent values appear to be valid
|
||||
// check if already playing and playbackType is the same
|
||||
} else if (media == null || mediaId != media.getId()
|
||||
} else if (media == null || playable != media
|
||||
|| playbackType != shouldStream) {
|
||||
pause(true, false);
|
||||
player.reset();
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
|
||||
if (media == null || mediaId != media.getId()) {
|
||||
feed = manager.getFeed(feedId);
|
||||
media = manager.getFeedMedia(mediaId, feed);
|
||||
if (media == null
|
||||
|| playable.getIdentifier() != media.getIdentifier()) {
|
||||
media = playable;
|
||||
}
|
||||
|
||||
if (media != null) {
|
||||
|
@ -460,23 +457,44 @@ public class PlaybackService extends Service {
|
|||
if (status == PlayerStatus.STOPPED
|
||||
|| status == PlayerStatus.AWAITING_VIDEO_SURFACE) {
|
||||
try {
|
||||
if (shouldStream) {
|
||||
player.setDataSource(media.getDownload_url());
|
||||
setStatus(PlayerStatus.PREPARING);
|
||||
player.prepareAsync();
|
||||
} else {
|
||||
player.setDataSource(media.getFile_url());
|
||||
setStatus(PlayerStatus.PREPARING);
|
||||
player.prepare();
|
||||
}
|
||||
InitTask initTask = new InitTask() {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Playable result) {
|
||||
if (result != null) {
|
||||
try {
|
||||
if (shouldStream) {
|
||||
player.setDataSource(media.getStreamUrl());
|
||||
setStatus(PlayerStatus.PREPARING);
|
||||
player.prepareAsync();
|
||||
} else {
|
||||
player.setDataSource(media.getFileUrl());
|
||||
setStatus(PlayerStatus.PREPARING);
|
||||
player.prepareAsync();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
setStatus(PlayerStatus.ERROR);
|
||||
sendBroadcast(new Intent(
|
||||
ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
setStatus(PlayerStatus.INITIALIZING);
|
||||
}
|
||||
|
||||
};
|
||||
initTask.executeAsync(media);
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,18 +526,43 @@ public class PlaybackService extends Service {
|
|||
if (mediaType == MediaType.AUDIO) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Mime type is audio");
|
||||
playingVideo = false;
|
||||
if (shouldStream) {
|
||||
player.setDataSource(media.getDownload_url());
|
||||
} else if (media.getFile_url() != null) {
|
||||
player.setDataSource(media.getFile_url());
|
||||
}
|
||||
if (prepareImmediately) {
|
||||
setStatus(PlayerStatus.PREPARING);
|
||||
player.prepareAsync();
|
||||
} else {
|
||||
setStatus(PlayerStatus.INITIALIZED);
|
||||
}
|
||||
|
||||
InitTask initTask = new InitTask() {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Playable result) {
|
||||
if (result != null) {
|
||||
playingVideo = false;
|
||||
try {
|
||||
if (shouldStream) {
|
||||
player.setDataSource(media.getStreamUrl());
|
||||
} else if (media.localFileAvailable()) {
|
||||
player.setDataSource(media.getFileUrl());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (prepareImmediately) {
|
||||
setStatus(PlayerStatus.PREPARING);
|
||||
player.prepareAsync();
|
||||
} else {
|
||||
setStatus(PlayerStatus.INITIALIZED);
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "InitTask could not load metadata");
|
||||
setStatus(PlayerStatus.ERROR);
|
||||
sendBroadcast(new Intent(
|
||||
ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
setStatus(PlayerStatus.INITIALIZING);
|
||||
}
|
||||
|
||||
};
|
||||
initTask.executeAsync(media);
|
||||
} else if (mediaType == MediaType.VIDEO) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Mime type is video");
|
||||
|
@ -534,8 +577,6 @@ public class PlaybackService extends Service {
|
|||
e.printStackTrace();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -574,39 +615,6 @@ public class PlaybackService extends Service {
|
|||
if (startWhenPrepared) {
|
||||
play();
|
||||
}
|
||||
if (shouldStream && media.getItem().getChapters() == null) {
|
||||
// load chapters if available
|
||||
if (chapterLoader != null) {
|
||||
chapterLoader.interrupt();
|
||||
}
|
||||
chapterLoader = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Starting chapterLoader thread");
|
||||
ChapterUtils
|
||||
.readID3ChaptersFromFeedMediaDownloadUrl(media
|
||||
.getItem());
|
||||
if (media.getItem().getChapters() == null) {
|
||||
ChapterUtils
|
||||
.readOggChaptersFromMediaDownloadUrl(media
|
||||
.getItem());
|
||||
}
|
||||
if (media.getItem().getChapters() != null
|
||||
&& !interrupted()) {
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
|
||||
0);
|
||||
manager.setFeedItem(PlaybackService.this,
|
||||
media.getItem());
|
||||
}
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "ChapterLoaderThread has finished");
|
||||
}
|
||||
|
||||
};
|
||||
chapterLoader.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -663,31 +671,35 @@ public class PlaybackService extends Service {
|
|||
audioManager.abandonAudioFocus(audioFocusChangeListener);
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext());
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
// Save state
|
||||
cancelPositionSaver();
|
||||
media.setPlaybackCompletionDate(new Date());
|
||||
manager.markItemRead(PlaybackService.this, media.getItem(), true,
|
||||
true);
|
||||
FeedItem nextItem = manager
|
||||
.getQueueSuccessorOfItem(media.getItem());
|
||||
boolean isInQueue = manager.isInQueue(media.getItem());
|
||||
if (isInQueue) {
|
||||
manager.removeQueueItem(PlaybackService.this, media.getItem());
|
||||
}
|
||||
manager.addItemToPlaybackHistory(PlaybackService.this,
|
||||
media.getItem());
|
||||
manager.setFeedMedia(PlaybackService.this, media);
|
||||
|
||||
long autoDeleteMediaId = media.getId();
|
||||
boolean isInQueue = false;
|
||||
FeedItem nextItem = null;
|
||||
|
||||
if (shouldStream) {
|
||||
autoDeleteMediaId = -1;
|
||||
if (media instanceof FeedMedia) {
|
||||
FeedItem item = ((FeedMedia) media).getItem();
|
||||
((FeedMedia) media).setPlaybackCompletionDate(new Date());
|
||||
manager.markItemRead(PlaybackService.this, item, true, true);
|
||||
nextItem = manager.getQueueSuccessorOfItem(item);
|
||||
isInQueue = media instanceof FeedMedia
|
||||
&& manager.isInQueue(((FeedMedia) media).getItem());
|
||||
if (isInQueue) {
|
||||
manager.removeQueueItem(PlaybackService.this, item);
|
||||
}
|
||||
manager.addItemToPlaybackHistory(PlaybackService.this, item);
|
||||
manager.setFeedMedia(PlaybackService.this, (FeedMedia) media);
|
||||
long autoDeleteMediaId = ((FeedComponent) media).getId();
|
||||
if (shouldStream) {
|
||||
autoDeleteMediaId = -1;
|
||||
}
|
||||
editor.putLong(PlaybackPreferences.PREF_AUTODELETE_MEDIA_ID,
|
||||
autoDeleteMediaId);
|
||||
}
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||
editor.putLong(PlaybackPreferences.PREF_AUTODELETE_MEDIA_ID,
|
||||
autoDeleteMediaId);
|
||||
editor.putBoolean(
|
||||
PlaybackPreferences.PREF_AUTO_DELETE_MEDIA_PLAYBACK_COMPLETED,
|
||||
true);
|
||||
|
@ -700,8 +712,7 @@ public class PlaybackService extends Service {
|
|||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Loading next item in queue");
|
||||
media = nextItem.getMedia();
|
||||
feed = nextItem.getFeed();
|
||||
shouldStream = !media.isDownloaded();
|
||||
shouldStream = !media.localFileAvailable();
|
||||
prepareImmediately = startWhenPrepared = true;
|
||||
} else {
|
||||
if (AppConfig.DEBUG)
|
||||
|
@ -832,12 +843,21 @@ public class PlaybackService extends Service {
|
|||
SharedPreferences.Editor editor = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext())
|
||||
.edit();
|
||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, media.getId());
|
||||
editor.putLong(PlaybackPreferences.PREF_LAST_PLAYED_FEED_ID, feed.getId());
|
||||
editor.putBoolean(PlaybackPreferences.PREF_LAST_IS_STREAM, shouldStream);
|
||||
editor.putBoolean(PlaybackPreferences.PREF_LAST_IS_VIDEO, playingVideo);
|
||||
editor.putLong(
|
||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
||||
media.getPlayableType());
|
||||
editor.putBoolean(PlaybackPreferences.PREF_LAST_IS_STREAM,
|
||||
shouldStream);
|
||||
editor.putBoolean(PlaybackPreferences.PREF_LAST_IS_VIDEO,
|
||||
playingVideo);
|
||||
editor.putLong(PlaybackPreferences.PREF_LAST_PLAYED_ID,
|
||||
media.getPlayableType());
|
||||
media.writeToPreferences(editor);
|
||||
|
||||
editor.commit();
|
||||
setLastPlayedMediaId(media.getId());
|
||||
if (media instanceof FeedMedia) {
|
||||
setLastPlayedMediaId(((FeedMedia) media).getId());
|
||||
}
|
||||
player.start();
|
||||
if (status != PlayerStatus.PAUSED) {
|
||||
player.seekTo((int) media.getPosition());
|
||||
|
@ -853,9 +873,7 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
audioManager
|
||||
.registerMediaButtonEventReceiver(mediaButtonReceiver);
|
||||
if (media.getItem().isRead() == false) {
|
||||
manager.markItemRead(this, media.getItem(), true, false);
|
||||
}
|
||||
media.onPlaybackStart();
|
||||
} else {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Failed to request Audiofocus");
|
||||
|
@ -893,12 +911,11 @@ public class PlaybackService extends Service {
|
|||
|
||||
Bitmap icon = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
if (media != null && media.getImage() != null
|
||||
&& media.getImage().getFile_url() != null) {
|
||||
if (media != null && media.getImageFileUrl() != null) {
|
||||
int iconSize = getResources().getDimensionPixelSize(
|
||||
android.R.dimen.notification_large_icon_width);
|
||||
icon = BitmapDecoder.decodeBitmap(iconSize, media.getImage()
|
||||
.getFile_url());
|
||||
icon = BitmapDecoder.decodeBitmap(iconSize,
|
||||
media.getImageFileUrl());
|
||||
}
|
||||
}
|
||||
if (icon == null) {
|
||||
|
@ -906,8 +923,8 @@ public class PlaybackService extends Service {
|
|||
R.drawable.ic_stat_antenna);
|
||||
}
|
||||
|
||||
String contentText = media.getItem().getFeed().getTitle();
|
||||
String contentTitle = media.getItem().getTitle();
|
||||
String contentText = media.getFeedTitle();
|
||||
String contentTitle = media.getEpisodeTitle();
|
||||
Notification notification = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= 16) {
|
||||
Intent pauseButtonIntent = new Intent(this, PlaybackService.class);
|
||||
|
@ -983,8 +1000,9 @@ public class PlaybackService extends Service {
|
|||
if (position != INVALID_TIME) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Saving current position to " + position);
|
||||
media.setPosition(position);
|
||||
manager.setFeedMedia(this, media);
|
||||
media.saveCurrentPosition(PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext()),
|
||||
position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1078,10 +1096,10 @@ public class PlaybackService extends Service {
|
|||
MetadataEditor editor = remoteControlClient
|
||||
.editMetadata(false);
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
|
||||
media.getItem().getTitle());
|
||||
media.getEpisodeTitle());
|
||||
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
|
||||
media.getItem().getFeed().getTitle());
|
||||
media.getFeedTitle());
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
@ -1144,12 +1162,8 @@ public class PlaybackService extends Service {
|
|||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
|
||||
schedExecutor.shutdownNow();
|
||||
if (chapterLoader != null) {
|
||||
chapterLoader.interrupt();
|
||||
}
|
||||
stop();
|
||||
media = null;
|
||||
feed = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1251,7 +1265,7 @@ public class PlaybackService extends Service {
|
|||
return status;
|
||||
}
|
||||
|
||||
public FeedMedia getMedia() {
|
||||
public Playable getMedia() {
|
||||
return media;
|
||||
}
|
||||
|
||||
|
@ -1321,4 +1335,37 @@ public class PlaybackService extends Service {
|
|||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, id);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private static class InitTask extends AsyncTask<Playable, Void, Playable> {
|
||||
private Playable playable;
|
||||
public PlayableException exception;
|
||||
|
||||
@Override
|
||||
protected Playable doInBackground(Playable... params) {
|
||||
if (params[0] == null) {
|
||||
throw new IllegalArgumentException("Playable must not be null");
|
||||
}
|
||||
playable = params[0];
|
||||
|
||||
try {
|
||||
playable.loadMetadata();
|
||||
} catch (PlayableException e) {
|
||||
e.printStackTrace();
|
||||
exception = e;
|
||||
return null;
|
||||
}
|
||||
return playable;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public void executeAsync(Playable playable) {
|
||||
FlattrUtils.hasToken();
|
||||
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
|
||||
executeOnExecutor(THREAD_POOL_EXECUTOR, playable);
|
||||
} else {
|
||||
execute(playable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,5 +9,6 @@ public enum PlayerStatus {
|
|||
PREPARED,
|
||||
SEEKING,
|
||||
AWAITING_VIDEO_SURFACE, // player has been initialized and the media type to be played is a video.
|
||||
INITIALIZING, // playback service is loading the Playable's metadata
|
||||
INITIALIZED // playback service was started, data source of media player was set.
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ import android.view.View;
|
|||
import android.widget.RemoteViews;
|
||||
import de.danoeh.antennapod.AppConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.receiver.MediaButtonReceiver;
|
||||
import de.danoeh.antennapod.receiver.PlayerWidget;
|
||||
import de.danoeh.antennapod.util.Converter;
|
||||
import de.danoeh.antennapod.util.Playable;
|
||||
|
||||
/** Updates the state of the player widget */
|
||||
public class PlayerWidgetService extends Service {
|
||||
|
@ -84,10 +84,10 @@ public class PlayerWidgetService extends Service {
|
|||
|
||||
views.setOnClickPendingIntent(R.id.layout_left, startMediaplayer);
|
||||
if (playbackService != null) {
|
||||
FeedMedia media = playbackService.getMedia();
|
||||
Playable media = playbackService.getMedia();
|
||||
PlayerStatus status = playbackService.getStatus();
|
||||
|
||||
views.setTextViewText(R.id.txtvTitle, media.getItem().getTitle());
|
||||
views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle());
|
||||
|
||||
if (status == PlayerStatus.PLAYING) {
|
||||
String progressString = getProgressString(playbackService);
|
||||
|
|
|
@ -833,11 +833,9 @@ public class DownloadService extends Service {
|
|||
}
|
||||
|
||||
if (media.getItem().getChapters() == null) {
|
||||
ChapterUtils.readID3ChaptersFromFeedMediaFileUrl(media
|
||||
.getItem());
|
||||
ChapterUtils.readID3ChaptersFromPlayableFileUrl(media);
|
||||
if (media.getItem().getChapters() == null) {
|
||||
ChapterUtils.readOggChaptersFromMediaFileUrl(media
|
||||
.getItem());
|
||||
ChapterUtils.readOggChaptersFromPlayableFileUrl(media);
|
||||
}
|
||||
if (media.getItem().getChapters() != null) {
|
||||
chaptersRead = true;
|
||||
|
|
|
@ -16,8 +16,6 @@ import org.apache.commons.io.IOUtils;
|
|||
import android.util.Log;
|
||||
import de.danoeh.antennapod.AppConfig;
|
||||
import de.danoeh.antennapod.feed.Chapter;
|
||||
import de.danoeh.antennapod.feed.FeedItem;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.util.comparator.ChapterStartTimeComparator;
|
||||
import de.danoeh.antennapod.util.id3reader.ChapterReader;
|
||||
import de.danoeh.antennapod.util.id3reader.ID3ReaderException;
|
||||
|
@ -35,14 +33,13 @@ public class ChapterUtils {
|
|||
* Uses the download URL of a media object of a feeditem to read its ID3
|
||||
* chapters.
|
||||
*/
|
||||
public static void readID3ChaptersFromFeedMediaDownloadUrl(FeedItem item) {
|
||||
public static void readID3ChaptersFromPlayableStreamUrl(Playable p) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Reading id3 chapters from item " + item.getTitle());
|
||||
final FeedMedia media = item.getMedia();
|
||||
if (media != null && media.getDownload_url() != null) {
|
||||
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
|
||||
if (p != null && p.getStreamUrl() != null) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
URL url = new URL(media.getDownload_url());
|
||||
URL url = new URL(p.getStreamUrl());
|
||||
ChapterReader reader = new ChapterReader();
|
||||
|
||||
in = url.openStream();
|
||||
|
@ -52,9 +49,9 @@ public class ChapterUtils {
|
|||
if (chapters != null) {
|
||||
Collections
|
||||
.sort(chapters, new ChapterStartTimeComparator());
|
||||
processChapters(chapters, item);
|
||||
processChapters(chapters, p);
|
||||
if (chaptersValid(chapters)) {
|
||||
item.setChapters(chapters);
|
||||
p.setChapters(chapters);
|
||||
Log.i(TAG, "Chapters loaded");
|
||||
} else {
|
||||
Log.e(TAG, "Chapter data was invalid");
|
||||
|
@ -87,13 +84,11 @@ public class ChapterUtils {
|
|||
* Uses the file URL of a media object of a feeditem to read its ID3
|
||||
* chapters.
|
||||
*/
|
||||
public static void readID3ChaptersFromFeedMediaFileUrl(FeedItem item) {
|
||||
public static void readID3ChaptersFromPlayableFileUrl(Playable p) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Reading id3 chapters from item " + item.getTitle());
|
||||
final FeedMedia media = item.getMedia();
|
||||
if (media != null && media.isDownloaded()
|
||||
&& media.getFile_url() != null) {
|
||||
File source = new File(media.getFile_url());
|
||||
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
|
||||
if (p != null && p.localFileAvailable() && p.getFileUrl() != null) {
|
||||
File source = new File(p.getFileUrl());
|
||||
if (source.exists()) {
|
||||
ChapterReader reader = new ChapterReader();
|
||||
InputStream in = null;
|
||||
|
@ -106,9 +101,9 @@ public class ChapterUtils {
|
|||
if (chapters != null) {
|
||||
Collections.sort(chapters,
|
||||
new ChapterStartTimeComparator());
|
||||
processChapters(chapters, item);
|
||||
processChapters(chapters, p);
|
||||
if (chaptersValid(chapters)) {
|
||||
item.setChapters(chapters);
|
||||
p.setChapters(chapters);
|
||||
Log.i(TAG, "Chapters loaded");
|
||||
} else {
|
||||
Log.e(TAG, "Chapter data was invalid");
|
||||
|
@ -136,15 +131,14 @@ public class ChapterUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void readOggChaptersFromMediaDownloadUrl(FeedItem item) {
|
||||
final FeedMedia media = item.getMedia();
|
||||
if (media != null && media.getDownload_url() != null) {
|
||||
public static void readOggChaptersFromPlayableStreamUrl(Playable media) {
|
||||
if (media != null && media.streamAvailable()) {
|
||||
InputStream input = null;
|
||||
try {
|
||||
URL url = new URL(media.getDownload_url());
|
||||
URL url = new URL(media.getStreamUrl());
|
||||
input = url.openStream();
|
||||
if (input != null) {
|
||||
readOggChaptersFromInputStream(item, input);
|
||||
readOggChaptersFromInputStream(media, input);
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -156,15 +150,14 @@ public class ChapterUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void readOggChaptersFromMediaFileUrl(FeedItem item) {
|
||||
final FeedMedia media = item.getMedia();
|
||||
if (media != null && media.getFile_url() != null) {
|
||||
File source = new File(media.getFile_url());
|
||||
public static void readOggChaptersFromPlayableFileUrl(Playable media) {
|
||||
if (media != null && media.getFileUrl() != null) {
|
||||
File source = new File(media.getFileUrl());
|
||||
if (source.exists()) {
|
||||
InputStream input = null;
|
||||
try {
|
||||
input = new BufferedInputStream(new FileInputStream(source));
|
||||
readOggChaptersFromInputStream(item, input);
|
||||
readOggChaptersFromInputStream(media, input);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
|
@ -174,21 +167,21 @@ public class ChapterUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static void readOggChaptersFromInputStream(FeedItem item,
|
||||
private static void readOggChaptersFromInputStream(Playable p,
|
||||
InputStream input) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"Trying to read chapters from item with title "
|
||||
+ item.getTitle());
|
||||
+ p.getEpisodeTitle());
|
||||
try {
|
||||
VorbisCommentChapterReader reader = new VorbisCommentChapterReader();
|
||||
reader.readInputStream(input);
|
||||
List<Chapter> chapters = reader.getChapters();
|
||||
if (chapters != null) {
|
||||
Collections.sort(chapters, new ChapterStartTimeComparator());
|
||||
processChapters(chapters, item);
|
||||
processChapters(chapters, p);
|
||||
if (chaptersValid(chapters)) {
|
||||
item.setChapters(chapters);
|
||||
p.setChapters(chapters);
|
||||
Log.i(TAG, "Chapters loaded");
|
||||
} else {
|
||||
Log.e(TAG, "Chapter data was invalid");
|
||||
|
@ -203,13 +196,12 @@ public class ChapterUtils {
|
|||
}
|
||||
|
||||
/** Makes sure that chapter does a title and an item attribute. */
|
||||
private static void processChapters(List<Chapter> chapters, FeedItem item) {
|
||||
private static void processChapters(List<Chapter> chapters, Playable p) {
|
||||
for (int i = 0; i < chapters.size(); i++) {
|
||||
Chapter c = chapters.get(i);
|
||||
if (c.getTitle() == null) {
|
||||
c.setTitle(Integer.toString(i));
|
||||
}
|
||||
c.setItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,4 +220,36 @@ public class ChapterUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Calls getCurrentChapter with current position. */
|
||||
public static Chapter getCurrentChapter(Playable media) {
|
||||
if (media.getChapters() != null) {
|
||||
List<Chapter> chapters = media.getChapters();
|
||||
Chapter current = null;
|
||||
if (chapters != null) {
|
||||
current = chapters.get(0);
|
||||
for (Chapter sc : chapters) {
|
||||
if (sc.getStart() > media.getPosition()) {
|
||||
break;
|
||||
} else {
|
||||
current = sc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return current;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadChapters(Playable media) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Starting chapterLoader thread");
|
||||
ChapterUtils.readID3ChaptersFromPlayableStreamUrl(media);
|
||||
if (media.getChapters() == null) {
|
||||
ChapterUtils.readOggChaptersFromPlayableStreamUrl(media);
|
||||
}
|
||||
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "ChapterLoaderThread has finished");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
package de.danoeh.antennapod.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
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. */
|
||||
public interface Playable extends Parcelable {
|
||||
|
||||
/**
|
||||
* 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 (for example:
|
||||
* chapter marks).
|
||||
*/
|
||||
public void loadMetadata() throws PlayableException;
|
||||
|
||||
/** 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 file url to an image or null if no such image exists. */
|
||||
public String getImageFileUrl();
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
public MediaType getMediaType();
|
||||
|
||||
/**
|
||||
* Returns an url to a local file that can be played or null if this file
|
||||
* does not exist.
|
||||
*/
|
||||
public String getFileUrl();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import de.danoeh.antennapod.feed.FeedMedia;
|
|||
import de.danoeh.antennapod.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.service.PlaybackService;
|
||||
import de.danoeh.antennapod.service.PlayerStatus;
|
||||
import de.danoeh.antennapod.util.Playable.PlayableUtils;
|
||||
|
||||
/**
|
||||
* Communicates with the playback service. GUI classes should use this class to
|
||||
|
@ -47,7 +48,7 @@ public abstract class PlaybackController {
|
|||
private Activity activity;
|
||||
|
||||
private PlaybackService playbackService;
|
||||
private FeedMedia media;
|
||||
private Playable media;
|
||||
private PlayerStatus status;
|
||||
|
||||
private ScheduledThreadPoolExecutor schedExecutor;
|
||||
|
@ -186,24 +187,22 @@ public abstract class PlaybackController {
|
|||
Log.d(TAG, "Trying to restore last played media");
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(activity.getApplicationContext());
|
||||
long mediaId = PlaybackPreferences.getLastPlayedId();
|
||||
long feedId = PlaybackPreferences.getLastPlayedFeedId();
|
||||
if (mediaId != -1 && feedId != -1) {
|
||||
FeedMedia media = FeedManager.getInstance().getFeedMedia(mediaId);
|
||||
long lastPlayedId = PlaybackPreferences.getLastPlayedId();
|
||||
if (lastPlayedId != PlaybackPreferences.NO_MEDIA_PLAYING) {
|
||||
Playable media = PlayableUtils.createInstanceFromPreferences((int) lastPlayedId, prefs);
|
||||
if (media != null) {
|
||||
Intent serviceIntent = new Intent(activity,
|
||||
PlaybackService.class);
|
||||
serviceIntent.putExtra(PlaybackService.EXTRA_FEED_ID, feedId);
|
||||
serviceIntent.putExtra(PlaybackService.EXTRA_MEDIA_ID, mediaId);
|
||||
serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
|
||||
serviceIntent.putExtra(
|
||||
PlaybackService.EXTRA_START_WHEN_PREPARED, false);
|
||||
serviceIntent.putExtra(
|
||||
PlaybackService.EXTRA_PREPARE_IMMEDIATELY, false);
|
||||
boolean fileExists = media.fileExists();
|
||||
boolean fileExists = media.localFileAvailable();
|
||||
boolean lastIsStream = PlaybackPreferences.isLastIsStream();
|
||||
if (!fileExists && !lastIsStream) {
|
||||
if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
|
||||
FeedManager.getInstance().notifyMissingFeedMediaFile(
|
||||
activity, media);
|
||||
activity, (FeedMedia) media);
|
||||
}
|
||||
serviceIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
|
||||
lastIsStream || !fileExists);
|
||||
|
@ -585,7 +584,7 @@ public abstract class PlaybackController {
|
|||
}
|
||||
}
|
||||
|
||||
public FeedMedia getMedia() {
|
||||
public Playable getMedia() {
|
||||
return media;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue