<feed xmlns='http://www.w3.org/2005/Atom'>
<title>feedreader.git/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt, branch master</title>
<subtitle>FeeDreader — personal Android RSS reader
</subtitle>
<id>https://git.terst.org/feedreader.git/atom?h=master</id>
<link rel='self' href='https://git.terst.org/feedreader.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/'/>
<updated>2026-08-22T22:35:58+00:00</updated>
<entry>
<title>Mute emptywheel's recurring "Fridays with Nicole Sandler" series</title>
<updated>2026-08-22T22:35:58+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-22T22:35:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=ef1cec60c91c2481ebad52d2d39da536606a1631'/>
<id>urn:sha1:ef1cec60c91c2481ebad52d2d39da536606a1631</id>
<content type='text'>
Reported via the in-app bug report against a specific numbered installment
(-111). Seeds a MUTE filter rule scoped to the emptywheel feed rather than
requiring it be added by hand in Settings, matching this file's existing
pattern of shipping known personal-data fixes as migrations.
</content>
</entry>
<entry>
<title>Keep a just-read article visible (and Previous-reachable) until the next fetch</title>
<updated>2026-08-22T01:13:30+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-22T01:13:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=55cdfc8fab6ddd1052593f8fd53dbb5bf730cc63'/>
<id>urn:sha1:55cdfc8fab6ddd1052593f8fd53dbb5bf730cc63</id>
<content type='text'>
The Unread/Older views are live Room Flow queries filtered on
isRead = 0, so marking an article read (which only happens on
Next/Previous, not on open) makes it vanish from the list the instant
Room re-emits -- mid-scroll, or from underneath the reader's own
Previous button, which re-queries this same flow to find its
neighbors. Concretely: Next from C to D marks C read and prunes it
from the live list; Previous from D then can't find C anymore, and
jumps past it to whatever was unread before C. A misclick or the
in-flight prune could cost you the article you meant to keep reading.

Adds ArticleEntity.readAt (set whenever isRead flips true, cleared on
explicit "mark unread"); observeUnread/observeOlderUnread now match
"isRead = 0 OR readAt &gt;= sinceFetch", where sinceFetch is
Settings.lastFetchAt -- the same durable signal the new-articles
snackbar already uses. A read article now lingers (shown dimmed via
the existing showReadState styling) until the next real fetch
actually completes, not the next DB write, and Previous/Next read from
the same still-inclusive query so stepping back to what you just read
finds it right where it was.
</content>
</entry>
<entry>
<title>v9: batch of feed URL fixes from July 31 triage; dedupe rename-and-reassign</title>
<updated>2026-08-22T01:03:37+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-22T01:03:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=2352cb39d9623074192a6a1a7fec47e22ab4606b'/>
<id>urn:sha1:2352cb39d9623074192a6a1a7fec47e22ab4606b</id>
<content type='text'>
Live triage pass over every subscription showing "Last fetch failed."
Host-substring LIKE matching, not exact-string like v6-v8, since the
exact previously-stored URL for most of these wasn't known ahead of
time. Also extracts the repeated "rename a feed's id, reassign its
articles, don't let a failure abort the migration transaction" dance
(duplicated inline in v6/v7) into one renameFeedSafely helper used by
both those and the new v9 migration.
</content>
</entry>
<entry>
<title>Migrate blog.securitymouse.com to its Blogger canonical https URL</title>
<updated>2026-07-25T02:41:45+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-25T02:41:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=e69da36b9ba2d79c383802d387084cf555169566'/>
<id>urn:sha1:e69da36b9ba2d79c383802d387084cf555169566</id>
<content type='text'>
Its own custom-domain TLS is broken, but it's a Blogger blog under the hood
(confirmed via its feed's Blogger-format Atom XML and "Server: GSE" on the
homepage) -- Blogger blogs are always reachable at a stable, always-https URL
keyed by blog ID regardless of the custom domain's own DNS/TLS setup.

Checked whether the other two remaining http feeds from the last migration
have a similar way out: they don't. webpolicy.org's feed is dead (last post
2015). bit-player.org was rebuilt on a static generator that no longer
exposes a feed at any checked path (/feed, /feed/, /feed.xml, /rss,
/atom.xml, or a &lt;link rel="alternate"&gt; on its homepage).

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Migrate http:// feeds to https:// where it actually works; add feeds as https-only going forward</title>
<updated>2026-07-25T01:30:21+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-25T01:30:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=72214b9d3617a66a70727819dbf7fe59ea037264'/>
<id>urn:sha1:72214b9d3617a66a70727819dbf7fe59ea037264</id>
<content type='text'>
Android blocks cleartext by default (targetSdk 28+), so every http:// feed was
failing every single fetch ("CLEARTEXT communication ... not permitted"). Checked
each of this user's 9 http feeds live via curl:

