From 24389d42e89037b205fff2bc681e4ad998895286 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sat, 28 Aug 2021 00:12:48 +0200 Subject: Moved feed parser to its own module --- .../antennapod/core/export/opml/OpmlWriter.java | 4 +- .../antennapod/core/feed/LocalFeedUpdater.java | 2 +- .../danoeh/antennapod/core/feed/SimpleChapter.java | 16 -- .../core/service/download/HttpDownloader.java | 2 +- .../service/download/handler/FeedParserTask.java | 6 +- .../service/download/handler/FeedSyncTask.java | 2 +- .../core/storage/mapper/ChapterCursorMapper.java | 2 +- .../core/syndication/handler/FeedHandler.java | 90 -------- .../syndication/handler/FeedHandlerResult.java | 19 -- .../core/syndication/handler/HandlerState.java | 121 ----------- .../core/syndication/handler/SyndHandler.java | 138 ------------- .../core/syndication/handler/TypeGetter.java | 120 ----------- .../handler/UnsupportedFeedtypeException.java | 44 ---- .../core/syndication/namespace/NSContent.java | 25 --- .../core/syndication/namespace/NSDublinCore.java | 37 ---- .../core/syndication/namespace/NSITunes.java | 113 ----------- .../core/syndication/namespace/NSMedia.java | 132 ------------ .../core/syndication/namespace/NSRSS20.java | 145 ------------- .../syndication/namespace/NSSimpleChapters.java | 53 ----- .../core/syndication/namespace/Namespace.java | 21 -- .../core/syndication/namespace/PodcastIndex.java | 38 ---- .../core/syndication/namespace/SyndElement.java | 22 -- .../core/syndication/namespace/atom/AtomText.java | 38 ---- .../core/syndication/namespace/atom/NSAtom.java | 226 --------------------- .../core/syndication/parsers/DurationParser.java | 37 ---- .../core/syndication/util/SyndStringUtils.java | 14 -- .../core/syndication/util/SyndTypeUtils.java | 44 ---- .../danoeh/antennapod/core/util/DateFormatter.java | 46 +++++ .../de/danoeh/antennapod/core/util/DateUtils.java | 202 ------------------ 29 files changed, 55 insertions(+), 1704 deletions(-) delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/parsers/DurationParser.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndStringUtils.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndTypeUtils.java create mode 100644 core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java index e2205471c..a44d90557 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java @@ -4,6 +4,7 @@ import android.content.Context; import android.util.Log; import android.util.Xml; +import de.danoeh.antennapod.core.util.DateFormatter; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; @@ -13,7 +14,6 @@ import java.util.List; import de.danoeh.antennapod.core.export.ExportWriter; import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.core.util.DateUtils; /** Writes OPML documents. */ public class OpmlWriter implements ExportWriter { @@ -44,7 +44,7 @@ public class OpmlWriter implements ExportWriter { xs.text(OPML_TITLE); xs.endTag(null, OpmlSymbols.TITLE); xs.startTag(null, OpmlSymbols.DATE_CREATED); - xs.text(DateUtils.formatRFC822Date(new Date())); + xs.text(DateFormatter.formatRfc822Date(new Date())); xs.endTag(null, OpmlSymbols.DATE_CREATED); xs.endTag(null, OpmlSymbols.HEAD); diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java index 7a8c4969b..82583b7b5 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java @@ -27,7 +27,7 @@ import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.DateUtils; +import de.danoeh.antennapod.parser.feed.util.DateUtils; import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java deleted file mode 100644 index ca59f867b..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java +++ /dev/null @@ -1,16 +0,0 @@ -package de.danoeh.antennapod.core.feed; - -import de.danoeh.antennapod.model.feed.Chapter; - -public class SimpleChapter extends Chapter { - public static final int CHAPTERTYPE_SIMPLECHAPTER = 0; - - public SimpleChapter(long start, String title, String link, String imageUrl) { - super(start, title, link, imageUrl); - } - - @Override - public int getChapterType() { - return CHAPTERTYPE_SIMPLECHAPTER; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java index 1320b7052..781110f82 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java @@ -24,7 +24,7 @@ import java.util.regex.Pattern; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.core.util.DateUtils; +import de.danoeh.antennapod.parser.feed.util.DateUtils; import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.core.util.URIUtil; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java index 3e3da00e7..9a0916109 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java @@ -8,9 +8,9 @@ import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting; import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DownloadRequester; -import de.danoeh.antennapod.core.syndication.handler.FeedHandler; -import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult; -import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException; +import de.danoeh.antennapod.parser.feed.FeedHandler; +import de.danoeh.antennapod.parser.feed.FeedHandlerResult; +import de.danoeh.antennapod.parser.feed.UnsupportedFeedtypeException; import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.InvalidFeedException; import org.xml.sax.SAXException; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java index 1ca4d1194..dcc1c8fdb 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java @@ -9,7 +9,7 @@ import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; -import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult; +import de.danoeh.antennapod.parser.feed.FeedHandlerResult; public class FeedSyncTask { private static final String TAG = "FeedParserTask"; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java index b171f2bcc..61613a25a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java @@ -4,7 +4,7 @@ import android.database.Cursor; import androidx.annotation.NonNull; import de.danoeh.antennapod.model.feed.Chapter; import de.danoeh.antennapod.core.feed.ID3Chapter; -import de.danoeh.antennapod.core.feed.SimpleChapter; +import de.danoeh.antennapod.parser.feed.element.SimpleChapter; import de.danoeh.antennapod.core.feed.VorbisCommentChapter; import de.danoeh.antennapod.core.storage.PodDBAdapter; diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java deleted file mode 100644 index 2928ba836..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java +++ /dev/null @@ -1,90 +0,0 @@ -package de.danoeh.antennapod.core.syndication.handler; - -import android.text.TextUtils; -import android.util.Log; - -import org.apache.commons.io.input.XmlStreamReader; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.model.feed.FeedItem; - -public class FeedHandler { - private static final String TAG = "FeedHandler"; - - public FeedHandlerResult parseFeed(Feed feed) throws SAXException, IOException, - ParserConfigurationException, UnsupportedFeedtypeException { - TypeGetter tg = new TypeGetter(); - TypeGetter.Type type = tg.getType(feed); - SyndHandler handler = new SyndHandler(feed, type); - - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - SAXParser saxParser = factory.newSAXParser(); - File file = new File(feed.getFile_url()); - Reader inputStreamReader = new XmlStreamReader(file); - InputSource inputSource = new InputSource(inputStreamReader); - - saxParser.parse(inputSource, handler); - inputStreamReader.close(); - feed.setItems(dedupItems(feed.getItems())); - return new FeedHandlerResult(handler.state.feed, handler.state.alternateUrls); - } - - /** - * For updating items that are stored in the database, see also: DBTasks.searchFeedItemByIdentifyingValue - */ - public static List dedupItems(List items) { - if (items == null) { - return null; - } - List list = new ArrayList<>(items); - Set seen = new HashSet<>(); - Iterator it = list.iterator(); - while (it.hasNext()) { - FeedItem item = it.next(); - if (!TextUtils.isEmpty(item.getItemIdentifier()) && seen.contains(item.getItemIdentifier())) { - Log.d(TAG, "Removing duplicate episode guid " + item.getItemIdentifier()); - it.remove(); - continue; - } - - if (item.getMedia() == null || TextUtils.isEmpty(item.getMedia().getStreamUrl())) { - continue; - } - if (seen.contains(item.getMedia().getStreamUrl())) { - Log.d(TAG, "Removing duplicate episode stream url " + item.getMedia().getStreamUrl()); - it.remove(); - } else { - seen.add(item.getMedia().getStreamUrl()); - if (TextUtils.isEmpty(item.getTitle()) || item.getPubDate() == null) { - continue; - } - if (!seen.contains(item.getTitle() + item.getPubDate().toString())) { - seen.add(item.getTitle() + item.getPubDate().toString()); - } else { - Log.d(TAG, "Removing duplicate episode title and pubDate " - + item.getTitle() - + " " + item.getPubDate()); - it.remove(); - } - } - seen.add(item.getItemIdentifier()); - } - return list; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java deleted file mode 100644 index fb4bf4707..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.danoeh.antennapod.core.syndication.handler; - -import java.util.Map; - -import de.danoeh.antennapod.model.feed.Feed; - -/** - * Container for results returned by the Feed parser - */ -public class FeedHandlerResult { - - public final Feed feed; - public final Map alternateFeedUrls; - - public FeedHandlerResult(Feed feed, Map alternateFeedUrls) { - this.feed = feed; - this.alternateFeedUrls = alternateFeedUrls; - } -} 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 deleted file mode 100644 index 2fecb0536..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java +++ /dev/null @@ -1,121 +0,0 @@ -package de.danoeh.antennapod.core.syndication.handler; - -import androidx.collection.ArrayMap; - -import java.util.ArrayList; -import java.util.Map; -import java.util.Stack; - -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.model.feed.FeedFunding; -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.core.syndication.namespace.Namespace; -import de.danoeh.antennapod.core.syndication.namespace.SyndElement; - -/** - * Contains all relevant information to describe the current state of a - * SyndHandler. - */ -public class HandlerState { - - /** - * Feed that the Handler is currently processing. - */ - 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 - */ - final Map alternateUrls; - private final ArrayList items; - private FeedItem currentItem; - private FeedFunding currentFunding; - final Stack tagstack; - /** - * Namespaces that have been defined so far. - */ - final Map namespaces; - final Stack defaultNamespaces; - /** - * Buffer for saving characters. - */ - protected StringBuilder contentBuf; - - /** - * Temporarily saved objects. - */ - private final Map tempObjects; - - public HandlerState(Feed feed) { - this.feed = feed; - alternateUrls = new ArrayMap<>(); - items = new ArrayList<>(); - tagstack = new Stack<>(); - namespaces = new ArrayMap<>(); - defaultNamespaces = new Stack<>(); - tempObjects = new ArrayMap<>(); - } - - 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; - } - - 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. - */ - 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 StringBuilder getContentBuf() { - return contentBuf; - } - - public void addAlternateFeedUrl(String title, String url) { - alternateUrls.put(url, title); - } - - public Map getTempObjects() { - return tempObjects; - } -} 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 deleted file mode 100644 index 9c09be714..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java +++ /dev/null @@ -1,138 +0,0 @@ -package de.danoeh.antennapod.core.syndication.handler; - -import android.util.Log; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.core.syndication.namespace.NSContent; -import de.danoeh.antennapod.core.syndication.namespace.NSDublinCore; -import de.danoeh.antennapod.core.syndication.namespace.NSITunes; -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; - -/** Superclass for all SAX Handlers which process Syndication formats */ -class SyndHandler extends DefaultHandler { - private static final String TAG = "SyndHandler"; - private static final String DEFAULT_PREFIX = ""; - final HandlerState state; - - public SyndHandler(Feed feed, TypeGetter.Type type) { - state = new HandlerState(feed); - if (type == TypeGetter.Type.RSS20 || type == TypeGetter.Type.RSS091) { - state.defaultNamespaces.push(new NSRSS20()); - } - } - - @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws SAXException { - state.contentBuf = new StringBuilder(); - Namespace handler = getHandlingNamespace(uri, qName); - if (handler != null) { - SyndElement element = handler.handleElementStart(localName, state, - attributes); - state.tagstack.push(element); - - } - } - - @Override - public void characters(char[] ch, int start, int length) - throws SAXException { - if (!state.tagstack.empty()) { - if (state.getTagstack().size() >= 2) { - if (state.contentBuf != null) { - state.contentBuf.append(ch, start, length); - } - } - } - } - - @Override - public void endElement(String uri, String localName, String qName) - throws SAXException { - Namespace handler = getHandlingNamespace(uri, qName); - if (handler != null) { - handler.handleElementEnd(localName, state); - state.tagstack.pop(); - - } - state.contentBuf = null; - - } - - @Override - public void endPrefixMapping(String prefix) throws SAXException { - if (state.defaultNamespaces.size() > 1 && prefix.equals(DEFAULT_PREFIX)) { - state.defaultNamespaces.pop(); - } - } - - @Override - public void startPrefixMapping(String prefix, String uri) - throws SAXException { - // Find the right namespace - if (!state.namespaces.containsKey(uri)) { - if (uri.equals(NSAtom.NSURI)) { - if (prefix.equals(DEFAULT_PREFIX)) { - state.defaultNamespaces.push(new NSAtom()); - } else if (prefix.equals(NSAtom.NSTAG)) { - state.namespaces.put(uri, new NSAtom()); - Log.d(TAG, "Recognized Atom namespace"); - } - } else if (uri.equals(NSContent.NSURI) - && prefix.equals(NSContent.NSTAG)) { - state.namespaces.put(uri, new NSContent()); - Log.d(TAG, "Recognized Content namespace"); - } else if (uri.equals(NSITunes.NSURI) - && prefix.equals(NSITunes.NSTAG)) { - state.namespaces.put(uri, new NSITunes()); - Log.d(TAG, "Recognized ITunes namespace"); - } else if (uri.equals(NSSimpleChapters.NSURI) - && prefix.matches(NSSimpleChapters.NSTAG)) { - state.namespaces.put(uri, new NSSimpleChapters()); - Log.d(TAG, "Recognized SimpleChapters namespace"); - } else if (uri.equals(NSMedia.NSURI) - && prefix.equals(NSMedia.NSTAG)) { - state.namespaces.put(uri, new NSMedia()); - Log.d(TAG, "Recognized media namespace"); - } else if (uri.equals(NSDublinCore.NSURI) - && 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); - if (handler == null && !state.defaultNamespaces.empty() - && !qName.contains(":")) { - handler = state.defaultNamespaces.peek(); - } - return handler; - } - - @Override - public void endDocument() throws SAXException { - super.endDocument(); - state.getFeed().setItems(state.getItems()); - } - - public HandlerState getState() { - return state; - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java deleted file mode 100644 index e6011e3fa..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java +++ /dev/null @@ -1,120 +0,0 @@ -package de.danoeh.antennapod.core.syndication.handler; - -import android.util.Log; - -import org.apache.commons.io.input.XmlStreamReader; -import org.jsoup.Jsoup; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.Reader; - -import de.danoeh.antennapod.model.feed.Feed; - -/** Gets the type of a specific feed by reading the root element. */ -public class TypeGetter { - private static final String TAG = "TypeGetter"; - - public enum Type { - RSS20, RSS091, ATOM, INVALID - } - - private static final String ATOM_ROOT = "feed"; - private static final String RSS_ROOT = "rss"; - - public Type getType(Feed feed) throws UnsupportedFeedtypeException { - XmlPullParserFactory factory; - if (feed.getFile_url() != null) { - Reader reader = null; - try { - factory = XmlPullParserFactory.newInstance(); - factory.setNamespaceAware(true); - XmlPullParser xpp = factory.newPullParser(); - reader = createReader(feed); - xpp.setInput(reader); - int eventType = xpp.getEventType(); - - while (eventType != XmlPullParser.END_DOCUMENT) { - if (eventType == XmlPullParser.START_TAG) { - String tag = xpp.getName(); - switch (tag) { - case ATOM_ROOT: - feed.setType(Feed.TYPE_ATOM1); - Log.d(TAG, "Recognized type Atom"); - - String strLang = xpp.getAttributeValue("http://www.w3.org/XML/1998/namespace", "lang"); - if (strLang != null) { - feed.setLanguage(strLang); - } - - return Type.ATOM; - case RSS_ROOT: - String strVersion = xpp.getAttributeValue(null, "version"); - if (strVersion == null) { - feed.setType(Feed.TYPE_RSS2); - Log.d(TAG, "Assuming type RSS 2.0"); - return Type.RSS20; - } else if (strVersion.equals("2.0")) { - feed.setType(Feed.TYPE_RSS2); - Log.d(TAG, "Recognized type RSS 2.0"); - return Type.RSS20; - } else if (strVersion.equals("0.91") || strVersion.equals("0.92")) { - Log.d(TAG, "Recognized type RSS 0.91/0.92"); - return Type.RSS091; - } - throw new UnsupportedFeedtypeException("Unsupported rss version"); - default: - Log.d(TAG, "Type is invalid"); - throw new UnsupportedFeedtypeException(Type.INVALID, tag); - } - } else { - eventType = xpp.next(); - } - } - } catch (XmlPullParserException e) { - e.printStackTrace(); - // XML document might actually be a HTML document -> try to parse as HTML - String rootElement = null; - try { - if (Jsoup.parse(new File(feed.getFile_url()), null) != null) { - rootElement = "html"; - } - } catch (IOException e1) { - e1.printStackTrace(); - } - throw new UnsupportedFeedtypeException(Type.INVALID, rootElement); - - } catch (IOException e) { - e.printStackTrace(); - } finally { - if(reader != null) { - try { - reader.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - Log.d(TAG, "Type is invalid"); - throw new UnsupportedFeedtypeException(Type.INVALID); - } - - private Reader createReader(Feed feed) { - Reader reader; - try { - reader = new XmlStreamReader(new File(feed.getFile_url())); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; - } catch (IOException e) { - e.printStackTrace(); - return null; - } - return reader; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java deleted file mode 100644 index c9f9f19c8..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.danoeh.antennapod.core.syndication.handler; - -import de.danoeh.antennapod.core.syndication.handler.TypeGetter.Type; - -public class UnsupportedFeedtypeException extends Exception { - private static final long serialVersionUID = 9105878964928170669L; - private final TypeGetter.Type type; - private String rootElement; - private String message = null; - - public UnsupportedFeedtypeException(Type type) { - super(); - this.type = type; - } - - public UnsupportedFeedtypeException(Type type, String rootElement) { - this.type = type; - this.rootElement = rootElement; - } - - public UnsupportedFeedtypeException(String message) { - this.message = message; - type = Type.INVALID; - } - - public TypeGetter.Type getType() { - return type; - } - - public String getRootElement() { - return rootElement; - } - - @Override - public String getMessage() { - if (message != null) { - return message; - } else if (type == TypeGetter.Type.INVALID) { - return "Invalid type"; - } else { - return "Type " + type + " not supported"; - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java deleted file mode 100644 index bedf377aa..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java +++ /dev/null @@ -1,25 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import org.xml.sax.Attributes; - -import de.danoeh.antennapod.core.syndication.handler.HandlerState; - -public class NSContent extends Namespace { - public static final String NSTAG = "content"; - public static final String NSURI = "http://purl.org/rss/1.0/modules/content/"; - - private static final String ENCODED = "encoded"; - - @Override - public SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes) { - return new SyndElement(localName, this); - } - - @Override - public void handleElementEnd(String localName, HandlerState state) { - if (ENCODED.equals(localName) && state.getCurrentItem() != null && state.getContentBuf() != null) { - state.getCurrentItem().setDescriptionIfLonger(state.getContentBuf().toString()); - } - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java deleted file mode 100644 index 0394b754a..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import org.xml.sax.Attributes; - -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.core.syndication.handler.HandlerState; -import de.danoeh.antennapod.core.util.DateUtils; - -public class NSDublinCore extends Namespace { - private static final String TAG = "NSDublinCore"; - public static final String NSTAG = "dc"; - public static final String NSURI = "http://purl.org/dc/elements/1.1/"; - - private static final String ITEM = "item"; - private static final String DATE = "date"; - - @Override - public SyndElement handleElementStart(String localName, HandlerState state, - Attributes attributes) { - return new SyndElement(localName, this); - } - - @Override - public void handleElementEnd(String localName, HandlerState state) { - if (state.getCurrentItem() != null && state.getContentBuf() != null && - state.getTagstack() != null && state.getTagstack().size() >= 2) { - FeedItem currentItem = state.getCurrentItem(); - String top = state.getTagstack().peek().getName(); - String second = state.getSecondTag().getName(); - if (DATE.equals(top) && ITEM.equals(second)) { - String content = state.getContentBuf().toString(); - currentItem.setPubDate(DateUtils.parseOrNullIfFuture(content)); - } - } - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java deleted file mode 100644 index 1dc8d8af3..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java +++ /dev/null @@ -1,113 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import android.text.TextUtils; -import android.util.Log; - -import androidx.core.text.HtmlCompat; - -import org.xml.sax.Attributes; - -import de.danoeh.antennapod.core.syndication.handler.HandlerState; -import de.danoeh.antennapod.core.syndication.parsers.DurationParser; - -public class NSITunes extends Namespace { - - public static final String NSTAG = "itunes"; - public static final String NSURI = "http://www.itunes.com/dtds/podcast-1.0.dtd"; - - private static final String IMAGE = "image"; - private static final String IMAGE_HREF = "href"; - - private static final String AUTHOR = "author"; - public static final String DURATION = "duration"; - private static final String SUBTITLE = "subtitle"; - private static final String SUMMARY = "summary"; - - @Override - public SyndElement handleElementStart(String localName, HandlerState state, - Attributes attributes) { - if (IMAGE.equals(localName)) { - String url = attributes.getValue(IMAGE_HREF); - - if (state.getCurrentItem() != null) { - state.getCurrentItem().setImageUrl(url); - } else { - // this is the feed image - // prefer to all other images - if (!TextUtils.isEmpty(url)) { - state.getFeed().setImageUrl(url); - } - } - } - return new SyndElement(localName, this); - } - - @Override - public void handleElementEnd(String localName, HandlerState state) { - if (state.getContentBuf() == null) { - return; - } - - if (AUTHOR.equals(localName)) { - parseAuthor(state); - } else if (DURATION.equals(localName)) { - parseDuration(state); - } else if (SUBTITLE.equals(localName)) { - parseSubtitle(state); - } else if (SUMMARY.equals(localName)) { - SyndElement secondElement = state.getSecondTag(); - parseSummary(state, secondElement.getName()); - } - } - - private void parseAuthor(HandlerState state) { - if (state.getFeed() != null) { - String author = state.getContentBuf().toString(); - state.getFeed().setAuthor(HtmlCompat.fromHtml(author, - HtmlCompat.FROM_HTML_MODE_LEGACY).toString()); - } - } - - private void parseDuration(HandlerState state) { - String durationStr = state.getContentBuf().toString(); - if (TextUtils.isEmpty(durationStr)) { - return; - } - - try { - long durationMs = DurationParser.inMillis(durationStr); - state.getTempObjects().put(DURATION, (int) durationMs); - } catch (NumberFormatException e) { - Log.e(NSTAG, String.format("Duration '%s' could not be parsed", durationStr)); - } - } - - private void parseSubtitle(HandlerState state) { - String subtitle = state.getContentBuf().toString(); - if (TextUtils.isEmpty(subtitle)) { - return; - } - if (state.getCurrentItem() != null) { - if (TextUtils.isEmpty(state.getCurrentItem().getDescription())) { - state.getCurrentItem().setDescriptionIfLonger(subtitle); - } - } else { - if (state.getFeed() != null && TextUtils.isEmpty(state.getFeed().getDescription())) { - state.getFeed().setDescription(subtitle); - } - } - } - - private void parseSummary(HandlerState state, String secondElementName) { - String summary = state.getContentBuf().toString(); - if (TextUtils.isEmpty(summary)) { - return; - } - - if (state.getCurrentItem() != null) { - state.getCurrentItem().setDescriptionIfLonger(summary); - } else if (NSRSS20.CHANNEL.equals(secondElementName) && state.getFeed() != null) { - state.getFeed().setDescription(summary); - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java deleted file mode 100644 index 3dba0735d..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java +++ /dev/null @@ -1,132 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import android.text.TextUtils; -import android.util.Log; - -import org.xml.sax.Attributes; - -import java.util.concurrent.TimeUnit; - -import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.core.syndication.handler.HandlerState; -import de.danoeh.antennapod.core.syndication.namespace.atom.AtomText; -import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils; - -/** Processes tags from the http://search.yahoo.com/mrss/ namespace. */ -public class NSMedia extends Namespace { - private static final String TAG = "NSMedia"; - - public static final String NSTAG = "media"; - public static final String NSURI = "http://search.yahoo.com/mrss/"; - - private static final String CONTENT = "content"; - private static final String DOWNLOAD_URL = "url"; - private static final String SIZE = "fileSize"; - private static final String MIME_TYPE = "type"; - private static final String DURATION = "duration"; - private static final String DEFAULT = "isDefault"; - private static final String MEDIUM = "medium"; - - private static final String MEDIUM_IMAGE = "image"; - private static final String MEDIUM_AUDIO = "audio"; - private static final String MEDIUM_VIDEO = "video"; - - private static final String IMAGE = "thumbnail"; - private static final String IMAGE_URL = "url"; - - private static final String DESCRIPTION = "description"; - private static final String DESCRIPTION_TYPE = "type"; - - @Override - public SyndElement handleElementStart(String localName, HandlerState state, - Attributes attributes) { - if (CONTENT.equals(localName)) { - String url = attributes.getValue(DOWNLOAD_URL); - String type = attributes.getValue(MIME_TYPE); - String defaultStr = attributes.getValue(DEFAULT); - String medium = attributes.getValue(MEDIUM); - boolean validTypeMedia = false; - boolean validTypeImage = false; - boolean isDefault = "true".equals(defaultStr); - String guessedType = SyndTypeUtils.getMimeTypeFromUrl(url); - - if (MEDIUM_AUDIO.equals(medium)) { - validTypeMedia = true; - type = "audio/*"; - } else if (MEDIUM_VIDEO.equals(medium)) { - validTypeMedia = true; - type = "video/*"; - } else if (MEDIUM_IMAGE.equals(medium) && (guessedType == null - || (!guessedType.startsWith("audio/") && !guessedType.startsWith("video/")))) { - // Apparently, some publishers explicitly specify the audio file as an image - validTypeImage = true; - type = "image/*"; - } else { - if (type == null) { - type = guessedType; - } - - if (SyndTypeUtils.enclosureTypeValid(type)) { - validTypeMedia = true; - } else if (SyndTypeUtils.imageTypeValid(type)) { - validTypeImage = true; - } - } - - if (state.getCurrentItem() != null && (state.getCurrentItem().getMedia() == null || isDefault) - && url != null && validTypeMedia) { - long size = 0; - String sizeStr = attributes.getValue(SIZE); - try { - size = Long.parseLong(sizeStr); - } catch (NumberFormatException e) { - Log.e(TAG, "Size \"" + sizeStr + "\" could not be parsed."); - } - - int durationMs = 0; - String durationStr = attributes.getValue(DURATION); - if (!TextUtils.isEmpty(durationStr)) { - try { - long duration = Long.parseLong(durationStr); - durationMs = (int) TimeUnit.MILLISECONDS.convert(duration, TimeUnit.SECONDS); - } catch (NumberFormatException e) { - Log.e(TAG, "Duration \"" + durationStr + "\" could not be parsed"); - } - } - FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type); - if (durationMs > 0) { - media.setDuration(durationMs); - } - state.getCurrentItem().setMedia(media); - } else if (state.getCurrentItem() != null && url != null && validTypeImage) { - state.getCurrentItem().setImageUrl(url); - } - } else if (IMAGE.equals(localName)) { - String url = attributes.getValue(IMAGE_URL); - if (url != null) { - if (state.getCurrentItem() != null) { - state.getCurrentItem().setImageUrl(url); - } else { - if (state.getFeed().getImageUrl() == null) { - state.getFeed().setImageUrl(url); - } - } - } - } else if (DESCRIPTION.equals(localName)) { - String type = attributes.getValue(DESCRIPTION_TYPE); - return new AtomText(localName, this, type); - } - return new SyndElement(localName, this); - } - - @Override - public void handleElementEnd(String localName, HandlerState state) { - if (DESCRIPTION.equals(localName)) { - String content = state.getContentBuf().toString(); - if (state.getCurrentItem() != null) { - state.getCurrentItem().setDescriptionIfLonger(content); - } - } - } -} - diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java deleted file mode 100644 index 50c2dc118..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java +++ /dev/null @@ -1,145 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import android.text.TextUtils; -import android.util.Log; - -import de.danoeh.antennapod.core.syndication.util.SyndStringUtils; -import org.xml.sax.Attributes; - -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.core.syndication.handler.HandlerState; -import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils; -import de.danoeh.antennapod.core.util.DateUtils; - -/** - * SAX-Parser for reading RSS-Feeds. - */ -public class NSRSS20 extends Namespace { - - private static final String TAG = "NSRSS20"; - - public static final String CHANNEL = "channel"; - public static final String ITEM = "item"; - private static final String GUID = "guid"; - private static final String TITLE = "title"; - private static final String LINK = "link"; - private static final String DESCR = "description"; - private static final String PUBDATE = "pubDate"; - private static final String ENCLOSURE = "enclosure"; - private static final String IMAGE = "image"; - private static final String URL = "url"; - private static final String LANGUAGE = "language"; - - private static final String ENC_URL = "url"; - private static final String ENC_LEN = "length"; - private static final String ENC_TYPE = "type"; - - @Override - public SyndElement handleElementStart(String localName, HandlerState state, - Attributes attributes) { - if (ITEM.equals(localName)) { - state.setCurrentItem(new FeedItem()); - state.getItems().add(state.getCurrentItem()); - state.getCurrentItem().setFeed(state.getFeed()); - - } else if (ENCLOSURE.equals(localName)) { - String type = attributes.getValue(ENC_TYPE); - String url = attributes.getValue(ENC_URL); - - boolean validType = SyndTypeUtils.enclosureTypeValid(type); - if (!validType) { - type = SyndTypeUtils.getMimeTypeFromUrl(url); - validType = SyndTypeUtils.enclosureTypeValid(type); - } - - boolean validUrl = !TextUtils.isEmpty(url); - if (state.getCurrentItem() != null && state.getCurrentItem().getMedia() == null - && validType && validUrl) { - long size = 0; - try { - size = Long.parseLong(attributes.getValue(ENC_LEN)); - if (size < 16384) { - // less than 16kb is suspicious, check manually - size = 0; - } - } catch (NumberFormatException e) { - Log.d(TAG, "Length attribute could not be parsed."); - } - FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type); - state.getCurrentItem().setMedia(media); - } - - } - return new SyndElement(localName, this); - } - - @Override - public void handleElementEnd(String localName, HandlerState state) { - if (ITEM.equals(localName)) { - if (state.getCurrentItem() != null) { - FeedItem currentItem = state.getCurrentItem(); - // the title tag is optional in RSS 2.0. The description is used - // as a title if the item has no title-tag. - if (currentItem.getTitle() == null) { - currentItem.setTitle(currentItem.getDescription()); - } - - if (state.getTempObjects().containsKey(NSITunes.DURATION)) { - if (currentItem.hasMedia()) { - Integer duration = (Integer) state.getTempObjects().get(NSITunes.DURATION); - currentItem.getMedia().setDuration(duration); - } - state.getTempObjects().remove(NSITunes.DURATION); - } - } - state.setCurrentItem(null); - } else if (state.getTagstack().size() >= 2 && state.getContentBuf() != null) { - String contentRaw = state.getContentBuf().toString(); - String content = SyndStringUtils.trimAllWhitespace(contentRaw); - SyndElement topElement = state.getTagstack().peek(); - String top = topElement.getName(); - SyndElement secondElement = state.getSecondTag(); - String second = secondElement.getName(); - String third = null; - if (state.getTagstack().size() >= 3) { - third = state.getThirdTag().getName(); - } - if (GUID.equals(top) && ITEM.equals(second)) { - // some feed creators include an empty or non-standard guid-element in their feed, - // which should be ignored - if (!TextUtils.isEmpty(contentRaw) && state.getCurrentItem() != null) { - state.getCurrentItem().setItemIdentifier(contentRaw); - } - } else if (TITLE.equals(top)) { - if (ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setTitle(content); - } else if (CHANNEL.equals(second) && state.getFeed() != null) { - state.getFeed().setTitle(content); - } - } else if (LINK.equals(top)) { - if (CHANNEL.equals(second) && state.getFeed() != null) { - state.getFeed().setLink(content); - } else if (ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setLink(content); - } - } else if (PUBDATE.equals(top) && ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setPubDate(DateUtils.parseOrNullIfFuture(content)); - } else if (URL.equals(top) && IMAGE.equals(second) && CHANNEL.equals(third)) { - // prefer itunes:image - if (state.getFeed() != null && state.getFeed().getImageUrl() == null) { - state.getFeed().setImageUrl(content); - } - } else if (DESCR.equals(localName)) { - if (CHANNEL.equals(second) && state.getFeed() != null) { - state.getFeed().setDescription(content); - } else if (ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setDescriptionIfLonger(content); - } - } else if (LANGUAGE.equals(localName) && state.getFeed() != null) { - state.getFeed().setLanguage(content.toLowerCase()); - } - } - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java deleted file mode 100644 index 97d0ebb53..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import android.util.Log; - -import org.xml.sax.Attributes; - -import java.util.ArrayList; - -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.core.feed.SimpleChapter; -import de.danoeh.antennapod.core.syndication.handler.HandlerState; -import de.danoeh.antennapod.core.util.DateUtils; - -public class NSSimpleChapters extends Namespace { - private static final String TAG = "NSSimpleChapters"; - - public static final String NSTAG = "psc|sc"; - public static final String NSURI = "http://podlove.org/simple-chapters"; - - private static final String CHAPTERS = "chapters"; - private static final String CHAPTER = "chapter"; - private static final String START = "start"; - private static final String TITLE = "title"; - private static final String HREF = "href"; - private static final String IMAGE = "image"; - - @Override - public SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes) { - FeedItem currentItem = state.getCurrentItem(); - if (currentItem != null) { - if (localName.equals(CHAPTERS)) { - currentItem.setChapters(new ArrayList<>()); - } else if (localName.equals(CHAPTER)) { - try { - long start = DateUtils.parseTimeString(attributes.getValue(START)); - String title = attributes.getValue(TITLE); - String link = attributes.getValue(HREF); - String imageUrl = attributes.getValue(IMAGE); - SimpleChapter chapter = new SimpleChapter(start, title, link, imageUrl); - currentItem.getChapters().add(chapter); - } catch (NumberFormatException e) { - Log.e(TAG, "Unable to read chapter", e); - } - } - } - return new SyndElement(localName, this); - } - - @Override - public void handleElementEnd(String localName, HandlerState state) { - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java deleted file mode 100644 index e5fbdb9bb..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import org.xml.sax.Attributes; - -import de.danoeh.antennapod.core.syndication.handler.HandlerState; - - -public abstract class Namespace { - public static final String NSTAG = null; - public static final String NSURI = null; - - /** Called by a Feedhandler when in startElement and it detects a namespace element - * @return The SyndElement to push onto the stack - * */ - public abstract SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes); - - /** Called by a Feedhandler when in endElement and it detects a namespace element - * */ - public abstract void handleElementEnd(String localName, HandlerState state); - -} 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 deleted file mode 100644 index ee150f839..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -import org.jsoup.helper.StringUtil; -import org.xml.sax.Attributes; -import de.danoeh.antennapod.model.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/SyndElement.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java deleted file mode 100644 index ba1b8ba5c..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace; - -/** Defines a XML Element that is pushed on the tagstack */ -public class SyndElement { - private final String name; - private final Namespace namespace; - - public SyndElement(String name, Namespace namespace) { - this.name = name; - this.namespace = namespace; - } - - public Namespace getNamespace() { - return namespace; - } - - public String getName() { - return name; - } - - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java deleted file mode 100644 index 0c0561279..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace.atom; - -import androidx.core.text.HtmlCompat; - -import de.danoeh.antennapod.core.syndication.namespace.Namespace; -import de.danoeh.antennapod.core.syndication.namespace.SyndElement; - -/** Represents Atom Element which contains text (content, title, summary). */ -public class AtomText extends SyndElement { - public static final String TYPE_TEXT = "text"; - public static final String TYPE_HTML = "html"; - private static final String TYPE_XHTML = "xhtml"; - - private final String type; - private String content; - - public AtomText(String name, Namespace namespace, String type) { - super(name, namespace); - this.type = type; - } - - /** Processes the content according to the type and returns it. */ - public String getProcessedContent() { - if (type == null) { - return content; - } else if (type.equals(TYPE_HTML)) { - return HtmlCompat.fromHtml(content, HtmlCompat.FROM_HTML_MODE_LEGACY).toString(); - } else if (type.equals(TYPE_XHTML)) { - return content; - } else { // Handle as text by default - return content; - } - } - - public void setContent(String content) { - this.content = 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 deleted file mode 100644 index b93f41771..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java +++ /dev/null @@ -1,226 +0,0 @@ -package de.danoeh.antennapod.core.syndication.namespace.atom; - -import android.text.TextUtils; -import android.util.Log; - -import de.danoeh.antennapod.model.feed.FeedFunding; -import de.danoeh.antennapod.core.syndication.util.SyndStringUtils; -import org.xml.sax.Attributes; - -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.core.syndication.handler.HandlerState; -import de.danoeh.antennapod.core.syndication.namespace.NSITunes; -import de.danoeh.antennapod.core.syndication.namespace.NSRSS20; -import de.danoeh.antennapod.core.syndication.namespace.Namespace; -import de.danoeh.antennapod.core.syndication.namespace.SyndElement; -import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils; -import de.danoeh.antennapod.core.util.DateUtils; - -public class NSAtom extends Namespace { - private static final String TAG = "NSAtom"; - public static final String NSTAG = "atom"; - public static final String NSURI = "http://www.w3.org/2005/Atom"; - - private static final String FEED = "feed"; - private static final String ID = "id"; - private static final String TITLE = "title"; - private static final String ENTRY = "entry"; - private static final String LINK = "link"; - private static final String UPDATED = "updated"; - private static final String AUTHOR = "author"; - private static final String AUTHOR_NAME = "name"; - private static final String CONTENT = "content"; - private static final String SUMMARY = "summary"; - private static final String IMAGE_LOGO = "logo"; - private static final String IMAGE_ICON = "icon"; - private static final String SUBTITLE = "subtitle"; - private static final String PUBLISHED = "published"; - - private static final String TEXT_TYPE = "type"; - // Link - private static final String LINK_HREF = "href"; - private static final String LINK_REL = "rel"; - private static final String LINK_TYPE = "type"; - private static final String LINK_TITLE = "title"; - private static final String LINK_LENGTH = "length"; - // rel-values - private static final String LINK_REL_ALTERNATE = "alternate"; - private static final String LINK_REL_ARCHIVES = "archives"; - private static final String LINK_REL_ENCLOSURE = "enclosure"; - private static final String LINK_REL_PAYMENT = "payment"; - private static final String LINK_REL_NEXT = "next"; - // type-values - private static final String LINK_TYPE_ATOM = "application/atom+xml"; - private static final String LINK_TYPE_HTML = "text/html"; - private static final String LINK_TYPE_XHTML = "application/xml+xhtml"; - - private static final String LINK_TYPE_RSS = "application/rss+xml"; - - /** - * Regexp to test whether an Element is a Text Element. - */ - private static final String isText = TITLE + "|" + CONTENT + "|" - + SUBTITLE + "|" + SUMMARY; - - private static final String isFeed = FEED + "|" + NSRSS20.CHANNEL; - private static final String isFeedItem = ENTRY + "|" + NSRSS20.ITEM; - - @Override - public SyndElement handleElementStart(String localName, HandlerState state, - Attributes attributes) { - if (ENTRY.equals(localName)) { - state.setCurrentItem(new FeedItem()); - state.getItems().add(state.getCurrentItem()); - state.getCurrentItem().setFeed(state.getFeed()); - } else if (localName.matches(isText)) { - String type = attributes.getValue(TEXT_TYPE); - return new AtomText(localName, this, type); - } else if (LINK.equals(localName)) { - String href = attributes.getValue(LINK_HREF); - String rel = attributes.getValue(LINK_REL); - SyndElement parent = state.getTagstack().peek(); - if (parent.getName().matches(isFeedItem)) { - if (rel == null || LINK_REL_ALTERNATE.equals(rel)) { - state.getCurrentItem().setLink(href); - } else if (LINK_REL_ENCLOSURE.equals(rel)) { - String strSize = attributes.getValue(LINK_LENGTH); - long size = 0; - try { - if (strSize != null) { - size = Long.parseLong(strSize); - } - } catch (NumberFormatException e) { - Log.d(TAG, "Length attribute could not be parsed."); - } - String type = attributes.getValue(LINK_TYPE); - - if (type == null) { - type = SyndTypeUtils.getMimeTypeFromUrl(href); - } - - FeedItem currItem = state.getCurrentItem(); - if (SyndTypeUtils.enclosureTypeValid(type) && currItem != null && !currItem.hasMedia()) { - currItem.setMedia(new FeedMedia(currItem, href, size, type)); - } - } else if (LINK_REL_PAYMENT.equals(rel)) { - state.getCurrentItem().setPaymentLink(href); - } - } else if (parent.getName().matches(isFeed)) { - if (rel == null || LINK_REL_ALTERNATE.equals(rel)) { - String type = attributes.getValue(LINK_TYPE); - /* - * 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 (state.getFeed() != null && - ((type == null && state.getFeed().getLink() == null) || - (LINK_TYPE_HTML.equals(type) || LINK_TYPE_XHTML.equals(type)))) { - state.getFeed().setLink(href); - } else if (LINK_TYPE_ATOM.equals(type) || LINK_TYPE_RSS.equals(type)) { - // treat as podlove alternate feed - String title = attributes.getValue(LINK_TITLE); - if (TextUtils.isEmpty(title)) { - title = href; - } - state.addAlternateFeedUrl(title, href); - } - } else if (LINK_REL_ARCHIVES.equals(rel) && state.getFeed() != null) { - String type = attributes.getValue(LINK_TYPE); - if (LINK_TYPE_ATOM.equals(type) || LINK_TYPE_RSS.equals(type)) { - String title = attributes.getValue(LINK_TITLE); - if (TextUtils.isEmpty(title)) { - title = href; - } - state.addAlternateFeedUrl(title, href); - } else if (LINK_TYPE_HTML.equals(type) || LINK_TYPE_XHTML.equals(type)) { - //A Link such as to a directory such as iTunes - } - } else if (LINK_REL_PAYMENT.equals(rel) && state.getFeed() != null) { - state.getFeed().addPayment(new FeedFunding(href, "")); - } else if (LINK_REL_NEXT.equals(rel) && state.getFeed() != null) { - state.getFeed().setPaged(true); - state.getFeed().setNextPageLink(href); - } - } - } - return new SyndElement(localName, this); - } - - @Override - public void handleElementEnd(String localName, HandlerState state) { - if (ENTRY.equals(localName)) { - if (state.getCurrentItem() != null && - state.getTempObjects().containsKey(NSITunes.DURATION)) { - FeedItem currentItem = state.getCurrentItem(); - if (currentItem.hasMedia()) { - Integer duration = (Integer) state.getTempObjects().get(NSITunes.DURATION); - currentItem.getMedia().setDuration(duration); - } - state.getTempObjects().remove(NSITunes.DURATION); - } - state.setCurrentItem(null); - } - - if (state.getTagstack().size() >= 2) { - AtomText textElement = null; - String contentRaw; - if (state.getContentBuf() != null) { - contentRaw = state.getContentBuf().toString(); - } else { - contentRaw = ""; - } - String content = SyndStringUtils.trimAllWhitespace(contentRaw); - SyndElement topElement = state.getTagstack().peek(); - String top = topElement.getName(); - SyndElement secondElement = state.getSecondTag(); - String second = secondElement.getName(); - - if (top.matches(isText)) { - textElement = (AtomText) topElement; - textElement.setContent(content); - } - - if (ID.equals(top)) { - if (FEED.equals(second) && state.getFeed() != null) { - state.getFeed().setFeedIdentifier(contentRaw); - } else if (ENTRY.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setItemIdentifier(contentRaw); - } - } else if (TITLE.equals(top) && textElement != null) { - if (FEED.equals(second) && state.getFeed() != null) { - state.getFeed().setTitle(textElement.getProcessedContent()); - } else if (ENTRY.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setTitle(textElement.getProcessedContent()); - } - } else if (SUBTITLE.equals(top) && FEED.equals(second) && textElement != null && - state.getFeed() != null) { - state.getFeed().setDescription(textElement.getProcessedContent()); - } else if (CONTENT.equals(top) && ENTRY.equals(second) && textElement != null && - state.getCurrentItem() != null) { - state.getCurrentItem().setDescriptionIfLonger(textElement.getProcessedContent()); - } else if (SUMMARY.equals(top) && ENTRY.equals(second) && textElement != null - && state.getCurrentItem() != null) { - state.getCurrentItem().setDescriptionIfLonger(textElement.getProcessedContent()); - } else if (UPDATED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null && - state.getCurrentItem().getPubDate() == null) { - state.getCurrentItem().setPubDate(DateUtils.parseOrNullIfFuture(content)); - } else if (PUBLISHED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setPubDate(DateUtils.parseOrNullIfFuture(content)); - } else if (IMAGE_LOGO.equals(top) && state.getFeed() != null && state.getFeed().getImageUrl() == null) { - state.getFeed().setImageUrl(content); - } else if (IMAGE_ICON.equals(top) && state.getFeed() != null) { - state.getFeed().setImageUrl(content); - } else if (AUTHOR_NAME.equals(top) && AUTHOR.equals(second) && - state.getFeed() != null && state.getCurrentItem() == null) { - String currentName = state.getFeed().getAuthor(); - if (currentName == null) { - state.getFeed().setAuthor(content); - } else { - state.getFeed().setAuthor(currentName + ", " + content); - } - } - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/parsers/DurationParser.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/parsers/DurationParser.java deleted file mode 100644 index 8b036c6a9..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/parsers/DurationParser.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.danoeh.antennapod.core.syndication.parsers; - -import static java.util.concurrent.TimeUnit.HOURS; -import static java.util.concurrent.TimeUnit.MINUTES; -import static java.util.concurrent.TimeUnit.SECONDS; - -public class DurationParser { - public static long inMillis(String durationStr) throws NumberFormatException { - String[] parts = durationStr.trim().split(":"); - - if (parts.length == 1) { - return toMillis(parts[0]); - } else if (parts.length == 2) { - return toMillis("0", parts[0], parts[1]); - } else if (parts.length == 3) { - return toMillis(parts[0], parts[1], parts[2]); - } else { - throw new NumberFormatException(); - } - } - - private static long toMillis(String hours, String minutes, String seconds) { - return HOURS.toMillis(Long.parseLong(hours)) - + MINUTES.toMillis(Long.parseLong(minutes)) - + toMillis(seconds); - } - - private static long toMillis(String seconds) { - if (seconds.contains(".")) { - float value = Float.parseFloat(seconds); - float millis = value % 1; - return SECONDS.toMillis((long) value) + (long) (millis * 1000); - } else { - return SECONDS.toMillis(Long.parseLong(seconds)); - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndStringUtils.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndStringUtils.java deleted file mode 100644 index addcdd4b7..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndStringUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.danoeh.antennapod.core.syndication.util; - -public class SyndStringUtils { - private SyndStringUtils() { - - } - - /** - * Trims all whitespace from beginning and ending of a String. {{@link String#trim()}} only trims spaces. - */ - public static String trimAllWhitespace(String string) { - return string.replaceAll("(^\\s*)|(\\s*$)", ""); - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndTypeUtils.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndTypeUtils.java deleted file mode 100644 index 155673296..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndTypeUtils.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.danoeh.antennapod.core.syndication.util; - -import android.webkit.MimeTypeMap; -import org.apache.commons.io.FilenameUtils; - -/** - * Utility class for handling MIME-Types of enclosures. - * */ -public class SyndTypeUtils { - private SyndTypeUtils() { - - } - - public static boolean enclosureTypeValid(String type) { - if (type == null) { - return false; - } else { - return type.startsWith("audio/") - || type.startsWith("video/") - || type.equals("application/ogg") - || type.equals("application/octet-stream"); - } - } - - public static boolean imageTypeValid(String type) { - if (type == null) { - return false; - } else { - return type.startsWith("image/"); - } - } - - /** - * Should be used if mime-type of enclosure tag is not supported. This - * method will return the mime-type of the file extension. - */ - public static String getMimeTypeFromUrl(String url) { - if (url == null) { - return null; - } - String extension = FilenameUtils.getExtension(url); - return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java new file mode 100644 index 000000000..99628dfcc --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java @@ -0,0 +1,46 @@ +package de.danoeh.antennapod.core.util; + +import android.content.Context; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; + +/** + * Formats dates. + */ +public class DateFormatter { + private DateFormatter() { + + } + + public static String formatRfc822Date(Date date) { + SimpleDateFormat format = new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US); + return format.format(date); + } + + public static String formatAbbrev(final Context context, final Date date) { + if (date == null) { + return ""; + } + GregorianCalendar now = new GregorianCalendar(); + GregorianCalendar cal = new GregorianCalendar(); + cal.setTime(date); + boolean withinLastYear = now.get(Calendar.YEAR) == cal.get(Calendar.YEAR); + int format = android.text.format.DateUtils.FORMAT_ABBREV_ALL; + if (withinLastYear) { + format |= android.text.format.DateUtils.FORMAT_NO_YEAR; + } + return android.text.format.DateUtils.formatDateTime(context, date.getTime(), format); + } + + public static String formatForAccessibility(final Context context, final Date date) { + if (date == null) { + return ""; + } + return DateFormat.getDateInstance(DateFormat.LONG).format(date); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java deleted file mode 100644 index a0b9fbef9..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java +++ /dev/null @@ -1,202 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import android.content.Context; -import android.util.Log; - -import androidx.annotation.Nullable; -import org.apache.commons.lang3.StringUtils; - -import java.text.DateFormat; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Parses several date formats. - */ -public class DateUtils { - - private DateUtils(){} - - private static final String TAG = "DateUtils"; - private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT"); - - public static Date parse(final String input) { - if (input == null) { - throw new IllegalArgumentException("Date must not be null"); - } - String date = input.trim().replace('/', '-').replaceAll("( ){2,}+", " "); - - // remove colon from timezone to avoid differences between Android and Java SimpleDateFormat - date = date.replaceAll("([+-]\\d\\d):(\\d\\d)$", "$1$2"); - - // CEST is widely used but not in the "ISO 8601 Time zone" list. Let's hack around. - date = date.replaceAll("CEST$", "+0200"); - date = date.replaceAll("CET$", "+0100"); - - // some generators use "Sept" for September - date = date.replaceAll("\\bSept\\b", "Sep"); - - // if datetime is more precise than seconds, make sure the value is in ms - if (date.contains(".")) { - int start = date.indexOf('.'); - int current = start + 1; - while (current < date.length() && Character.isDigit(date.charAt(current))) { - current++; - } - // even more precise than microseconds: discard further decimal places - if (current - start > 4) { - if (current < date.length() - 1) { - date = date.substring(0, start + 4) + date.substring(current); - } else { - date = date.substring(0, start + 4); - } - // less than 4 decimal places: pad to have a consistent format for the parser - } else if (current - start < 4) { - if (current < date.length() - 1) { - date = date.substring(0, current) + StringUtils.repeat("0", 4 - (current - start)) + date.substring(current); - } else { - date = date.substring(0, current) + StringUtils.repeat("0", 4 - (current - start)); - } - } - } - final String[] patterns = { - "dd MMM yy HH:mm:ss Z", - "dd MMM yy HH:mm Z", - "EEE, dd MMM yyyy HH:mm:ss Z", - "EEE, dd MMM yyyy HH:mm:ss", - "EEE, dd MMMM yyyy HH:mm:ss Z", - "EEE, dd MMMM yyyy HH:mm:ss", - "EEEE, dd MMM yyyy HH:mm:ss Z", - "EEEE, dd MMM yy HH:mm:ss Z", - "EEEE, dd MMM yyyy HH:mm:ss", - "EEEE, dd MMM yy HH:mm:ss", - "EEE MMM d HH:mm:ss yyyy", - "EEE, dd MMM yyyy HH:mm Z", - "EEE, dd MMM yyyy HH:mm", - "EEE, dd MMMM yyyy HH:mm Z", - "EEE, dd MMMM yyyy HH:mm", - "EEEE, dd MMM yyyy HH:mm Z", - "EEEE, dd MMM yy HH:mm Z", - "EEEE, dd MMM yyyy HH:mm", - "EEEE, dd MMM yy HH:mm", - "EEE MMM d HH:mm yyyy", - "yyyy-MM-dd'T'HH:mm:ss", - "yyyy-MM-dd'T'HH:mm:ss.SSS Z", - "yyyy-MM-dd'T'HH:mm:ss.SSS", - "yyyy-MM-dd'T'HH:mm:ssZ", - "yyyy-MM-dd'T'HH:mm:ss'Z'", - "yyyy-MM-dd'T'HH:mm:ss.SSSZ", - "yyyy-MM-ddZ", - "yyyy-MM-dd", - "EEE d MMM yyyy HH:mm:ss 'GMT'Z (z)" - }; - - SimpleDateFormat parser = new SimpleDateFormat("", Locale.US); - parser.setLenient(false); - parser.setTimeZone(defaultTimezone); - - ParsePosition pos = new ParsePosition(0); - for (String pattern : patterns) { - parser.applyPattern(pattern); - pos.setIndex(0); - try { - Date result = parser.parse(date, pos); - if (result != null && pos.getIndex() == date.length()) { - return result; - } - } catch (Exception e) { - Log.e(TAG, Log.getStackTraceString(e)); - } - } - - // if date string starts with a weekday, try parsing date string without it - if (date.matches("^\\w+, .*$")) { - return parse(date.substring(date.indexOf(',') + 1)); - } - - Log.d(TAG, "Could not parse date string \"" + input + "\" [" + date + "]"); - return null; - } - - /** - * Parses the date but if the date is in the future, returns null. - */ - @Nullable - public static Date parseOrNullIfFuture(final String input) { - Date date = parse(input); - if (date == null) { - return null; - } - Date now = new Date(); - if (date.after(now)) { - return null; - } - return date; - } - - /** - * Takes a string of the form [HH:]MM:SS[.mmm] and converts it to - * milliseconds. - * - * @throws java.lang.NumberFormatException if the number segments contain invalid numbers. - */ - public static long parseTimeString(final String time) { - String[] parts = time.split(":"); - long result = 0; - int idx = 0; - if (parts.length == 3) { - // string has hours - result += Integer.parseInt(parts[idx]) * 3600000L; - idx++; - } - if (parts.length >= 2) { - result += Integer.parseInt(parts[idx]) * 60000L; - idx++; - result += (long) (Float.parseFloat(parts[idx]) * 1000L); - } - return result; - } - - public static String formatRFC822Date(Date date) { - SimpleDateFormat format = new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US); - return format.format(date); - } - - public static String formatRFC3339Local(Date date) { - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US); - return format.format(date); - } - - public static String formatRFC3339UTC(Date date) { - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); - format.setTimeZone(defaultTimezone); - return format.format(date); - } - - public static String formatAbbrev(final Context context, final Date date) { - if (date == null) { - return ""; - } - GregorianCalendar now = new GregorianCalendar(); - GregorianCalendar cal = new GregorianCalendar(); - cal.setTime(date); - boolean withinLastYear = now.get(Calendar.YEAR) == cal.get(Calendar.YEAR); - int format = android.text.format.DateUtils.FORMAT_ABBREV_ALL; - if (withinLastYear) { - format |= android.text.format.DateUtils.FORMAT_NO_YEAR; - } - return android.text.format.DateUtils.formatDateTime(context, date.getTime(), format); - } - - public static String formatForAccessibility(final Context context, final Date date) { - if (date == null) { - return ""; - } - return DateFormat.getDateInstance(DateFormat.LONG).format(date); - } -} -- cgit v1.2.3