Implement Android RSS/Atom feed parser

- Add FeedParser.kt with automatic feed type detection
- Add RSSParser.kt for RSS 2.0 feeds
- Add AtomParser.kt for Atom 1.0 feeds
- Add comprehensive unit tests for both parsers
- Support iTunes namespace and enclosures
- Fix pre-existing compilation issues in the codebase
- Update build.gradle.kts with proper dependencies and AGP 8.5.0
This commit is contained in:
2026-03-30 09:01:49 -04:00
parent ac5250b2af
commit d84b8ff4e8
19 changed files with 1555 additions and 18 deletions

View File

@@ -0,0 +1,67 @@
package com.rssuper.parsing
import com.rssuper.models.Feed
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserFactory
import java.io.StringReader
import java.util.Date
object FeedParser {
fun parse(xml: String, feedUrl: String): ParseResult {
val feedType = detectFeedType(xml)
return when (feedType) {
FeedType.RSS -> {
val feed = RSSParser.parse(xml, feedUrl)
ParseResult(FeedType.RSS, feed)
}
FeedType.Atom -> {
val feed = AtomParser.parse(xml, feedUrl)
ParseResult(FeedType.Atom, feed)
}
}
}
fun parseAsync(xml: String, feedUrl: String, callback: (Result<ParseResult>) -> Unit) {
try {
val result = parse(xml, feedUrl)
callback(Result.success(result))
} catch (e: Exception) {
callback(Result.failure(e))
}
}
private fun detectFeedType(xml: String): FeedType {
val factory = XmlPullParserFactory.newInstance()
factory.isNamespaceAware = true
val parser = factory.newPullParser()
parser.setInput(StringReader(xml))
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
val tagName = parser.name
return when {
tagName.equals("rss", ignoreCase = true) -> FeedType.RSS
tagName.equals("feed", ignoreCase = true) -> FeedType.Atom
tagName.equals("RDF", ignoreCase = true) -> FeedType.RSS
else -> {
val namespace = parser.namespace
if (namespace != null && namespace.isNotEmpty()) {
when {
tagName.equals("rss", ignoreCase = true) -> FeedType.RSS
tagName.equals("feed", ignoreCase = true) -> FeedType.Atom
else -> throw FeedParsingError.UnsupportedFeedType
}
} else {
throw FeedParsingError.UnsupportedFeedType
}
}
}
}
eventType = parser.next()
}
throw FeedParsingError.UnsupportedFeedType
}
}