@@ -19,6 +19,7 @@ import android.webkit.RenderProcessGoneDetail
1919import android.webkit.WebView
2020import android.webkit.WebViewClient
2121import android.widget.FrameLayout
22+ import androidx.annotation.RequiresApi
2223import androidx.media3.common.C
2324import androidx.media3.common.MediaItem
2425import androidx.media3.common.MediaMetadata
@@ -109,15 +110,15 @@ object PlayerCore {
109110 private var blurType = " dark"
110111
111112 /* *
112- * Real-time blur of the actual PlayerView, not an overlay above it — a
113- * `RenderEffect` composited by the GPU directly onto the view that's
114- * rendering the video, so it blurs a live TextureView the way a bitmap-
115- * snapshot blur library can't.
113+ * Real-time blur of the actual video output, not an overlay above it — a
114+ * `RenderEffect` composited by the GPU directly onto whichever engine's
115+ * view is currently active (the PlayerView, or the YouTube engine's plain
116+ * WebView — both are ordinary Views a RenderEffect blurs the same way), so
117+ * it blurs a live TextureView the way a bitmap-snapshot blur library can't.
116118 *
117119 * API 31+ only: `RenderEffect` doesn't exist below Android 12, and there's
118120 * no reasonable fallback that would actually blur (vs. just covering) the
119- * video, so this is a silent no-op there — same as it already is for the
120- * WebView (YouTube) engine, which has no PlayerView to blur at all.
121+ * video, so this is a silent no-op there.
121122 *
122123 * `blurAmount` (0-100) maps to the blur radius; `blurType` tints the blur
123124 * (`"dark"` a black tint, `"light"`/`"xlight"` white at decreasing
@@ -132,17 +133,29 @@ object PlayerCore {
132133 }
133134
134135 /* *
135- * Re-applied whenever the PlayerView is (re)created — [setBlurred] can be
136- * called before the view exists yet (e.g. before the first attach), and
137- * the desired state must survive that.
136+ * Re-applied whenever the PlayerView/WebView is (re)created, and whenever
137+ * the active engine changes (a `RenderEffect` lives on one specific View
138+ * instance, so switching from url <-> youtube must move it) — see the
139+ * calls to this from [ensurePlayerView], [ensureWebView] and [attachTo].
140+ * [setBlurred] can also be called before either view exists yet (e.g.
141+ * before the first attach), and the desired state must survive that.
138142 */
139143 private fun applyBlur () {
140144 if (Build .VERSION .SDK_INT < Build .VERSION_CODES .S ) return
141- val view = playerView ? : return
142- if (! blurred) {
143- view.setRenderEffect(null )
144- return
145+ val effect = if (blurred) buildBlurEffect() else null
146+ // Always clear the inactive engine's view too, or a stale blur would
147+ // reappear with no explicit setBlurred call if the engine switches back.
148+ if (engine == Engine .WEB ) {
149+ webView?.setRenderEffect(effect)
150+ playerView?.setRenderEffect(null )
151+ } else {
152+ playerView?.setRenderEffect(effect)
153+ webView?.setRenderEffect(null )
145154 }
155+ }
156+
157+ @RequiresApi(Build .VERSION_CODES .S )
158+ private fun buildBlurEffect (): RenderEffect {
146159 // Radius 0 renders as a no-op blur but a jarring instant tint pop-in, so
147160 // floor it slightly once "blurred" is true at all.
148161 val radius = (blurAmount.coerceIn(0.0 , 100.0 ) / 100.0 * 25.0 ).coerceAtLeast(1.0 ).toFloat()
@@ -152,11 +165,10 @@ object PlayerCore {
152165 " xlight" -> Color .argb(60 , 255 , 255 , 255 )
153166 else -> Color .argb(120 , 0 , 0 , 0 ) // "dark"
154167 }
155- val tinted = RenderEffect .createColorFilterEffect(
168+ return RenderEffect .createColorFilterEffect(
156169 PorterDuffColorFilter (tint, PorterDuff .Mode .SRC_OVER ),
157170 blur
158171 )
159- view.setRenderEffect(tinted)
160172 }
161173
162174 // Second engine: a re-parentable WebView running the YouTube IFrame API.
@@ -425,6 +437,7 @@ object PlayerCore {
425437 }
426438 }
427439 webView = wv
440+ applyBlur()
428441 return wv
429442 }
430443
@@ -961,6 +974,9 @@ setInterval(function(){if(player&&player.getCurrentTime){post({type:'time',posit
961974 if (engine == Engine .EXO ) {
962975 playerView?.let { rebindPlayerToView(it, surfaceId, container) }
963976 }
977+ // Keeps the blur (if any) on whichever engine's view is now active —
978+ // `view` above may be a different engine's view than last time.
979+ applyBlur()
964980 listener?.onAttach(surfaceId)
965981 }
966982
0 commit comments