summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt')
-rw-r--r--app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt87
1 files changed, 74 insertions, 13 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt b/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt
index b13fc58..039255f 100644
--- a/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt
+++ b/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt
@@ -3,6 +3,7 @@ package org.terst.feedreader.ui
import android.content.Context
import android.content.Intent
import android.net.Uri
+import android.view.ScaleGestureDetector
import android.view.View
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
@@ -23,6 +24,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import org.terst.feedreader.data.DEFAULT_READER_FONT_SIZE_PX
+import org.terst.feedreader.data.pinchZoomFontSizePx
// In-memory only (process lifetime, not persisted to disk) -- scoped to "don't lose my
// place if Next/Previous takes me away and back," not to surviving an app restart. A
@@ -76,15 +78,44 @@ private fun extractYouTubeVideoId(uri: Uri): String? {
* YouTubePlayerDialog instead, so watching a video doesn't leave the reader. Falls
* back to the external-open behavior when a confident video ID can't be extracted
* (a playlist/channel link, say).
+ *
+ * Pinch-to-zoom adjusts the persisted reader font size, not WebView's own page zoom
+ * (never enabled here -- setSupportZoom/builtInZoomControls are left at their defaults,
+ * which is effectively off). A real page zoom would scale layout and images along with
+ * text and wouldn't survive the next article/reload; this app already has a font-size
+ * concept the rest of the reader respects (Settings.readerFontSizePx, adjustable from
+ * the Settings screen), so pinching should drive that same value instead of inventing a
+ * second, WebView-local notion of "how big does this look right now."
+ *
+ * Getting this right without breaking ordinary one-finger scrolling/tapping is the whole
+ * trick: a ScaleGestureDetector is fed every touch via setOnTouchListener, but that
+ * listener always returns false (never consumes the event), so WebView's own touch
+ * handling -- scrolling, link taps, the long-press-link listener above -- proceeds
+ * exactly as before. ScaleGestureDetector only calls back once a second pointer is down,
+ * so a single-finger drag never triggers any of this.
+ *
+ * currentFontSizePx is read fresh (not captured once) when a gesture ends, since the
+ * WebView this attaches to is created once but can outlive a Settings-screen-driven font
+ * size change made while this same article is still open.
+ *
+ * During the gesture the WebView is visually scaled live (View.scaleX/scaleY) for
+ * immediate feedback -- cheap, and independent of the WebView's own content layout.
+ * At gesture end the scale resets to 1x and onFontSizeChanged reports the new,
+ * clamped, rounded font size for the caller to persist; ReaderScreen's existing
+ * content-vs-fontSizePx reload logic (see ReaderScreen.ReaderContent) then re-renders
+ * the actual HTML at that size.
*/
-fun newReaderWebView(
- context: Context,
- onOpenLink: (String) -> Unit,
- onLongPressLink: (String) -> Unit = {},
- onYouTubeVideo: (String) -> Unit = {},
- onScrollChanged: (Int) -> Unit = {},
- onPageFinished: (WebView) -> Unit = {}
-): WebView =
+data class ReaderWebViewCallbacks(
+ val onOpenLink: (String) -> Unit,
+ val onLongPressLink: (String) -> Unit = {},
+ val onYouTubeVideo: (String) -> Unit = {},
+ val onScrollChanged: (Int) -> Unit = {},
+ val onPageFinished: (WebView) -> Unit = {},
+ val currentFontSizePx: () -> Int = { DEFAULT_READER_FONT_SIZE_PX },
+ val onFontSizeChanged: (Int) -> Unit = {}
+)
+
+fun newReaderWebView(context: Context, callbacks: ReaderWebViewCallbacks): WebView =
WebView(context).apply {
settings.javaScriptEnabled = false
overScrollMode = View.OVER_SCROLL_NEVER
@@ -95,11 +126,41 @@ fun newReaderWebView(
// Next/Previous replaces this screen's back-stack entry with a fresh one for the same
// article, which recreates the WebView from scratch and would otherwise always land
// back at the top.
- setOnScrollChangeListener { _, _, scrollY, _, _ -> onScrollChanged(scrollY) }
+ setOnScrollChangeListener { _, _, scrollY, _, _ -> callbacks.onScrollChanged(scrollY) }
+
+ var pinchScale = 1f
+ val webView = this
+ val scaleDetector = ScaleGestureDetector(
+ context,
+ object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
+ pinchScale = 1f
+ return true
+ }
+
+ override fun onScale(detector: ScaleGestureDetector): Boolean {
+ pinchScale *= detector.scaleFactor
+ webView.scaleX = pinchScale
+ webView.scaleY = pinchScale
+ return true
+ }
+
+ override fun onScaleEnd(detector: ScaleGestureDetector) {
+ val newSizePx = pinchZoomFontSizePx(callbacks.currentFontSizePx(), pinchScale)
+ webView.scaleX = 1f
+ webView.scaleY = 1f
+ callbacks.onFontSizeChanged(newSizePx)
+ }
+ }
+ )
+ setOnTouchListener { _, event ->
+ scaleDetector.onTouchEvent(event)
+ false // never consume -- WebView still handles scroll/tap/long-press normally
+ }
webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String?) {
super.onPageFinished(view, url)
- onPageFinished(view)
+ callbacks.onPageFinished(view)
}
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
@@ -117,12 +178,12 @@ fun newReaderWebView(
if (isYouTubeWatchUrl(url)) {
val videoId = extractYouTubeVideoId(url)
if (videoId != null) {
- onYouTubeVideo(videoId)
+ callbacks.onYouTubeVideo(videoId)
} else {
context.startActivity(Intent(Intent.ACTION_VIEW, url))
}
} else {
- onOpenLink(url.toString())
+ callbacks.onOpenLink(url.toString())
}
return true
}
@@ -134,7 +195,7 @@ fun newReaderWebView(
else -> null
}
if (url != null) {
- onLongPressLink(url)
+ callbacks.onLongPressLink(url)
true
} else {
false