diff options
author | Tony Tam <149837+tonytamsf@users.noreply.github.com> | 2021-04-06 09:15:14 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-06 18:15:14 +0200 |
commit | 79c79efce588b446cf9816a01e0056e7e3dfd9db (patch) | |
tree | faa91fbd058c9fb93a827f68dac4062de3c0b04d /core | |
parent | ef41704166cc17b95159a891e7be36c8b334b8b4 (diff) | |
download | AntennaPod-79c79efce588b446cf9816a01e0056e7e3dfd9db.zip |
Parsing podcast:funding tag, showing payment, funding links on the show info screen (#4933)
Diffstat (limited to 'core')
13 files changed, 198 insertions, 22 deletions
diff --git a/core/build.gradle b/core/build.gradle index 75ad7faad..554847cbf 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -107,6 +107,7 @@ dependencies { playApi "com.google.android.support:wearable:$wearableSupportVersion" compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" + testImplementation 'androidx.test:core:1.2.0' testImplementation "org.awaitility:awaitility:$awaitilityVersion" testImplementation 'junit:junit:4.13' testImplementation 'org.mockito:mockito-inline:3.5.13' diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java index dd8a466eb..617997465 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java @@ -54,7 +54,7 @@ public class Feed extends FeedFile { */ private String lastUpdate; - private String paymentLink; + private ArrayList<FeedFunding> fundingList; /** * Feed type, for example RSS 2 or Atom. */ @@ -103,8 +103,9 @@ public class Feed extends FeedFile { /** * This constructor is used for restoring a feed from the database. */ - public Feed(long id, String lastUpdate, String title, String customTitle, String link, String description, String paymentLink, - String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl, + public Feed(long id, String lastUpdate, String title, String customTitle, String link, + String description, String paymentLinks, String author, String language, + String type, String feedIdentifier, String imageUrl, String fileUrl, String downloadUrl, boolean downloaded, boolean paged, String nextPageLink, String filter, @Nullable SortOrder sortOrder, boolean lastUpdateFailed) { super(fileUrl, downloadUrl, downloaded); @@ -114,7 +115,7 @@ public class Feed extends FeedFile { this.lastUpdate = lastUpdate; this.link = link; this.description = description; - this.paymentLink = paymentLink; + this.fundingList = FeedFunding.extractPaymentLinks(paymentLinks); this.author = author; this.language = language; this.type = type; @@ -237,8 +238,8 @@ public class Feed extends FeedFile { if (other.author != null) { author = other.author; } - if (other.paymentLink != null) { - paymentLink = other.paymentLink; + if (other.fundingList != null) { + fundingList = other.fundingList; } // this feed's nextPage might already point to a higher page, so we only update the nextPage value // if this feed is not paged and the other feed is. @@ -285,8 +286,8 @@ public class Feed extends FeedFile { return true; } } - if (other.paymentLink != null) { - if (paymentLink == null || !paymentLink.equals(other.paymentLink)) { + if (other.fundingList != null) { + if (fundingList == null || !fundingList.equals(other.fundingList)) { return true; } } @@ -390,12 +391,15 @@ public class Feed extends FeedFile { this.feedIdentifier = feedIdentifier; } - public String getPaymentLink() { - return paymentLink; + public void addPayment(FeedFunding funding) { + if (fundingList == null) { + fundingList = new ArrayList<FeedFunding>(); + } + fundingList.add(funding); } - public void setPaymentLink(String paymentLink) { - this.paymentLink = paymentLink; + public ArrayList<FeedFunding> getPaymentLinks() { + return fundingList; } public String getLanguage() { diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFunding.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFunding.java new file mode 100644 index 000000000..151e68a4f --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFunding.java @@ -0,0 +1,91 @@ +package de.danoeh.antennapod.core.feed; + +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; + +public class FeedFunding { + public static final String FUNDING_ENTRIES_SEPARATOR = "\u001e"; + public static final String FUNDING_TITLE_SEPARATOR = "\u001f"; + + public String url; + public String content; + + public FeedFunding(String url, String content) { + this.url = url; + this.content = content; + } + + public void setContent(String content) { + this.content = content; + } + + public void setUrl(String url) { + this.url = url; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !obj.getClass().equals(this.getClass())) { + return false; + } + + FeedFunding funding = (FeedFunding) obj; + if (url == null && funding.url == null && content == null && funding.content == null) { + return true; + } + if (url != null && url.equals(funding.url) && content != null && content.equals(funding.content)) { + return true; + } + return false; + } + + @Override + public int hashCode() { + return (url + FUNDING_TITLE_SEPARATOR + content).hashCode(); + } + + public static ArrayList<FeedFunding> extractPaymentLinks(String payLinks) { + if (StringUtils.isBlank(payLinks)) { + return null; + } + // old format before we started with PodcastIndex funding tag + ArrayList<FeedFunding> funding = new ArrayList<FeedFunding>(); + if (!payLinks.contains(FeedFunding.FUNDING_ENTRIES_SEPARATOR) + && !payLinks.contains(FeedFunding.FUNDING_TITLE_SEPARATOR)) { + funding.add(new FeedFunding(payLinks, "")); + return funding; + } + String [] list = payLinks.split(FeedFunding.FUNDING_ENTRIES_SEPARATOR); + if (list.length == 0) { + return null; + } + + for (String str : list) { + String [] linkContent = str.split(FeedFunding.FUNDING_TITLE_SEPARATOR); + if (StringUtils.isBlank(linkContent[0])) { + continue; + } + String url = linkContent[0]; + String title = ""; + if (linkContent.length > 1 && ! StringUtils.isBlank(linkContent[1])) { + title = linkContent[1]; + } + funding.add(new FeedFunding(url, title)); + } + return funding; + } + + public static String getPaymentLinksAsString(ArrayList<FeedFunding> fundingList) { + StringBuilder result = new StringBuilder(); + if (fundingList == null) { + return null; + } + for (FeedFunding fund : fundingList) { + result.append(fund.url).append(FeedFunding.FUNDING_TITLE_SEPARATOR).append(fund.content); + result.append(FeedFunding.FUNDING_ENTRIES_SEPARATOR); + } + return StringUtils.removeEnd(result.toString(), FeedFunding.FUNDING_ENTRIES_SEPARATOR); + } + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 7ab776856..bad7775f2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -17,6 +17,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import de.danoeh.antennapod.core.feed.FeedFunding; import de.danoeh.antennapod.core.storage.mapper.FeedItemFilterQuery; import org.apache.commons.io.FileUtils; @@ -403,7 +404,7 @@ public class PodDBAdapter { values.put(KEY_TITLE, feed.getFeedTitle()); values.put(KEY_LINK, feed.getLink()); values.put(KEY_DESCRIPTION, feed.getDescription()); - values.put(KEY_PAYMENT_LINK, feed.getPaymentLink()); + values.put(KEY_PAYMENT_LINK, FeedFunding.getPaymentLinksAsString(feed.getPaymentLinks())); values.put(KEY_AUTHOR, feed.getAuthor()); values.put(KEY_LANGUAGE, feed.getLanguage()); values.put(KEY_IMAGE_URL, feed.getImageUrl()); diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java index 608cade88..7aaf66668 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Stack; import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedFunding; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.syndication.namespace.Namespace; import de.danoeh.antennapod.core.syndication.namespace.SyndElement; @@ -28,6 +29,7 @@ public class HandlerState { final Map<String, String> alternateUrls; private final ArrayList<FeedItem> items; private FeedItem currentItem; + private FeedFunding currentFunding; final Stack<SyndElement> tagstack; /** * Namespaces that have been defined so far. @@ -78,6 +80,14 @@ public class HandlerState { this.currentItem = currentItem; } + public FeedFunding getCurrentFunding() { + return currentFunding; + } + + public void setCurrentFunding(FeedFunding currentFunding) { + this.currentFunding = currentFunding; + } + /** * Returns the SyndElement that comes after the top element of the tagstack. */ diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java index ab66b912b..5beb36d6d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java @@ -14,6 +14,7 @@ import de.danoeh.antennapod.core.syndication.namespace.NSMedia; import de.danoeh.antennapod.core.syndication.namespace.NSRSS20; import de.danoeh.antennapod.core.syndication.namespace.NSSimpleChapters; import de.danoeh.antennapod.core.syndication.namespace.Namespace; +import de.danoeh.antennapod.core.syndication.namespace.PodcastIndex; import de.danoeh.antennapod.core.syndication.namespace.SyndElement; import de.danoeh.antennapod.core.syndication.namespace.atom.NSAtom; @@ -107,9 +108,13 @@ class SyndHandler extends DefaultHandler { && prefix.equals(NSDublinCore.NSTAG)) { state.namespaces.put(uri, new NSDublinCore()); Log.d(TAG, "Recognized DublinCore namespace"); + } else if (uri.equals(PodcastIndex.NSURI) || uri.equals(PodcastIndex.NSURI2) + && prefix.equals(PodcastIndex.NSTAG)) { + state.namespaces.put(uri, new PodcastIndex()); + Log.d(TAG, "Recognized PodcastIndex namespace"); } - } - } + } + } private Namespace getHandlingNamespace(String uri, String qName) { Namespace handler = state.namespaces.get(uri); diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java new file mode 100644 index 000000000..eb68c0915 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java @@ -0,0 +1,38 @@ +package de.danoeh.antennapod.core.syndication.namespace; + +import org.jsoup.helper.StringUtil; +import org.xml.sax.Attributes; +import de.danoeh.antennapod.core.feed.FeedFunding; +import de.danoeh.antennapod.core.syndication.handler.HandlerState; + +public class PodcastIndex extends Namespace { + + public static final String NSTAG = "podcast"; + public static final String NSURI = "https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md"; + public static final String NSURI2 = "https://podcastindex.org/namespace/1.0"; + private static final String URL = "url"; + private static final String FUNDING = "funding"; + + @Override + public SyndElement handleElementStart(String localName, HandlerState state, + Attributes attributes) { + if (FUNDING.equals(localName)) { + String href = attributes.getValue(URL); + FeedFunding funding = new FeedFunding(href, ""); + state.setCurrentFunding(funding); + state.getFeed().addPayment(state.getCurrentFunding()); + } + return new SyndElement(localName, this); + } + + @Override + public void handleElementEnd(String localName, HandlerState state) { + if (state.getContentBuf() == null) { + return; + } + String content = state.getContentBuf().toString(); + if (FUNDING.equals(localName) && state.getCurrentFunding() != null && !StringUtil.isBlank(content)) { + state.getCurrentFunding().setContent(content); + } + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java index 42f787d98..eeca82aee 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.syndication.namespace.atom; import android.text.TextUtils; import android.util.Log; +import de.danoeh.antennapod.core.feed.FeedFunding; import de.danoeh.antennapod.core.syndication.util.SyndStringUtils; import org.xml.sax.Attributes; @@ -137,7 +138,7 @@ public class NSAtom extends Namespace { //A Link such as to a directory such as iTunes } } else if (LINK_REL_PAYMENT.equals(rel) && state.getFeed() != null) { - state.getFeed().setPaymentLink(href); + state.getFeed().addPayment(new FeedFunding(href, "")); } else if (LINK_REL_NEXT.equals(rel) && state.getFeed() != null) { state.getFeed().setPaged(true); state.getFeed().setNextPageLink(href); diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 6b7ea2be4..d4e8dc7cb 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -88,6 +88,8 @@ <string name="author_label">Author(s)</string> <string name="language_label">Language</string> <string name="url_label">URL</string> + <string name="support_funding_label">Support</string> + <string name="support_podcast">Support this Podcast</string> <string name="cover_label">Cover</string> <string name="error_label">Error</string> <string name="error_msg_prefix">An error occurred:</string> diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java index c779b6d55..89843e941 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java @@ -48,7 +48,7 @@ public class FeedCursorMapperTest { assertEquals("feed custom title", feed.getCustomTitle()); assertEquals("feed link", feed.getLink()); assertEquals("feed description", feed.getDescription()); - assertEquals("feed payment link", feed.getPaymentLink()); + assertEquals("feed payment link", feed.getPaymentLinks().get(0).url); assertEquals("feed author", feed.getAuthor()); assertEquals("feed language", feed.getLanguage()); assertEquals("feed image url", feed.getImageUrl()); diff --git a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java index 1195bed69..5e982ed3c 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java @@ -30,7 +30,7 @@ public class AtomParserTest { assertEquals("http://example.com/feed", feed.getFeedIdentifier()); assertEquals("http://example.com", feed.getLink()); assertEquals("This is the description", feed.getDescription()); - assertEquals("http://example.com/payment", feed.getPaymentLink()); + assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url); assertEquals("http://example.com/picture", feed.getImageUrl()); assertEquals(10, feed.getItems().size()); for (int i = 0; i < feed.getItems().size(); i++) { @@ -62,7 +62,7 @@ public class AtomParserTest { assertEquals("http://example.com/feed", feed.getFeedIdentifier()); assertEquals("http://example.com", feed.getLink()); assertEquals("This is the description", feed.getDescription()); - assertEquals("http://example.com/payment", feed.getPaymentLink()); + assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url); assertEquals("https://example.com/image.png", feed.getImageUrl()); assertEquals(0, feed.getItems().size()); } diff --git a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java index 95b04a511..90b14c875 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.core.syndication.handler; +import android.text.TextUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -31,7 +32,7 @@ public class RssParserTest { assertEquals("en", feed.getLanguage()); assertEquals("http://example.com", feed.getLink()); assertEquals("This is the description", feed.getDescription()); - assertEquals("http://example.com/payment", feed.getPaymentLink()); + assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url); assertEquals("http://example.com/picture", feed.getImageUrl()); assertEquals(10, feed.getItems().size()); for (int i = 0; i < feed.getItems().size(); i++) { @@ -62,7 +63,7 @@ public class RssParserTest { assertEquals("title", feed.getTitle()); assertEquals("http://example.com", feed.getLink()); assertEquals("This is the description", feed.getDescription()); - assertEquals("http://example.com/payment", feed.getPaymentLink()); + assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url); assertEquals("https://example.com/image.png", feed.getImageUrl()); assertEquals(0, feed.getItems().size()); } @@ -74,7 +75,7 @@ public class RssParserTest { assertEquals("title", feed.getTitle()); assertEquals("http://example.com", feed.getLink()); assertEquals("This is the description", feed.getDescription()); - assertEquals("http://example.com/payment", feed.getPaymentLink()); + assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url); assertNull(feed.getImageUrl()); assertEquals(1, feed.getItems().size()); FeedItem feedItem = feed.getItems().get(0); @@ -82,4 +83,17 @@ public class RssParserTest { assertEquals(MediaType.VIDEO, feedItem.getMedia().getMediaType()); assertEquals("https://www.example.com/file.mp4", feedItem.getMedia().getDownload_url()); } + + @Test + public void testMultipleFundingTags() throws Exception { + File feedFile = FeedParserTestHelper.getFeedFile("feed-rss-testMultipleFundingTags.xml"); + Feed feed = FeedParserTestHelper.runFeedParser(feedFile); + assertEquals(3, feed.getPaymentLinks().size()); + assertEquals("Text 1", feed.getPaymentLinks().get(0).content); + assertEquals("https://example.com/funding1", feed.getPaymentLinks().get(0).url); + assertEquals("Text 2", feed.getPaymentLinks().get(1).content); + assertEquals("https://example.com/funding2", feed.getPaymentLinks().get(1).url); + assertTrue(TextUtils.isEmpty(feed.getPaymentLinks().get(2).content)); + assertEquals("https://example.com/funding3", feed.getPaymentLinks().get(2).url); + } } diff --git a/core/src/test/resources/feed-rss-testMultipleFundingTags.xml b/core/src/test/resources/feed-rss-testMultipleFundingTags.xml new file mode 100644 index 000000000..2535bda32 --- /dev/null +++ b/core/src/test/resources/feed-rss-testMultipleFundingTags.xml @@ -0,0 +1,9 @@ +<?xml version='1.0' encoding='UTF-8' ?> +<rss version="2.0" xmlns:podcast="https://podcastindex.org/namespace/1.0"> + <channel> + <title>title</title> + </channel> + <podcast:funding url="https://example.com/funding1">Text 1</podcast:funding> + <podcast:funding url="https://example.com/funding2">Text 2</podcast:funding> + <podcast:funding url="https://example.com/funding3" /> +</rss> |