summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt
AgeCommit message (Collapse)Author
2026-08-22Restore the 5am morning-fetch alarmPeter Stone
Reverts part of the previous commit: the explicit refresh button and fetch-on-launch stay, but the user still wants the background 5am alarm so articles are fresh before the app is opened, not just the periodic hourly WorkManager job.
2026-08-22Replace background auto-refresh with fetch-on-launch + explicit buttonPeter Stone
Periodic hourly WorkManager fetches and the 5am exact-alarm morning fetch both refreshed without any visible trigger, which surprised the user more than it helped. Refreshing now only ever happens on a fresh app launch (MainActivity.onCreate) or an explicit tap of the new refresh button on the Unread/Older screens' top bar (wired to the fetch-status state that was already being computed there but never rendered). Drops MorningFetchReceiver entirely along with its exact-alarm permission and boot receiver.
2026-08-22Add a 5am morning-fetch alarm so articles are fresh before you open the appPeter Stone
FeedFetchWorker's periodic 60-minute WorkManager job can (and does) get silently deferred for hours by Doze on an idle overnight device. An exact alarm (setExactAndAllowWhileIdle) wakes the device at a fixed 5am to run a fetch, without requesting the much broader "ignore battery optimizations" exemption -- battery optimization stays on otherwise, just this one daily wake-up. Android 12+ requires a one-time user grant for exact alarms (no in-app permission dialog); MainActivity requests it via Settings if not already granted, tracked across rotation via savedInstanceState so a config change doesn't re-trigger the Settings screen. The alarm also gets re-armed on BOOT_COMPLETED, since AlarmManager alarms don't survive a device restart on their own.
2026-08-22Fix reader rotation bug: replace-nav instead of a swipe-queue snapshotPeter Stone
The reader used to snapshot the article-id order of its list ONCE at entry and page a HorizontalPager across that fixed queue. Two real bugs traced back to one root cause: the snapshot lived in plain remember state that did NOT survive an orientation change, while the pager's own page index did -- rotating mid-session left the index pointing into a freshly re-fetched (and by then shorter, since paging past an article marks it read and drops it from the Unread query) list, landing on a different article than the one actually being read. Back was confusing for the same reason: it always returned to wherever the ORIGINAL initialArticleId pointed, not wherever the pager had since scrolled to. Fixed by not having a separate queue/index at all: each Next/Previous now replaces this screen's own back-stack entry with a fresh navigation to the neighboring article's id (MainActivity wires onNavigateToArticle with the same popUpTo-replace pattern LinkReaderScreen already uses for its own link chain). "Which article is showing" is now the nav route argument itself -- Android's own Saved State mechanism, not custom state that can drift out of sync with it -- and the back stack is always exactly [list, one reader entry] no matter how many articles were paged through.
2026-07-24Collapsible folders, long-press link menu, YouTube thumbnails, strip more ↵Peter Stone
Ars sequel chrome - Feed list: folders now collapse (start collapsed, with a per-folder unread count so collapsing doesn't hide "is there anything new") - Long-pressing a link inside an article shows an action menu (open in browser, share, save to Stash, save for later) instead of WebView's default text-selection popup -- confirmed live end to end, including the Stash/save-for-later paths creating a real standalone article - New shared ensureStandaloneArticle() helper (data/ArticleSaving.kt), factored out of ShareReceiverActivity, now also used by the long-press menu - YouTube iframe embeds become tap-to-open thumbnails (JS is off in the reader WebView, so the real player never worked anyway); tapping one goes straight to the YouTube app/browser instead of our own article-extraction pipeline - Fixed back navigation through a chain of links-within-links: each hop was stacking a new backstack entry, so back stepped through the chain one link at a time instead of returning straight to the source article. Confirmed live: A -> link -> B -> link -> C, one back-press now goes straight to A - Extended the <header> chrome-stripping from last round: also strip <footer>, an author bio card, and a "Most Read" trending-stories widget -- confirmed live one of the latter showed up as a giant unrelated photo plus an empty numbered list (its lazy-loaded items never populate in this hidden, off-screen extraction WebView) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-22Fix extraction regression on kottke; add Older view, mark-read-from-herePeter Stone
- ReaderExtractor: the un-hide-before-extracting fix (for soft paywalls like West Hawaii Today) was unconditional -- it also un-hid normal, legitimately-collapsed chrome (kottke's footer/membership notice), which Readability then folded into the result alongside the real article. Reported live with a screenshot showing the extracted content wrapped in site footer boilerplate. Now tries the normal (hidden-respecting) pass first and only falls back to the un-hide pass if that came back thin (<500 chars), keeping whichever pass actually found more text. Verified live: the same kottke article now stays clean and stable (no delayed switch to the footer version). - New Unread/Older split: the default Unread view is now clamped to the last 72 hours (RECENT_WINDOW_MS); the rest of the unread backlog moved to a new "Older" view (new FeedListScreen row, ArticleDao.observeUnread now takes a cutoff, observeOlderUnread/observeUnreadCount/ observeOlderUnreadCount added). Undated articles are kept in the main Unread view rather than silently dropped from both. Verified live: 150 recent / 1172 older, summing to the prior combined total. - "Mark read from here": long-press an article row to mark it and everything older in that same filtered list as read in one batch (re-added ArticleDao.markReadBatch, this time as an explicit, confirmed action -- not the implicit on-scroll version that was reverted earlier). Verified live: confirmed a 147-article batch: the tapped article and older were marked read, newer ones above it were not. - BACKLOG.md: added a YouTube-embeds note (JS is off in the reader WebView, so embeds currently show broken -- rough shape is a tap-to-play thumbnail rather than turning JS on for third-party content).
2026-07-22Fix accidental page-swipe while scrolling; bring linked-article reader to parityPeter Stone
- ReaderScreen: HorizontalPager's full-surface drag detection was treating ordinary vertical scrolling inside the embedded WebView (a native View, which doesn't arbitrate with Compose's gesture system the way a pure-Compose scrollable child would) as an attempted horizontal page-turn on anything less than a perfectly vertical swipe -- i.e. most real thumb scrolling. Tried gating it to edge-zone-only drags first, but that collides with Android's own edge-swipe-back gesture on gesture-nav devices (reproduced live: an edge swipe triggered system back, not the pager). Landed on disabling the Pager's built-in gesture and driving it from a custom detector that works anywhere on screen but only commits to a page turn once a drag is clearly horizontal-dominant (>2x the vertical movement) -- ordinary scrolling, even sloppy/diagonal, is left completely unconsumed for the WebView. Verified live: a deliberately diagonal mid-screen scroll now stays on the same article; a decisive horizontal swipe still changes pages. - LinkReaderScreen: was missing the bookmark/Stash/reader-toggle/menu actions entirely -- reported after tapping a link left "all my icons... gone." Brought to parity with the main article reader: save locally and save to Stash now lazily create a standalone ArticleEntity (feedId=null, id = hash of the URL, same scheme ShareReceiverActivity uses) on first use; reader-mode toggle falls back to the live page (JS on) instead of always forcing extraction; same login/find-another-source menu. Verified live: all 5 icons present once extraction finishes, bookmarking a linked article correctly creates a standalone DB row.
2026-07-22Add site login and alternate-source search for paywalled articlesPeter Stone
Deliberately not a paywall-bypass mechanism -- that space (archive.today-style proxies, "Bypass Paywalls" extensions) is legally grey, adversarial toward publishers, and constantly breaking. Instead: - BrowserScreen: a real interactive mini-browser (JS + DOM storage on, ordinary navigation, no Readability interception) for the two cases where the user needs to actually USE a page rather than read it. Reached from a new overflow menu on the article reader: - "Log in to this site" opens the article's own URL there so the user can sign into a site they already subscribe to. Needs no extra plumbing: Android's CookieManager is one process-wide store shared by every WebView in the app, so a session cookie set here is picked up automatically by the reader's (JS-off) extraction WebView on the next visit to that domain -- same as a normal browser remembering you're signed in. - "Find another source" runs a web search on the article's title in the same browser, so the user can pick a different, non-paywalled outlet covering the same story themselves -- plain search/discovery, not circumvention. Verified live: search opened a real, interactive Google results page with working title-bar sync; back navigation (via BackHandler, goBack() when the WebView has history, else pop the screen) returned cleanly to the article.
2026-07-22Split save/Stash actions, follow links in-app via reader pipeline, tune ↵Peter Stone
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 "&#8220;" 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 &#8220; now renders the actual curly quote. All changes verified live on the emulator against the real 107-feed dataset.
2026-07-22Fix Unread/startup crash; add reader-mode toggle and swipe navigationPeter Stone
- Root cause of the reported crashes: observeUnread/observeByFeed/ observeSaved used SELECT *, pulling feedContentHtml/extractedContent (potentially large per-article blobs) just to render a list row. With 107 feeds worth of real articles this blows past Android's CursorWindow/memory limits, scaling with item count -- and since Unread is now the landing screen, it crashed at startup too. Added an ArticleListItem projection excluding the heavy content columns; list queries now only select what a row actually renders. - FilterRuleEngine now takes a small Titled interface (title/feedId) instead of the full entity, so it works with both ArticleEntity and the new lighter projection. - Reader-mode toggle: a button in the reader to manually switch between feed-provided content and Readability-extracted full text, matching Inoreader's full-text-view toggle (previously only an automatic fallback when feed content was thin). - Swipe between articles: ReaderScreen now wraps a HorizontalPager over a snapshot of the current list's article-id order, so swiping moves to the next/previous article. Snapshotted rather than live, since marking an article read would otherwise drop it from the live Unread query mid-swipe and shift every later page underneath the user. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-22First round of testing feedback: images, unread landing, dark, deletePeter Stone
- 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 <img> 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->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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-22Initial commit: Feeds, an Android RSS readerPeter Stone
Feed subscriptions (RSS 2.0 + Atom, hand-rolled parser), OPML import/export, WorkManager periodic fetch with conditional GET, Room-backed local storage (feeds/articles/filter rules), keyword mute/highlight filtering. Reader mode: on-device extraction via Mozilla's Readability.js (vendored from github.com/mozilla/readability, pinned to release 0.6.0) run in a hidden WebView, for feed articles whose provided content is too thin to be the real article body. Save: explicit action (from a feed article, or the Android share-sheet target for any URL) archives to Stash via POST /artifacts?sync=true, while a local on-device copy (Readability-extracted) is always kept so saved articles read offline regardless of Stash's reachability. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL