diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-08-22 01:00:02 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-08-22 01:00:02 +0000 |
| commit | fd49bacfb92ef8d29cbad56419acd016ff40f357 (patch) | |
| tree | 015caea07a9c556d39705c62a77b4a25f2b79219 /app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt | |
| parent | fb69196f961eeaa30432c33db3003effcdbced53 (diff) | |
In-app YouTube playback, shared ConfirmDialog, hardened link handling
- ReaderWebView/LinkReaderScreen/ReaderScreen: tapping a YouTube link
inside an article now opens an in-app player (youtube-nocookie.com,
JS scoped to just that dialog) instead of handing off to the
YouTube app/browser. Falls back to external-open when a confident
video ID can't be extracted (playlist/channel links).
- A subframe (ad/embed iframe) auto-navigating is now blocked outright
instead of being treated like a real link tap -- it fired the exact
same callback as a user tap, with no interaction required.
- wrapReaderHtml can now show a fallback hero image + date when the
feed's own body HTML has no image of its own.
- ConfirmDialog: the generic "confirm before doing something
irreversible" dialog shape was independently rebuilt from scratch at
FeedListScreen's unsubscribe and ArticleListScreen's mark-read-from-
here confirmations. Both now share one composable; the latter also
chunks its batch update (markReadBatchChunked) so a long-unread feed
can't exceed SQLite's per-statement bound-variable limit.
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt | 49 |
1 files changed, 37 insertions, 12 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt index 3451833..0f7540d 100644 --- a/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt @@ -39,6 +39,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.viewinterop.AndroidView import kotlinx.coroutines.launch @@ -80,6 +81,7 @@ fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Un var isReextracting by remember(url) { mutableStateOf(false) } var moreMenuExpanded by remember(url) { mutableStateOf(false) } var longPressedLink by remember(url) { mutableStateOf<String?>(null) } + var youtubeVideoId by remember(url) { mutableStateOf<String?>(null) } LaunchedEffect(url) { val existing = db.articleDao().getById(articleId) @@ -230,7 +232,12 @@ fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Un AndroidView( modifier = Modifier.fillMaxSize(), factory = { ctx -> - newReaderWebView(ctx, onOpenLink, onLongPressLink = { longPressedLink = it }).apply { + newReaderWebView( + ctx, + onOpenLink, + onLongPressLink = { longPressedLink = it }, + onYouTubeVideo = { youtubeVideoId = it } + ).apply { loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent, extractedTitle), "text/html", "UTF-8", null) loadedContent.value = currentExtractedContent } @@ -247,21 +254,39 @@ fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Un // Reader mode off, or extraction failed -- fall back to the live page // itself (JS on) rather than leaving the screen blank. !isReaderMode || extractionFailed -> { - AndroidView( - modifier = Modifier.fillMaxSize(), - factory = { ctx -> - WebView(ctx).apply { - settings.javaScriptEnabled = true - settings.domStorageEnabled = true - loadUrl(url) - } - }, - onRelease = { webView -> webView.destroy() } - ) + if (!url.startsWith("http://", ignoreCase = true) && !url.startsWith("https://", ignoreCase = true)) { + // `url` isn't necessarily a link the user themselves tapped: it can + // also be whatever a WebView's shouldOverrideUrlLoading reported for + // ANY navigation attempt inside an article (see ReaderWebView's + // onOpenLink), including one triggered by the article's own HTML + // linking to a non-http scheme (intent://, tel:, etc.). Refusing to + // load anything but http(s) here means this JS-enabled WebView can't + // become a sink for whatever scheme a crafted article handed it. + Text( + "Can't open this link: $url", + modifier = Modifier.fillMaxSize().padding(16.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } else { + AndroidView( + modifier = Modifier.fillMaxSize(), + factory = { ctx -> + WebView(ctx).apply { + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + loadUrl(url) + } + }, + onRelease = { webView -> webView.destroy() } + ) + } } else -> CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } } + youtubeVideoId?.let { videoId -> + YouTubePlayerDialog(videoId = videoId, onDismiss = { youtubeVideoId = null }) + } longPressedLink?.let { linkUrl -> LinkActionsDialog( url = linkUrl, |
