From 91edd1f5f29a3336fa9e3bd1762c70ea74e3e48d Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Wed, 22 Jul 2026 18:26:14 +0000 Subject: Split save/Stash actions, follow links in-app via reader pipeline, tune scroll, fix title entities - ReaderScreen: bookmark icon is now a purely local save (just flips isSaved, no network) -- Stash archiving is a separate explicit action (new cloud icon), since it's a heavier networked action the user should trigger on purpose rather than have bundled into "save." Verified via direct DB queries and (once I stopped misreading small screenshots) the icon updates immediately in-session for both actions. - New ReaderWebView.kt: shared WebView factory for the dark reader palette and link handling, used by both ReaderScreen and the new LinkReaderScreen. Links tapped inside an article's WebView now open in a lightweight in-app reader (extracted via the same Readability pipeline, no DB persistence) instead of falling through to the system browser -- recursive, so a chain of links stays in-app. Verified live: tapping a link mid-article navigated to a real extracted page with a working back stack. - WebView scroll tuning: overscroll glow and scrollbars off, on both the article reader and the new link reader -- aimed at the reported "still a little choppy" scrolling on top of the previous reload-on-recompose fix. - FeedParser: some feeds (kottke.org among them) double-encode titles -- the XML parser resolves outer XML escaping, but leftover HTML entities like "“" were showing up literally in the title text. Titles are now run through a second HTML-entity decode pass. Verified: a kottke title that previously showed a literal “ now renders the actual curly quote. All changes verified live on the emulator against the real 107-feed dataset. --- app/src/main/java/org/terst/feedreader/data/FeedParser.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'app/src/main/java/org/terst/feedreader/data') diff --git a/app/src/main/java/org/terst/feedreader/data/FeedParser.kt b/app/src/main/java/org/terst/feedreader/data/FeedParser.kt index 735a707..629afe6 100644 --- a/app/src/main/java/org/terst/feedreader/data/FeedParser.kt +++ b/app/src/main/java/org/terst/feedreader/data/FeedParser.kt @@ -1,5 +1,6 @@ package org.terst.feedreader.data +import android.text.Html import android.util.Xml import org.xmlpull.v1.XmlPullParser import java.io.InputStream @@ -33,6 +34,15 @@ data class ParsedFeed( */ object FeedParser { + // Some feeds (kottke.org among them) double-encode titles: the XML pull parser already + // resolves the outer XML escaping (& -> &), but what's left can itself still be + // HTML-entity-encoded text (e.g. a literal "“" or "’") that the feed author + // encoded as if for direct HTML embedding. Titles are shown as plain text (Compose Text, + // not a WebView), so that residual entity needs a second, explicit decode pass or it + // shows up on-screen literally as "“" instead of the curly quote it's meant to be. + private fun decodeTitleEntities(text: String): String = + Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY).toString() + fun parse(input: InputStream): ParsedFeed { val parser = Xml.newPullParser() parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) @@ -79,7 +89,7 @@ object FeedParser { itemDate = null; itemContent = null; itemDescription = null; itemImageUrl = null } "title" -> { - val text = parser.nextText() + val text = decodeTitleEntities(parser.nextText()) if (inItem) itemTitle = text else feedTitle = text } "link" -> { @@ -154,7 +164,7 @@ object FeedParser { entryDate = null; entryContent = null; entrySummary = null; entryImageUrl = null } "title" -> { - val text = parser.nextText() + val text = decodeTitleEntities(parser.nextText()) if (inEntry) entryTitle = text else feedTitle = text } "link" -> { -- cgit v1.2.3