From 8a2332f8533d90fd03f58dbe4cf8143b1aa989d7 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 25 May 2014 16:45:54 +0200 Subject: Added support for podlove alternate feeds http://podlove.org/alternate-feeds/ --- .../activity/DefaultOnlineFeedViewActivity.java | 53 +++++++- .../activity/OnlineFeedViewActivity.java | 11 +- .../service/download/DownloadService.java | 2 +- .../syndication/handler/FeedHandler.java | 4 +- .../syndication/handler/FeedHandlerResult.java | 19 +++ .../syndication/handler/HandlerState.java | 150 +++++++++++---------- .../syndication/namespace/atom/NSAtom.java | 12 +- 7 files changed, 168 insertions(+), 83 deletions(-) create mode 100644 src/de/danoeh/antennapod/syndication/handler/FeedHandlerResult.java (limited to 'src/de/danoeh') diff --git a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java index 54b193cbf..597189885 100644 --- a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java +++ b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java @@ -8,10 +8,7 @@ import android.support.v4.app.NavUtils; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; +import android.widget.*; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter; import de.danoeh.antennapod.asynctask.ImageDiskCache; @@ -22,8 +19,10 @@ import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.storage.DownloadRequestException; import de.danoeh.antennapod.storage.DownloadRequester; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; /** * Created by daniel on 24.08.13. @@ -33,6 +32,7 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity { private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED | EventDistributor.FEED_LIST_UPDATE; private volatile List feeds; private Feed feed; + private String selectedDownloadUrl; private Button subscribeButton; @@ -64,11 +64,12 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity { } @Override - protected void showFeedInformation(final Feed feed) { - super.showFeedInformation(feed); + protected void showFeedInformation(final Feed feed, final Map alternateFeedUrls) { + super.showFeedInformation(feed, alternateFeedUrls); setContentView(R.layout.listview_activity); this.feed = feed; + this.selectedDownloadUrl = feed.getDownload_url(); EventDistributor.getInstance().register(listener); ListView listView = (ListView) findViewById(R.id.listview); LayoutInflater inflater = (LayoutInflater) @@ -82,6 +83,8 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity { TextView title = (TextView) header.findViewById(R.id.txtvTitle); TextView author = (TextView) header.findViewById(R.id.txtvAuthor); TextView description = (TextView) header.findViewById(R.id.txtvDescription); + Spinner spAlternateUrls = (Spinner) header.findViewById(R.id.spinnerAlternateUrls); + subscribeButton = (Button) header.findViewById(R.id.butSubscribe); if (feed.getImage() != null) { @@ -96,8 +99,10 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity { @Override public void onClick(View v) { try { - Feed f = new Feed(feed.getDownload_url(), new Date(), feed.getTitle()); + Feed f = new Feed(selectedDownloadUrl, new Date(), feed.getTitle()); f.setPreferences(feed.getPreferences()); + DefaultOnlineFeedViewActivity.this.feed = f; + DownloadRequester.getInstance().downloadFeed( DefaultOnlineFeedViewActivity.this, f); @@ -109,6 +114,40 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity { setSubscribeButtonState(feed); } }); + + if (alternateFeedUrls.isEmpty()) { + spAlternateUrls.setVisibility(View.GONE); + } else { + spAlternateUrls.setVisibility(View.VISIBLE); + + final List alternateUrlsList = new ArrayList(); + final List alternateUrlsTitleList = new ArrayList(); + + alternateUrlsList.add(feed.getDownload_url()); + alternateUrlsTitleList.add(feed.getTitle()); + + + alternateUrlsList.addAll(alternateFeedUrls.keySet()); + for (String url : alternateFeedUrls.keySet()) { + alternateUrlsTitleList.add(alternateFeedUrls.get(url)); + } + ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spAlternateUrls.setAdapter(adapter); + spAlternateUrls.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + selectedDownloadUrl = alternateUrlsList.get(position); + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + + } setSubscribeButtonState(feed); } diff --git a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index 829c016b7..6dd4b4edc 100644 --- a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -23,6 +23,7 @@ import de.danoeh.antennapod.service.download.DownloadStatus; import de.danoeh.antennapod.service.download.Downloader; import de.danoeh.antennapod.service.download.HttpDownloader; import de.danoeh.antennapod.syndication.handler.FeedHandler; +import de.danoeh.antennapod.syndication.handler.FeedHandlerResult; import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException; import de.danoeh.antennapod.util.DownloadError; import de.danoeh.antennapod.util.FileNameGenerator; @@ -35,6 +36,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; import java.util.Date; +import java.util.Map; /** * Downloads a feed from a feed URL and parses it. Subclasses can display the @@ -56,6 +58,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity { public static final int RESULT_ERROR = 2; private Feed feed; + private Map alternateFeedUrls; private Downloader downloader; @Override @@ -211,7 +214,9 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity { boolean successful = false; FeedHandler handler = new FeedHandler(); try { - handler.parseFeed(feed); + FeedHandlerResult result = handler.parseFeed(feed); + feed = result.feed; + alternateFeedUrls = result.alternateFeedUrls; successful = true; } catch (SAXException e) { e.printStackTrace(); @@ -235,7 +240,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity { runOnUiThread(new Runnable() { @Override public void run() { - showFeedInformation(feed); + showFeedInformation(feed, alternateFeedUrls); } }); } else { @@ -266,7 +271,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity { /** * Called when feed parsed successfully */ - protected void showFeedInformation(Feed feed) { + protected void showFeedInformation(Feed feed, Map alternateFeedUrls) { } diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index 3810c071d..ed2f5d532 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -847,7 +847,7 @@ public class DownloadService extends Service { FeedHandler feedHandler = new FeedHandler(); try { - feed = feedHandler.parseFeed(feed); + feed = feedHandler.parseFeed(feed).feed; if (BuildConfig.DEBUG) Log.d(TAG, feed.getTitle() + " parsed"); if (checkFeedData(feed) == false) { diff --git a/src/de/danoeh/antennapod/syndication/handler/FeedHandler.java b/src/de/danoeh/antennapod/syndication/handler/FeedHandler.java index 5576603b6..aafa1c209 100644 --- a/src/de/danoeh/antennapod/syndication/handler/FeedHandler.java +++ b/src/de/danoeh/antennapod/syndication/handler/FeedHandler.java @@ -14,7 +14,7 @@ import java.io.Reader; public class FeedHandler { - public Feed parseFeed(Feed feed) throws SAXException, IOException, + public FeedHandlerResult parseFeed(Feed feed) throws SAXException, IOException, ParserConfigurationException, UnsupportedFeedtypeException { TypeGetter tg = new TypeGetter(); TypeGetter.Type type = tg.getType(feed); @@ -29,6 +29,6 @@ public class FeedHandler { saxParser.parse(inputSource, handler); inputStreamReader.close(); - return handler.state.feed; + return new FeedHandlerResult(handler.state.feed, handler.state.alternateUrls); } } diff --git a/src/de/danoeh/antennapod/syndication/handler/FeedHandlerResult.java b/src/de/danoeh/antennapod/syndication/handler/FeedHandlerResult.java new file mode 100644 index 000000000..41aa29b52 --- /dev/null +++ b/src/de/danoeh/antennapod/syndication/handler/FeedHandlerResult.java @@ -0,0 +1,19 @@ +package de.danoeh.antennapod.syndication.handler; + +import de.danoeh.antennapod.feed.Feed; + +import java.util.Map; + +/** + * Container for results returned by the Feed parser + */ +public class FeedHandlerResult { + + public Feed feed; + public Map alternateFeedUrls; + + public FeedHandlerResult(Feed feed, Map alternateFeedUrls) { + this.feed = feed; + this.alternateFeedUrls = alternateFeedUrls; + } +} diff --git a/src/de/danoeh/antennapod/syndication/handler/HandlerState.java b/src/de/danoeh/antennapod/syndication/handler/HandlerState.java index a9a8bb934..17f84724f 100644 --- a/src/de/danoeh/antennapod/syndication/handler/HandlerState.java +++ b/src/de/danoeh/antennapod/syndication/handler/HandlerState.java @@ -5,9 +5,7 @@ import de.danoeh.antennapod.feed.FeedItem; import de.danoeh.antennapod.syndication.namespace.Namespace; import de.danoeh.antennapod.syndication.namespace.SyndElement; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Stack; +import java.util.*; /** * Contains all relevant information to describe the current state of a @@ -15,70 +13,86 @@ import java.util.Stack; */ public class HandlerState { - /** Feed that the Handler is currently processing. */ - protected Feed feed; - protected ArrayList items; - protected FeedItem currentItem; - protected Stack tagstack; - /** Namespaces that have been defined so far. */ - protected HashMap namespaces; - protected Stack defaultNamespaces; - /** Buffer for saving characters. */ - protected StringBuffer contentBuf; - - public HandlerState(Feed feed) { - this.feed = feed; - items = new ArrayList(); - tagstack = new Stack(); - namespaces = new HashMap(); - defaultNamespaces = new Stack(); - } - - public Feed getFeed() { - return feed; - } - - public ArrayList getItems() { - return items; - } - - public FeedItem getCurrentItem() { - return currentItem; - } - - public Stack getTagstack() { - return tagstack; - } - - public void setFeed(Feed feed) { - this.feed = feed; - } - - public void setCurrentItem(FeedItem currentItem) { - this.currentItem = currentItem; - } - - /** - * Returns the SyndElement that comes after the top element of the tagstack. - */ - public SyndElement getSecondTag() { - SyndElement top = tagstack.pop(); - SyndElement second = tagstack.peek(); - tagstack.push(top); - return second; - } - - public SyndElement getThirdTag() { - SyndElement top = tagstack.pop(); - SyndElement second = tagstack.pop(); - SyndElement third = tagstack.peek(); - tagstack.push(second); - tagstack.push(top); - return third; - } - - public StringBuffer getContentBuf() { - return contentBuf; - } + /** + * Feed that the Handler is currently processing. + */ + protected Feed feed; + /** + * Contains links to related feeds, e.g. feeds with enclosures in other formats. The key of the map is the + * URL of the feed, the value is the title + */ + protected Map alternateUrls; + protected ArrayList items; + protected FeedItem currentItem; + protected Stack tagstack; + /** + * Namespaces that have been defined so far. + */ + protected HashMap namespaces; + protected Stack defaultNamespaces; + /** + * Buffer for saving characters. + */ + protected StringBuffer contentBuf; + + public HandlerState(Feed feed) { + this.feed = feed; + alternateUrls = new LinkedHashMap(); + items = new ArrayList(); + tagstack = new Stack(); + namespaces = new HashMap(); + defaultNamespaces = new Stack(); + } + + public Feed getFeed() { + return feed; + } + + public ArrayList getItems() { + return items; + } + + public FeedItem getCurrentItem() { + return currentItem; + } + + public Stack getTagstack() { + return tagstack; + } + + public void setFeed(Feed feed) { + this.feed = feed; + } + + public void setCurrentItem(FeedItem currentItem) { + this.currentItem = currentItem; + } + + /** + * Returns the SyndElement that comes after the top element of the tagstack. + */ + public SyndElement getSecondTag() { + SyndElement top = tagstack.pop(); + SyndElement second = tagstack.peek(); + tagstack.push(top); + return second; + } + + public SyndElement getThirdTag() { + SyndElement top = tagstack.pop(); + SyndElement second = tagstack.pop(); + SyndElement third = tagstack.peek(); + tagstack.push(second); + tagstack.push(top); + return third; + } + + public StringBuffer getContentBuf() { + return contentBuf; + } + + public void addAlternateFeedUrl(String title, String url) { + alternateUrls.put(url, title); + } } diff --git a/src/de/danoeh/antennapod/syndication/namespace/atom/NSAtom.java b/src/de/danoeh/antennapod/syndication/namespace/atom/NSAtom.java index 383b29fc8..2c8e232ff 100644 --- a/src/de/danoeh/antennapod/syndication/namespace/atom/NSAtom.java +++ b/src/de/danoeh/antennapod/syndication/namespace/atom/NSAtom.java @@ -92,7 +92,8 @@ public class NSAtom extends Namespace { .getValidMimeTypeFromUrl(href)) != null) { state.getCurrentItem().setMedia( new FeedMedia(state.getCurrentItem(), href, - size, type)); + size, type) + ); } } else if (rel.equals(LINK_REL_PAYMENT)) { state.getCurrentItem().setPaymentLink(href); @@ -101,13 +102,20 @@ public class NSAtom extends Namespace { if (rel == null || rel.equals(LINK_REL_ALTERNATE)) { String type = attributes.getValue(LINK_TYPE); /* - * Use as link if a) no type-attribute is given and + * Use as link if a) no type-attribute is given and * feed-object has no link yet b) type of link is * LINK_TYPE_HTML or LINK_TYPE_XHTML */ if ((type == null && state.getFeed().getLink() == null) || (type != null && (type.equals(LINK_TYPE_HTML) || type.equals(LINK_TYPE_XHTML)))) { state.getFeed().setLink(href); + } else if (type != null && (type.equals(LINK_TYPE_ATOM) || type.equals(LINK_TYPE_RSS))) { + // treat as podlove alternate feed + String title = attributes.getValue(LINK_TITLE); + if (title == null) { + title = href; + } + state.addAlternateFeedUrl(title, href); } } else if (rel.equals(LINK_REL_PAYMENT)) { state.getFeed().setPaymentLink(href); -- cgit v1.2.3