Expandable player: static poster during collapse/morph, live player only when expanded — vc=77
The remaining sluggishness was scaling a live-playing TextureView through the morph's graphicsLayer every frame. Now the minibar + the whole collapse/expand morph render the video's static poster (AsyncImage); the live PlayerView only mounts once settled fully expanded. Audio is unaffected — it's owned by the foreground service, never stops.
This commit is contained in:
parent
d7f86ebf14
commit
05381e8d8b
2 changed files with 45 additions and 28 deletions
|
|
@ -9,6 +9,13 @@ const val STRAW_SDK_TARGET = 35
|
||||||
|
|
||||||
// Sulkta fork — Straw
|
// Sulkta fork — Straw
|
||||||
//
|
//
|
||||||
|
// vc=77 / 0.1.0-CK — morph perf: static poster during collapse/morph:
|
||||||
|
// * The minibar + the whole collapse/expand morph now render the video's
|
||||||
|
// static poster, not the live TextureView. Scaling a live-playing
|
||||||
|
// TextureView through the morph's graphicsLayer every frame was the
|
||||||
|
// remaining sluggishness; the live PlayerView only mounts once settled
|
||||||
|
// fully expanded. Audio is unaffected (it's in the foreground service).
|
||||||
|
//
|
||||||
// vc=76 / 0.1.0-CJ — expandable-player smoothness pass:
|
// vc=76 / 0.1.0-CJ — expandable-player smoothness pass:
|
||||||
// * The detail body no longer renders to an offscreen buffer every
|
// * The detail body no longer renders to an offscreen buffer every
|
||||||
// frame during the morph (CompositingStrategy.ModulateAlpha) — that
|
// frame during the morph (CompositingStrategy.ModulateAlpha) — that
|
||||||
|
|
@ -82,6 +89,6 @@ const val STRAW_SDK_TARGET = 35
|
||||||
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
|
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
|
||||||
// strawcore-core (Sulkta-OSS/strawcore) via the UniFFI wrapper; no
|
// strawcore-core (Sulkta-OSS/strawcore) via the UniFFI wrapper; no
|
||||||
// NewPipeExtractor in the runtime path.
|
// NewPipeExtractor in the runtime path.
|
||||||
const val STRAW_VERSION_CODE = 76
|
const val STRAW_VERSION_CODE = 77
|
||||||
const val STRAW_VERSION_NAME = "0.1.0-CJ"
|
const val STRAW_VERSION_NAME = "0.1.0-CK"
|
||||||
const val STRAW_APPLICATION_ID = "com.sulkta.straw"
|
const val STRAW_APPLICATION_ID = "com.sulkta.straw"
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@ fun ExpandablePlayer(
|
||||||
InlinePlayerSurface(
|
InlinePlayerSurface(
|
||||||
streamUrl = cur.streamUrl,
|
streamUrl = cur.streamUrl,
|
||||||
title = cur.title,
|
title = cur.title,
|
||||||
controlsEnabled = fullyExpanded,
|
expanded = fullyExpanded,
|
||||||
onFullscreen = { onFullscreen(cur.streamUrl, cur.title) },
|
onFullscreen = { onFullscreen(cur.streamUrl, cur.title) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -413,17 +413,18 @@ private fun BarIconButton(
|
||||||
* The single TextureView-backed PlayerView, plus the resolve→play wiring
|
* The single TextureView-backed PlayerView, plus the resolve→play wiring
|
||||||
* that used to live in VideoDetailScreen's InlinePlayer. Renders a
|
* that used to live in VideoDetailScreen's InlinePlayer. Renders a
|
||||||
* thumbnail + spinner until the shared controller has actually swapped to
|
* thumbnail + spinner until the shared controller has actually swapped to
|
||||||
* this video, then the live surface. [controlsEnabled] gates the Media3
|
* this video. [expanded] = fully expanded + settled: only then do we mount
|
||||||
* controller overlay (off while collapsed so taps fall through to the
|
* the live PlayerView (+ controls). While collapsed or mid-morph we show a
|
||||||
* expand gesture). Honors the Auto-start-playback setting: when off, a
|
* static poster instead, so the morph never scales a live TextureView frame
|
||||||
* Play overlay waits for a tap before priming the stream.
|
* by frame (the sluggishness). Honors Auto-start-playback: when off, a Play
|
||||||
|
* overlay waits for a tap before priming the stream.
|
||||||
*/
|
*/
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun InlinePlayerSurface(
|
private fun InlinePlayerSurface(
|
||||||
streamUrl: String,
|
streamUrl: String,
|
||||||
title: String,
|
title: String,
|
||||||
controlsEnabled: Boolean,
|
expanded: Boolean,
|
||||||
onFullscreen: () -> Unit,
|
onFullscreen: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val controller = LocalStrawController.current
|
val controller = LocalStrawController.current
|
||||||
|
|
@ -533,40 +534,49 @@ private fun InlinePlayerSurface(
|
||||||
}
|
}
|
||||||
CircularProgressIndicator(color = Color.White)
|
CircularProgressIndicator(color = Color.White)
|
||||||
}
|
}
|
||||||
|
!expanded -> {
|
||||||
|
// Collapsed or mid-morph: show the static poster, NOT the live
|
||||||
|
// TextureView. Scaling a live-playing TextureView through the
|
||||||
|
// morph's graphicsLayer every frame was the remaining cost —
|
||||||
|
// only mount the real player once we've settled fully expanded.
|
||||||
|
// Audio keeps playing via the service the whole time.
|
||||||
|
val poster = nowPlaying?.thumbnail ?: thumbnail
|
||||||
|
if (!poster.isNullOrBlank()) {
|
||||||
|
AsyncImage(
|
||||||
|
model = poster,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { ctx ->
|
factory = { ctx ->
|
||||||
// Inflate from XML for a TEXTURE_VIEW surface — a
|
// Inflate from XML for a TEXTURE_VIEW surface — a
|
||||||
// SurfaceView won't follow the graphicsLayer scale,
|
// SurfaceView won't follow the graphicsLayer scale.
|
||||||
// which is exactly the morph here.
|
|
||||||
(LayoutInflater.from(ctx)
|
(LayoutInflater.from(ctx)
|
||||||
.inflate(R.layout.inline_player_view, null) as PlayerView)
|
.inflate(R.layout.inline_player_view, null) as PlayerView)
|
||||||
.apply {
|
.apply {
|
||||||
player = controller
|
player = controller
|
||||||
useController = controlsEnabled
|
useController = true
|
||||||
keepScreenOn = true
|
keepScreenOn = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update = {
|
update = { it.player = controller },
|
||||||
it.player = controller
|
|
||||||
it.useController = controlsEnabled
|
|
||||||
},
|
|
||||||
onRelease = { it.player = null },
|
onRelease = { it.player = null },
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
if (controlsEnabled) {
|
Box(
|
||||||
Box(
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.align(Alignment.TopEnd)
|
||||||
.align(Alignment.TopEnd)
|
.padding(8.dp)
|
||||||
.padding(8.dp)
|
.size(36.dp)
|
||||||
.size(36.dp)
|
.clip(RoundedCornerShape(6.dp))
|
||||||
.clip(RoundedCornerShape(6.dp))
|
.background(OverlayChromeColor)
|
||||||
.background(OverlayChromeColor)
|
.clickable(onClick = onFullscreen),
|
||||||
.clickable(onClick = onFullscreen),
|
contentAlignment = Alignment.Center,
|
||||||
contentAlignment = Alignment.Center,
|
) {
|
||||||
) {
|
Text("⛶", color = Color.White)
|
||||||
Text("⛶", color = Color.White)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue