diff options
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.kt | 87 |
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 |
