diff --git a/README.md b/README.md index 37b482e6..6a2e0cd3 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Articles can have following attributes: The library is uploaded in jCenter, so you can easily add the dependency: ```Gradle dependencies { - compile 'com.prof.rssparser:rssparser:2.1.0' + compile 'com.prof.rssparser:rssparser:2.1.1' } ``` #### Use: diff --git a/build.gradle b/build.gradle index d89b1b89..3cbfa1f7 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,8 @@ apply plugin: "com.github.ben-manes.versions" buildscript { ext.versions = [ - 'libVersionCode' : 21000, - 'libVersionName' : '2.1.0', + 'libVersionCode' : 21001, + 'libVersionName' : '2.1.1', 'compileSdk' : 29, 'minSdk' : 14, 'targetSdk' : 29, diff --git a/rssparser/build.gradle b/rssparser/build.gradle index 5f24e483..edde83f2 100644 --- a/rssparser/build.gradle +++ b/rssparser/build.gradle @@ -9,8 +9,8 @@ android { defaultConfig { minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk - versionCode 21000 - versionName "2.1.0" + versionCode 21001 + versionName "2.1.1" } buildTypes { diff --git a/rssparser/src/main/java/com/prof/rssparser/Image.kt b/rssparser/src/main/java/com/prof/rssparser/Image.kt index 894bf780..108978a4 100644 --- a/rssparser/src/main/java/com/prof/rssparser/Image.kt +++ b/rssparser/src/main/java/com/prof/rssparser/Image.kt @@ -3,5 +3,6 @@ package com.prof.rssparser data class Image( var title: String? = null, var url: String? = null, - var link: String? = null + var link: String? = null, + var description: String? = null ) \ No newline at end of file diff --git a/rssparser/src/main/java/com/prof/rssparser/core/CoreXMLParser.kt b/rssparser/src/main/java/com/prof/rssparser/core/CoreXMLParser.kt index 5458e6bd..7ef6375a 100644 --- a/rssparser/src/main/java/com/prof/rssparser/core/CoreXMLParser.kt +++ b/rssparser/src/main/java/com/prof/rssparser/core/CoreXMLParser.kt @@ -17,7 +17,6 @@ package com.prof.rssparser.core -import android.util.Log import com.prof.rssparser.Article import com.prof.rssparser.Channel import com.prof.rssparser.Image @@ -62,32 +61,42 @@ object CoreXMLParser { if (eventType == XmlPullParser.START_TAG) { if (xmlPullParser.name.equals(RSSKeywords.RSS_CHANNEL, ignoreCase = true)) { insideChannel = true - insideItem = false - insideChannelImage = false } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM, ignoreCase = true)) { insideItem = true - insideChannel = false - insideChannelImage = false } else if (xmlPullParser.name.equals(RSSKeywords.RSS_CHANNEL_IMAGE, ignoreCase = true)) { - insideItem = false - insideChannel = false insideChannelImage = true channelImage = Image() } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_TITLE, ignoreCase = true)) { - when { - insideChannel -> channelTitle = xmlPullParser.nextText().trim() - insideChannelImage -> channelImage?.title = xmlPullParser.nextText().trim() - insideItem -> currentArticle.title = xmlPullParser.nextText().trim() + if (insideChannel) { + when { + insideChannelImage -> { + channelImage?.title = xmlPullParser.nextText().trim() + } + insideItem -> { + currentArticle.title = xmlPullParser.nextText().trim() + } + else -> { + channelTitle = xmlPullParser.nextText().trim() + } + } } } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_LINK, ignoreCase = true)) { - when { - insideChannel -> channelLink = xmlPullParser.nextText().trim() - insideChannelImage -> channelImage?.link = xmlPullParser.nextText().trim() - insideItem -> currentArticle.link = xmlPullParser.nextText().trim() + if (insideChannel) { + when { + insideChannelImage -> { + channelImage?.link = xmlPullParser.nextText().trim() + } + insideItem -> { + currentArticle.link = xmlPullParser.nextText().trim() + } + else -> { + channelLink = xmlPullParser.nextText().trim() + } + } } } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_AUTHOR, ignoreCase = true)) { @@ -108,88 +117,103 @@ object CoreXMLParser { } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_URL, ignoreCase = true)) { if (insideChannelImage) { channelImage?.url = xmlPullParser.nextText().trim() - Log.d("PARSER", "") } } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_ENCLOSURE, ignoreCase = true)) { if (insideItem) { val type = xmlPullParser.getAttributeValue(null, RSSKeywords.RSS_ITEM_TYPE) - if (type != null && type.contains("image/")) { + if (type != null && type.contains("image")) { currentArticle.image = xmlPullParser.getAttributeValue(null, RSSKeywords.RSS_ITEM_URL) + } else { + // let's try if there is the url + val url = xmlPullParser.getAttributeValue(null, RSSKeywords.RSS_ITEM_URL) + if (url != null) { + currentArticle.image = url + } } } } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_DESCRIPTION, ignoreCase = true)) { if (insideChannel) { - channelDescription = xmlPullParser.nextText().trim() - } else if (insideItem) { - val description = xmlPullParser.nextText() - currentArticle.description = description.trim() - if (currentArticle.image == null) { - currentArticle.image = getImageUrl(description) - } - } - - } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_CONTENT, ignoreCase = true)) { - if (insideItem) { - val content = xmlPullParser.nextText().trim() - currentArticle.content = content - if (currentArticle.image == null) { - currentArticle.image = getImageUrl(content) + if (insideItem) { + val description = xmlPullParser.nextText() + currentArticle.description = description.trim() + if (currentArticle.image == null) { + currentArticle.image = getImageUrl(description) + } + } else if (insideChannelImage) { + channelImage?.description = xmlPullParser.nextText().trim() + } else { + channelDescription = xmlPullParser.nextText().trim() } } - } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_PUB_DATE, ignoreCase = true)) { - if (insideItem) { - val nextTokenType = xmlPullParser.next() - if (nextTokenType == XmlPullParser.TEXT) { - currentArticle.pubDate = xmlPullParser.text.trim() - } - // Skip to be able to find date inside 'tag' tag - continue + } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_CONTENT, ignoreCase = true)) { + if (insideItem) { + val content = xmlPullParser.nextText().trim() + currentArticle.content = content + if (currentArticle.image == null) { + currentArticle.image = getImageUrl(content) } + } - } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_TIME, ignoreCase = true)) { - if (insideItem) { - currentArticle.pubDate = xmlPullParser.nextText() + } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_PUB_DATE, ignoreCase = true)) { + if (insideItem) { + val nextTokenType = xmlPullParser.next() + if (nextTokenType == XmlPullParser.TEXT) { + currentArticle.pubDate = xmlPullParser.text.trim() } + // Skip to be able to find date inside 'tag' tag + continue + } - } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_GUID, ignoreCase = true)) { - if (insideItem) { - currentArticle.guid = xmlPullParser.nextText().trim() - } + } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_TIME, ignoreCase = true)) { + if (insideItem) { + currentArticle.pubDate = xmlPullParser.nextText() } - } else if (eventType == XmlPullParser.END_TAG && xmlPullParser.name.equals("item", ignoreCase = true)) { - // The item is correctly parsed - insideItem = false - articleList.add(currentArticle) - currentArticle = Article() + } else if (xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_GUID, ignoreCase = true)) { + if (insideItem) { + currentArticle.guid = xmlPullParser.nextText().trim() + } } - eventType = xmlPullParser.next() + + } else if (eventType == XmlPullParser.END_TAG && xmlPullParser.name.equals(RSSKeywords.RSS_ITEM, ignoreCase = true)) { + // The item is correctly parsed + insideItem = false + articleList.add(currentArticle) + currentArticle = Article() + } else if (eventType == XmlPullParser.END_TAG && xmlPullParser.name.equals(RSSKeywords.RSS_CHANNEL, ignoreCase = true)) { + // The channel is correctly parsed + insideChannel = false + } else if (eventType == XmlPullParser.END_TAG && xmlPullParser.name.equals(RSSKeywords.RSS_CHANNEL_IMAGE, ignoreCase = true)) { + // The channel image is correctly parsed + insideChannelImage = false } - return Channel(channelTitle, channelLink, channelDescription, channelImage, articleList) + eventType = xmlPullParser.next() } + return Channel(channelTitle, channelLink, channelDescription, channelImage, articleList) +} - /** - * Finds the first img tag and get the src as featured image - * - * @param input The content in which to search for the tag - * @return The url, if there is one - */ - private fun getImageUrl(input: String): String? { - - var url: String? = null - val patternImg = Pattern.compile("()") - val matcherImg = patternImg.matcher(input) - if (matcherImg.find()) { - val imgTag = matcherImg.group(1) - val patternLink = Pattern.compile("src\\s*=\\s*\"(.+?)\"") - val matcherLink = patternLink.matcher(imgTag) - if (matcherLink.find()) { - url = matcherLink.group(1).trim() - } +/** + * Finds the first img tag and get the src as featured image + * + * @param input The content in which to search for the tag + * @return The url, if there is one + */ +private fun getImageUrl(input: String): String? { + + var url: String? = null + val patternImg = Pattern.compile("()") + val matcherImg = patternImg.matcher(input) + if (matcherImg.find()) { + val imgTag = matcherImg.group(1) + val patternLink = Pattern.compile("src\\s*=\\s*\"(.+?)\"") + val matcherLink = patternLink.matcher(imgTag) + if (matcherLink.find()) { + url = matcherLink.group(1).trim() } - return url } + return url +} } diff --git a/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImage2FeedTest.kt b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImage2FeedTest.kt index 84bd75e2..d84e7090 100644 --- a/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImage2FeedTest.kt +++ b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImage2FeedTest.kt @@ -76,6 +76,11 @@ class CoreXMLParserImage2FeedTest { assertEquals(channel.image?.url, "https://www.mundodeportivo.com/rsc/images/logo_MD_feed.png") } + @Test + fun channelImageDescription_isCorrect() { + assertEquals(channel.image?.description, "Mundo Deportivo es tu diario deportivo On Line. Noticias de deporte, fútbol y del Barça") + } + @Test @Throws fun size_isCorrect() { diff --git a/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImageChannelReverseTest.kt b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImageChannelReverseTest.kt new file mode 100644 index 00000000..fefb867a --- /dev/null +++ b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImageChannelReverseTest.kt @@ -0,0 +1,126 @@ +package com.prof18.rssparser + +import android.os.Build +import com.prof.rssparser.Article +import com.prof.rssparser.Channel +import com.prof.rssparser.core.CoreXMLParser +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.P]) +class CoreXMLParserImageChannelReverseTest { + private lateinit var articleList: MutableList
+ private lateinit var article: Article + private val feedPath = "/feed-test-image-channel-reverse.xml" + private lateinit var channel: Channel + + @Before + fun setUp() { + val inputStream = javaClass.getResourceAsStream(feedPath)!! + val feed = inputStream.bufferedReader().use { it.readText() } + channel = CoreXMLParser.parseXML(feed) + articleList = channel.articles + article = articleList[0] + } + + @Test + fun channelTitle_isCorrect() { + Assert.assertEquals(channel.title, "The Joe Rogan Experience") + } + + @Test + fun channelDesc_isCorrect() { + Assert.assertEquals(channel.description, "The podcast of Comedian Joe Rogan..") + } + + @Test + fun channelLink_isCorrect() { + Assert.assertEquals(channel.link, "https://www.joerogan.com") + } + + @Test + fun channelImageTitle_isCorrect() { + Assert.assertEquals(channel.image?.title, "The Joe Rogan Experience") + } + + @Test + fun channelImageLink_isCorrect() { + Assert.assertEquals(channel.image?.link, "https://www.joerogan.com") + } + + @Test + fun channelImageUrl_isCorrect() { + Assert.assertEquals(channel.image?.url, "http://static.libsyn.com/p/assets/7/1/f/3/71f3014e14ef2722/JREiTunesImage2.jpg") + } + + @Test + fun channelImageDescription_isCorrect() { + Assert.assertNull(channel.image?.description) + } + + @Test + @Throws + fun size_isCorrect() { + Assert.assertEquals(articleList.size, 6) + } + + + @Test + @Throws + fun title_isCorrect() { + Assert.assertEquals(article.title, "#1405 - Sober October 3 Recap") + } + + @Test + @Throws + fun author_isCorrect() { + Assert.assertEquals(article.author, null) + } + + @Test + @Throws + fun link_isCorrect() { + Assert.assertEquals(article.link, "http://traffic.libsyn.com/joeroganexp/p1405.mp3") + } + + @Test + @Throws + fun pubDate_isCorrect() { + Assert.assertEquals(article.pubDate, "Tue, 24 Dec 2019 20:00:00 +0000") + } + + @Test + @Throws + fun description_isPresent() { + Assert.assertEquals(article.description, "Joe is joined by Ari Shaffir, Bert Kreischer & Tom Segura to recap their 3rd annual Sober October challenge.") + } + + @Test + @Throws + fun content_isCorrect() { + Assert.assertEquals(article.content, "Joe is joined by Ari Shaffir, Bert Kreischer & Tom Segura to recap their 3rd annual Sober October challenge.") + } + + @Test + @Throws + fun image_isCorrect() { + Assert.assertEquals(article.image, "http://traffic.libsyn.com/joeroganexp/p1405.mp3?dest-id=19997") + } + + @Test + @Throws + fun categories_isCorrect() { + assert(article.categories.isEmpty()) + } + + @Test + @Throws + fun guid_isCorrect() { + Assert.assertEquals(article.guid, "0d7147a3-f1c1-4ae6-bbf8-2e0a493639ca") + } +} \ No newline at end of file diff --git a/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImageFeedTest.kt b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImageFeedTest.kt index 3bdfe356..9d4a6dcf 100644 --- a/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImageFeedTest.kt +++ b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserImageFeedTest.kt @@ -21,6 +21,7 @@ import android.os.Build import com.prof.rssparser.Article import com.prof.rssparser.Channel import com.prof.rssparser.core.CoreXMLParser +import org.junit.Assert import org.junit.Assert.* import org.junit.Before import org.junit.Test @@ -75,6 +76,11 @@ class CoreXMLParserImageFeedTest { assertEquals(channel.image?.url, "https://cdn.movieweb.com/assets/1/sites/movieweb.com/chrome-touch-icon-192x192.png") } + @Test + fun channelImageDescription_isCorrect() { + assertNull(channel.image?.description) + } + @Test @Throws fun size_isCorrect() { diff --git a/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserXSLFeedTest.kt b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserXSLFeedTest.kt index 413111aa..06a1b393 100644 --- a/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserXSLFeedTest.kt +++ b/rssparser/src/test/java/com/prof18/rssparser/CoreXMLParserXSLFeedTest.kt @@ -21,6 +21,7 @@ import android.os.Build import com.prof.rssparser.Article import com.prof.rssparser.Channel import com.prof.rssparser.core.CoreXMLParser +import org.junit.Assert import org.junit.Assert.* import org.junit.Before import org.junit.Test @@ -75,6 +76,11 @@ class CoreXMLParserXSLFeedTest { assertEquals(channel.image?.url, "https://www.skysports.com/images/site/ss-logo-07.gif") } + @Test + fun channelImageDescription_isCorrect() { + assertNull(channel.image?.description) + } + @Test @Throws fun size_isCorrect() { diff --git a/rssparser/src/test/resources/feed-test-image-channel-reverse.xml b/rssparser/src/test/resources/feed-test-image-channel-reverse.xml new file mode 100644 index 00000000..db6f2183 --- /dev/null +++ b/rssparser/src/test/resources/feed-test-image-channel-reverse.xml @@ -0,0 +1,135 @@ + + + + + The Joe Rogan Experience + Tue, 24 Dec 2019 20:00:00 +0000 + Sat, 04 Jan 2020 01:06:48 +0000 + Libsyn WebEngine 2.0 + https://www.joerogan.com + en + + https://www.joerogan.com + joe@joerogan.net (joe@joerogan.net) + + + http://static.libsyn.com/p/assets/7/1/f/3/71f3014e14ef2722/JREiTunesImage2.jpg + The Joe Rogan Experience + + + Joe Rogan + comedian,joe,monkey,redban,rogan,talking,ufc + + + + + + + yes + + + joe@joerogan.net + + + + episodic + + http://joeroganexp.joerogan.libsynpro.com/rss + + #1405 - Sober October 3 Recap + Tue, 24 Dec 2019 20:00:00 +0000 + + + + + + + 03:30:48 + no + podcast,3,joe,party,experience,tom,ari,october,bert,freak,rogan,recap,sober,kreischer,shaffir,segura,deathsquad,jre,1405 + + 1405 + full + + + #1404 - Bryan Callen + Sat, 21 Dec 2019 05:00:00 +0000 + + + + + + + 03:08:16 + no + podcast,joe,party,experience,bryan,freak,rogan,callen,deathsquad,jre,1404 + + 1404 + full + + + #1403 - Forrest Galante + Thu, 19 Dec 2019 20:00:00 +0000 + + + + + + + 02:56:30 + no + podcast,joe,forrest,party,experience,freak,rogan,galante,deathsquad,jre,1403 + + 1403 + full + + + JRE MMA Show #85 with Max Holloway + Wed, 18 Dec 2019 20:00:00 +0000 + + + + + + + 02:13:41 + no + podcast,mma,show,joe,party,experience,max,holloway,freak,rogan,85,deathsquad + + 1402 + full + + + #1402 - Boyan Slat + Tue, 17 Dec 2019 20:00:00 +0000 + + + + + + + 01:54:00 + no + podcast,joe,party,experience,freak,rogan,1402,deathsquad,slat,jre,boyan + + 1402 + full + + + #1401 - Iliza Shlesinger + Tue, 17 Dec 2019 04:00:00 +0000 + + + + + + + 02:35:30 + no + podcast,joe,party,experience,freak,rogan,iliza,deathsquad,jre,shlesinger,1401 + + 1401 + full + + + \ No newline at end of file