summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-09-15 23:12:44 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-09-15 23:12:44 +0000
commitb5a96ad1d0a412c21159d6265e451f7c3aefc796 (patch)
treee1284afa3b8ef2bd896342c95d5db55430434813 /app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt
parent0cd94d0821661fef3fc34b51011be95034e51a5b (diff)
Support pinch-to-zoom for reader text size in the article view
Pinching in ReaderScreen's article WebView now adjusts the same persisted reader font size the Settings slider controls, instead of doing nothing (WebView's own page zoom was never enabled, so a pinch previously had no effect at all). A ScaleGestureDetector is fed through setOnTouchListener, which always returns false so it never consumes the touch -- WebView's existing one-finger scrolling, link taps and long-press-link handling are unaffected, since ScaleGestureDetector only fires once a second pointer is down. During the gesture the WebView is scaled live via View.scaleX/ scaleY for immediate visual feedback; at gesture end the scale resets to 1x and the computed, clamped font size is persisted, which drives ReaderScreen's existing content-reload-on-fontSizePx-change path to re-render the actual HTML at the new size. TDD: pinchZoomFontSizePx (the pure scale-factor -> clamped-px calculation) was written test-first, red on a TODO() stub, green on the real implementation -- covered by SettingsTest. newReaderWebView grew from 6 to 8 parameters to carry the new gesture hooks, which pushed it over detekt's LongParameterList threshold; bundled all of its callbacks into a ReaderWebViewCallbacks data class rather than baselining past it, updating both call sites (ReaderScreen, LinkReaderScreen). ReaderContent gained no new parameter -- it now independently remembers its own Settings instance to persist the pinch-committed size, matching how it already remembers its own db and scope rather than threading them from ReaderScreen. LinkReaderScreen's WebView is untouched: it doesn't read the reader font size setting at all today, so wiring pinch-zoom there would be a separate, unrelated fix.
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt')
-rw-r--r--app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt30
1 files changed, 22 insertions, 8 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt
index a89052c..e2b8aa9 100644
--- a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt
+++ b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt
@@ -46,6 +46,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -180,6 +181,10 @@ private fun ReaderContent(
) {
val context = LocalContext.current
val db = remember { AppDatabase.get(context) }
+ // Independently remembered rather than threaded down from ReaderScreen, matching db/scope
+ // above -- Settings is a stateless DataStore wrapper, cheap to construct again, and this
+ // keeps pinch-to-zoom from growing ReaderContent's already-long parameter list any further.
+ val settings = remember { Settings(context) }
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
@@ -526,19 +531,28 @@ private fun ReaderContent(
// changing it in Settings and coming back to an already-open article
// picks up the new size, without reloading on every unrelated recomposition.
val loadedKey = remember(articleId) { mutableStateOf<Pair<String?, Int>?>(null) }
+ // newReaderWebView's factory runs once per WebView instance, but fontSizePx can
+ // change later (e.g. the user tweaks it in Settings while this same article stays
+ // open) -- rememberUpdatedState keeps the pinch gesture's "what am I scaling from"
+ // read current instead of frozen at whatever it was on first composition.
+ val latestFontSizePx by rememberUpdatedState(fontSizePx)
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { ctx ->
newReaderWebView(
ctx,
- onOpenLink,
- onLongPressLink = { longPressedLink = it },
- onYouTubeVideo = { youtubeVideoId = it },
- onScrollChanged = { scrollY -> ReaderScrollPositions.set(articleId, scrollY) },
- // The page's real content height isn't known until layout finishes
- // -- scrolling to a saved offset any earlier would just clamp back
- // to 0 on a page that hasn't grown tall enough yet.
- onPageFinished = { webView -> webView.scrollTo(0, ReaderScrollPositions.get(articleId)) }
+ ReaderWebViewCallbacks(
+ onOpenLink = onOpenLink,
+ onLongPressLink = { longPressedLink = it },
+ onYouTubeVideo = { youtubeVideoId = it },
+ onScrollChanged = { scrollY -> ReaderScrollPositions.set(articleId, scrollY) },
+ // The page's real content height isn't known until layout finishes
+ // -- scrolling to a saved offset any earlier would just clamp back
+ // to 0 on a page that hasn't grown tall enough yet.
+ onPageFinished = { webView -> webView.scrollTo(0, ReaderScrollPositions.get(articleId)) },
+ currentFontSizePx = { latestFontSizePx },
+ onFontSizeChanged = { newSizePx -> scope.launch { settings.setReaderFontSizePx(newSizePx) } }
+ )
).apply {
val html = wrapReaderHtml(content, article?.title, article?.publishedAt, article?.imageUrl, fontSizePx)
loadDataWithBaseURL(baseUrl, html, "text/html", "UTF-8", null)