diff --git a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt index 27285b1a..3728bf20 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerCore.kt @@ -1390,23 +1390,21 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener { trackSelector = null assHandler = null - surfaceView?.holder?.removeCallback(surfaceCallback) + val cb = surfaceCallback + val sv = surfaceView overlayLayoutListener?.let { listener -> val contentView = activity.findViewById(android.R.id.content) contentView.viewTreeObserver.removeOnGlobalLayoutListener(listener) } overlayLayoutListener = null - // Remove SurfaceView from container synchronously — triggers - // onDetachedFromWindow which properly unregisters PositionUpdateListener - // and releases SurfaceControl before the render thread can access it. + // Defer all view removal to avoid AOSP bug where + // dispatchWindowVisibilityChanged iterates stale children array + // when removeView() runs during an active performTraversals pass. val container = surfaceContainer - surfaceView?.let { container?.removeView(it) } - subtitleView?.let { container?.removeView(it) } - - // Defer empty container removal (plain FrameLayout, no SurfaceControl) val contentView = activity.findViewById(android.R.id.content) contentView.post { + sv?.holder?.removeCallback(cb) container?.let { contentView.removeView(it) } } surfaceContainer = null diff --git a/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt b/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt index 13a2f97b..0e894414 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt @@ -809,15 +809,14 @@ class MpvPlayerCore(private val activity: Activity) : } overlayLayoutListener = null - // Remove SurfaceView synchronously (keep callback so surfaceDestroyed - // fires → MPVLib.detachSurface()), then remove callback after. + // Defer all view removal to avoid AOSP bug where + // dispatchWindowVisibilityChanged iterates stale children array + // when removeView() runs during an active performTraversals pass. + val sv = surfaceView val container = surfaceContainer - surfaceView?.let { container?.removeView(it) } - surfaceView?.holder?.removeCallback(this) - - // Defer empty container removal (plain FrameLayout, no SurfaceControl) val contentView = activity.findViewById(android.R.id.content) contentView.post { + sv?.holder?.removeCallback(this) container?.let { contentView.removeView(it) } } surfaceContainer = null