From dccb5f53ddf9ba24a5033f15b421c3dbe6d00060 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 16 Aug 2026 18:46:15 +0200 Subject: [PATCH] fix(android): stop the rotation cross-fade replaying the old video frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rotating with native video on shows the previous frame flashing in what become the letterbox bars. It reads as a TextureView artefact — the view retains its last frame, so between the rotation and fitSurfaceToScreen() landing that frame sits at the old size — and two fixes were built on that reading: 1. reveal after two postOnAnimation hops. An animation frame is not a video frame; at 24fps the next decoded frame can be several vsyncs away. 2. reveal on onSurfaceTextureUpdated, i.e. when a real frame lands. This meant owning the SurfaceTextureListener and handing ExoPlayer the Surface directly instead of via setVideoTextureView, which installs its own and leaves us blind to frame arrival. Neither stopped the flash. The mechanism is the WINDOW's rotation animation: Android cross-fades a screenshot of the old orientation, that screenshot holds the old video frame at the old size, and nothing at the TextureView level can reach it. The app cannot pre-empt the screenshot either — onConfigurationChanged fires after it is taken. So the animation itself has to go: ROTATION_ANIMATION_JUMPCUT. That was accepted and silently ignored, and the platform said why out loud — "VRI[MainActivity]: setLayoutParams: not fullscreen" — because the attribute is honoured only for a fullscreen window. FLAG_FULLSCREEN is therefore set with it, scoped to while native compositing is active so the rest of the app keeps its normal animation. After the change that complaint is gone from logcat. The frame-arrival reveal is kept: it replaces a fixed-timeout guess with a real signal, and its timeout is required rather than defensive — a resize while paused means no new frame is ever coming, and revealing a stale frame beats a permanently black player. NOT CONFIRMED FIXED on device. The forced-rotation harness (settings put system user_rotation) proved unreliable here, and screenrecord fixes its canvas at start, so a rotation inside a recording never changes frame dimensions — which defeated two separate attempts to measure this. DR-194 is recorded as "Needs device verification" rather than Done. --- docs/requirements.md | 2 +- docs/traceability.md | 2 +- .../com/dtourolle/jellytau/MainActivity.kt | 59 ++++++++ .../jellytau/player/JellyTauPlayer.kt | 136 +++++++++++++++--- 4 files changed, 178 insertions(+), 21 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index bbac69ec..4aaa88c2 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -349,7 +349,7 @@ Internal architecture, components, and application logic. | DR-188 | Native Android video is **ready to be the default except for the background-audio handoff**, and the flip therefore waits. The picture defects behind DR-172 are all found, fixed and device-verified — DR-185 (the app shell painted over the surface through a CSS rule targeting an attribute nothing set), DR-182 (nothing could lift the poster card on a path with no `