- 6 work fine over https (5 are a plain scheme swap; divingforpearlsblog.com's own
  https endpoint redirects through an insecure feedburner hop, so that one points
  straight at feedburner's https endpoint instead) -- migrated via a new Room
  migration (v6-&gt;v7), rewriting both the feed row and its articles' feedId so
  read history isn't orphaned. Confirmed live: all 6 now fetch clean with no
  lastError.
- 3 have no usable https at all (bit-player.org: connection refused, no TLS
  listener; blog.securitymouse.com: broken TLS handshake; webpolicy.org: a
  certificate that doesn't match the hostname) and are deliberately left alone.

Going forward, adding a feed (manually or via OPML import) upgrades a given
http:// URL to https:// before using it -- most sites serve identical content
either way, and a site with no real https support just fails its very next
fetch attempt the normal way instead of silently subscribing to a feed that
can never actually update.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Rebrand to FeeDreader; overflow-menu cleanup; deferred read-marking; folder-organized feed list</title>
<updated>2026-07-23T07:38:28+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-23T07:38:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=ecdb45778e274126737aa87e27ed1b1c00cda01c'/>
<id>urn:sha1:ecdb45778e274126737aa87e27ed1b1c00cda01c</id>
<content type='text'>
- Rename app to FeeDreader with an owl launcher icon and a paywall-avoidance
  note in Settings
- Move Open original/Save to Stash into the reader's overflow menu; add
  Mark unread there too
- Only mark an article read when navigating away via Previous/Next, not on
  open or on back
- Saved list no longer crosses out read items; its count includes them
- Back from Unread (the root screen) now opens the feed list instead of
  exiting the app
- Feed list: clearer separator above the per-feed list, folder grouping
  (from OPML categories) with case-insensitive sort, and a virtual "Last
  fetch failed" section at the top
- Reader extraction: cap unsized decorative SVG icons (fixes Ars
  Technica's giant section-badge icon), force native loading="lazy"
  images to load eagerly in the hidden extraction WebView, and drop a
  lightbox's duplicate caption only when a real figcaption sibling exists
- Backlog notes on swipe-to-next research, extraction test coverage, and
  an async-rendered-gallery race confirmed live on Ars Technica

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Fix crash on blank-guid articles; simplify browser back; backlog notes</title>
<updated>2026-07-23T01:59:14+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-23T01:59:14+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=3914ba17608884a6fa8614b9058752e0c1c0d94f'/>
<id>urn:sha1:3914ba17608884a6fa8614b9058752e0c1c0d94f</id>
<content type='text'>
- FeedParser: a feed with a literal empty &lt;guid&gt;/&lt;id&gt; element (confirmed
  live: a Mother Jones entry) produced an ArticleEntity with id = "" --
  the fallback chain only checked for null, not blank, so an empty-but-
  present tag passed straight through. An empty-string primary key broke
  Navigation Compose's route matching and crashed on tap ("tapping this
  article crashes the app"). Fixed both RSS and Atom paths to treat blank
  the same as null before falling back to a link-based id. Schema v5
  migration deletes any row already on-device with the bad id; it'll
  come back with a real, hash-based id on the next fetch of that feed.
  Verified live: zero blank-id rows after the migration and a fresh
  fetch, no crash.

- BrowserScreen: back (both the system gesture and the in-app arrow) now
  always closes the screen directly, rather than stepping through
  in-page WebView history first. Reported live as not making sense for
  multi-page browsing (search results -&gt; article -&gt; another link, etc.)
  -- you'd have to tap back that many times just to leave. There's no
  separate in-page-back affordance now, matching how every other screen
  in this app behaves.

- Tried moving swipe-to-next-article detection off Compose's gesture
  system entirely (a passive native OnTouchListener on the WebView, never
  consuming) on the theory that Compose's gesture arbitration was the
  source of reported scroll jerkiness. Reverted after confirming live
  that swipe stopped working at all with that approach -- a worse
  regression than unproven jerkiness. Back to the known-working
  Compose-based detector; the jerkiness complaint itself is still open.

- BACKLOG.md: added bug-report-button, smaller-title-text, and
  bottom-action-strip-with-auto-hide notes.
</content>
</entry>
<entry>
<title>Remove reader-mode heuristics entirely; explicit per-feed opt-in only</title>
<updated>2026-07-22T21:07:05+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T21:07:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=d7c05609692153670dc136082da4e0566d7838a6'/>
<id>urn:sha1:d7c05609692153670dc136082da4e0566d7838a6</id>
<content type='text'>
Reader mode was still switching kottke articles even after the v3
opt-out and the "un-hide only if thin" fallback -- both were still
heuristic (content-length-based), and kottke's posts are routinely short
by design, not paywalled, so the heuristics kept firing on ordinary
content. Removed the auto-extract-on-thin-content logic entirely: reader
mode now NEVER turns on by itself, under any condition.

- FeedEntity.readerModeDisabled -&gt; readerModeEnabled (opt-in, default off
  for every feed -- schema v4, full table rebuild since Room's migration
  validation requires an exact column match and a bare ADD COLUMN left
  the old column orphaned; reproduced live as a crash, "Migration didn't
  properly handle: feeds").
- The reader's toggle icon now sets reader mode for the article's WHOLE
  FEED, persists it immediately, and confirms via snackbar ("Reader mode
  enabled/disabled for X") -- both in the reader screen and the Feeds
  list's own toggle. This is the only way reader mode ever turns on.
- ReaderExtractor: replaced the "un-hide everything if thin" paywall
  fallback (which is what pulled kottke's site footer into the result --
  confirmed live, twice) with one that only un-hides elements whose
  class/id actually names them as paywall/premium/subscriber content
  (West Hawaii Today's hidden div is literally class="paywall
  paywall_&lt;id&gt;"). This turned out not to be the kottke bug's actual cause
  either -- see below -- but it's a real, independent correctness
  improvement over the previous blanket version, so it stays.
- Also added: noscript-wrapped lazy-load image fallback (a common
  pattern pairs a placeholder &lt;img&gt; with a real one inside &lt;noscript&gt;,
  which our JS-enabled extraction WebView never parses into DOM per the
  HTML spec) and lazy data-src/srcset resolution generally.

Root cause of the kottke footer bug, found by fetching and inspecting
the actual page structure rather than guessing again: it's not a
CSS-hidden-element issue at all. kottke's real post content lives in a
plain, undecorated &lt;div class="post"&gt; that's a sibling (not ancestor) of
an ads container and a "Socials &amp; More" footer widget, all under one
shared &lt;div class="grid-container"&gt;. Because kottke's posts are so
short, Readability's generic paragraph-density scoring doesn't find
enough signal in .post alone and climbs to a broader container that
happens to include the neighboring footer. No un-hide logic is involved
at any point -- this reproduces identically on the plain, unmodified
Readability pass. This is a genuine limit of heuristic content
extraction on very short-form posts, not something to keep patching with
more heuristics. Reset kottke's setting back to off (its correct
default) after testing.
</content>
</entry>
<entry>
<title>Smooth scrolling, dark reader, mark-read-on-scroll, feed names, per-feed reader-mode opt-out</title>
<updated>2026-07-22T17:56:50+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T17:56:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=9da3fbcdef71e90d52045531c7c18faff02f2ff6'/>
<id>urn:sha1:9da3fbcdef71e90d52045531c7c18faff02f2ff6</id>
<content type='text'>
- ReaderScreen: AndroidView's update() was reloading the WebView's HTML on
  every recomposition (e.g. isSaving flipping mid-read), resetting scroll
  position -- this is what made scrolling feel unsmooth. Now only reloads
  when the content string actually changes.
- ReaderScreen: reader content now uses a hardcoded dark palette instead of
  browser-default white, matching the app's always-dark theme.
- ArticleListScreen: mark-as-read-on-scroll -- once an article has scrolled
  fully above the viewport it's marked read, the standard feed-reader
  behavior, via a new batched ArticleDao.markReadBatch(). Verified against
  the on-device DB (scrolled-past articles flipped to isRead=1, the
  still-visible one stayed unread).
- ArticleListItem/queries: LEFT JOIN feeds for a feedTitle column, shown per
  row in the Unread/Saved aggregated views and as a reader subtitle; the
  per-feed article list's top bar now shows the real feed title instead of
  a generic "Articles".
- FeedEntity.readerModeDisabled (schema v3, migration 2-&gt;3): per-feed
  opt-out of the Readability fallback/toggle, toggled from a new icon in
  FeedListScreen. Added for kottke.org specifically -- confirmed its thin
  linkblog posts were making Readability extract the page footer instead
  of the actual post; disabling reader mode for that feed now shows its
  own (short but correct) content instead.

All verified live on the emulator against the real 107-feed dataset.
</content>
</entry>
<entry>
<title>First round of testing feedback: images, unread landing, dark, delete</title>
<updated>2026-07-22T16:26:45+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T16:26:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=52d7877fccd60a1dae33d877ba6aa765dc098a52'/>
<id>urn:sha1:52d7877fccd60a1dae33d877ba6aa765dc098a52</id>
<content type='text'>
- Fix OPML import status message reporting the raw pre-dedup count
  (144) instead of the actual unique feed count after Room's upsert
  collapses duplicates (108) -- cosmetic bug, the DB was already
  correct.
- Article thumbnails: extract media:thumbnail/enclosure or the first
  &lt;img&gt; in feed content, show via Coil in the article list.
- App now lands on Unread instead of the feed list; a top-bar icon
  gets from there to the feed list (forward nav, not back-stack pop,
  since Unread is now the root).
- Dark color scheme by default.
- Delete-feed action (with confirmation) on each feed row -- was
  entirely missing. Deletes its non-saved articles too, so they don't
  linger as orphaned rows in the cross-feed Unread view; saved articles
  are kept regardless.
- Room migration 1-&gt;2 for the new imageUrl column (real migration, not
  destructive fallback, since there's now real on-device subscription
  data from testing).

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
</feed>
