diff options
Diffstat (limited to 'app/src')
3 files changed, 74 insertions, 0 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 3af460a..68562e8 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt @@ -416,6 +416,16 @@ fun YouTubePlayerDialog(videoId: String, onDismiss: () -> Unit) { host == "youtube.com" || host.endsWith(".youtube.com") || host == "youtu.be" return !allowed } + + // The player has no height of its own in here: YouTube sizes itself with + // percentage heights, and in this WebView those resolve to zero however + // tall the view actually is. Without this the video element is laid out + // 380x0 -- audio plays, the controls draw, and the picture has nowhere + // to go. See YOUTUBE_FIT_PLAYER_JS. + override fun onPageFinished(view: WebView, url: String?) { + super.onPageFinished(view, url) + view.evaluateJavascript(YOUTUBE_FIT_PLAYER_JS, null) + } } webChromeClient = WebChromeClient() // The embed as this WebView's own ordinary document, with a Referer diff --git a/app/src/main/java/org/terst/feedreader/ui/YouTubeLinks.kt b/app/src/main/java/org/terst/feedreader/ui/YouTubeLinks.kt index 00624a1..a971615 100644 --- a/app/src/main/java/org/terst/feedreader/ui/YouTubeLinks.kt +++ b/app/src/main/java/org/terst/feedreader/ui/YouTubeLinks.kt @@ -97,6 +97,41 @@ fun youTubeEmbedUrl(videoId: String): String = "https://www.youtube-nocookie.com/embed/${scrub(videoId)}?autoplay=1&playsinline=1&rel=0" /** + * A script that gives YouTube's player a height, run once the embed has loaded. + * + * **Why a WebView needs this and a browser does not.** YouTube's embed lays itself out with + * `body { position: absolute; height: 100% }`, which resolves against the initial containing + * block -- and in this WebView that block has zero height. Measured in the running app: + * `window.innerHeight` correctly reports 214, while `100vh` and every percentage height resolve + * to 0. So the body collapsed, the player chain under it collapsed, and the video element was + * laid out 380x0. + * + * That is why the symptom was a *blank rectangle with working audio and working controls*: the + * page, the chrome and the stream were all fine, and the picture had nowhere to go. What showed + * through the zero-height player was YouTube's own white `<html>` background. + * + * The script pins the chain to the pixel height the page does know, and dispatches a resize so + * the player's own code re-measures. Confirmed on a device: the video element goes from 380x0 to + * 380x214 and the picture appears. + * + * This treats the symptom rather than whatever makes the viewport zero-height in the first + * place, which is below this app in the WebView itself. `!important` is deliberate -- YouTube's + * own styles would otherwise win it back on the next layout. + */ +const val YOUTUBE_FIT_PLAYER_JS: String = + """(function () { + var h = window.innerHeight; + if (!h) { return; } + var css = document.createElement('style'); + css.textContent = + 'html,body{height:' + h + 'px !important;min-height:' + h + 'px !important}' + + '#player,#movie_player,.html5-video-player,.html5-video-container{height:' + h + 'px !important}' + + 'video{height:' + h + 'px !important}'; + document.head.appendChild(css); + window.dispatchEvent(new Event('resize')); + })();""" + +/** * The id with anything outside YouTube's own alphabet removed. * * The ids reaching here are already matched against `[a-zA-Z0-9_-]{11}` upstream, so this diff --git a/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt b/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt index af9884d..d2c623c 100644 --- a/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt +++ b/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt @@ -127,4 +127,33 @@ class YouTubeLinksTest { fun `the watch link for a video is the one a phone can hand to the YouTube app`() { assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ", youTubeWatchUrl("dQw4w9WgXcQ")) } + + // --- giving the player a height ------------------------------------------------------- + + @Test + fun `the fit script sizes the player from the height the page does know`() { + val js = YOUTUBE_FIT_PLAYER_JS + + assertTrue("percentage heights are what resolve to zero: $js", js.contains("window.innerHeight")) + assertTrue(js.contains("dispatchEvent")) + } + + @Test + fun `the fit script overrides YouTube's own styles, which would otherwise win`() { + assertTrue(YOUTUBE_FIT_PLAYER_JS.contains("!important")) + } + + @Test + fun `the fit script covers the video element and the containers above it`() { + val js = YOUTUBE_FIT_PLAYER_JS + + assertTrue(js.contains("html,body")) + assertTrue(js.contains("#movie_player")) + assertTrue(js.contains("video{height:")) + } + + @Test + fun `the fit script does nothing when the page has no height to copy`() { + assertTrue("must not pin the player to zero", YOUTUBE_FIT_PLAYER_JS.contains("if (!h) { return; }")) + } } |
