diff --git a/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt b/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt index 166efe0..47538f3 100644 --- a/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt +++ b/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt @@ -905,15 +905,22 @@ fun VideoPlayerScreen( } Column(Modifier.fillMaxWidth().statusBarsPadding().padding(top = LumaSpacing.md)) { - // P0: Delegated to extracted composable — only this component polls and recomposes - PlayerStatusBar( - playbackUrl = playbackUrl, - networkSpeed = networkSpeed, - isWifi = isWifi, - batteryPercent = batteryPercent, - isCharging = isCharging, - ) - Spacer(Modifier.height(8.dp)) + // 第一排:时间(左)| 网速+电量(右) + Row( + Modifier.fillMaxWidth().padding(horizontal = capsuleInset), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + PlayerTimeDisplay(modifier = Modifier) + PlayerNetworkInfo( + playbackUrl = playbackUrl, + networkSpeed = networkSpeed, + isWifi = isWifi, + batteryPercent = batteryPercent, + isCharging = isCharging, + ) + } + Spacer(Modifier.height(2.dp)) // 第二排:关闭按钮(左)|Emby 艺术字(紧跟关闭按钮右侧)|字幕/剧集按钮(右) Row( Modifier.fillMaxWidth().padding(horizontal = capsuleInset), @@ -974,9 +981,8 @@ fun VideoPlayerScreen( } } if (serverName.isNotBlank()) { - Spacer(Modifier.height(8.dp)) // 第三排:服务器图标 + 服务器名字 - Row(Modifier.fillMaxWidth().padding(start = capsuleInset + 52.dp, end = capsuleInset), verticalAlignment = Alignment.CenterVertically) { + Row(Modifier.fillMaxWidth().padding(start = capsuleInset + 52.dp, end = capsuleInset, top = 2.dp), verticalAlignment = Alignment.CenterVertically) { Box(Modifier.size(30.dp).clip(CircleShape), contentAlignment = Alignment.Center) { if (serverIconUrl.isNotBlank()) { AsyncImage( @@ -1624,51 +1630,46 @@ private fun LiquidMenuItem(text: String, selected: Boolean, onClick: () -> Unit) } } -// P0: Extract status bar into its own composable so polling states (time/wifi/battery/speed) -// only recompose this small component instead of the entire VideoPlayerScreen. +// Extract status bar into separate composables so polling states only recompose their own component. @Composable -private fun PlayerStatusBar( - playbackUrl: String?, - networkSpeed: Long, - isWifi: Boolean, - batteryPercent: Int, - isCharging: Boolean, - capsuleInset: Dp = 18.dp, -) { +private fun PlayerTimeDisplay(modifier: Modifier = Modifier) { val topTimeText = remember { mutableStateOf("") } - val localNetworkSpeed by rememberUpdatedState(networkSpeed) - val localIsWifi by rememberUpdatedState(isWifi) - val localBatteryPercent by rememberUpdatedState(batteryPercent) - val localIsCharging by rememberUpdatedState(isCharging) - - // Clock update — only this composable recomposes LaunchedEffect(Unit) { while (true) { topTimeText.value = java.text.SimpleDateFormat("HH:mm", java.util.Locale.getDefault()).format(java.util.Date()) delay(1000) } } + Text(topTimeText.value, color = Color.White, fontSize = 13.sp, modifier = modifier) +} - Row( - Modifier.fillMaxWidth().padding(horizontal = capsuleInset), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text(topTimeText.value, color = Color.White, fontSize = 13.sp) - Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp)) { - if (playbackUrl != null) { - Row(verticalAlignment = Alignment.CenterVertically) { - LumaIconView(if (localIsWifi) LumaIcon.Wifi else LumaIcon.Signal, contentDescription = null, tint = Color.White, modifier = Modifier.size(16.dp)) - Spacer(Modifier.width(4.dp)) - Text(formatNetworkSpeed(localNetworkSpeed), color = Color.White, fontSize = 13.sp) - } +@Composable +private fun PlayerNetworkInfo( + playbackUrl: String?, + networkSpeed: Long, + isWifi: Boolean, + batteryPercent: Int, + isCharging: Boolean, + modifier: Modifier = Modifier, +) { + val localNetworkSpeed by rememberUpdatedState(networkSpeed) + val localIsWifi by rememberUpdatedState(isWifi) + val localBatteryPercent by rememberUpdatedState(batteryPercent) + val localIsCharging by rememberUpdatedState(isCharging) + + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = modifier) { + if (playbackUrl != null) { + Row(verticalAlignment = Alignment.CenterVertically) { + LumaIconView(if (localIsWifi) LumaIcon.Wifi else LumaIcon.Signal, contentDescription = null, tint = Color.White, modifier = Modifier.size(16.dp)) + Spacer(Modifier.width(4.dp)) + Text(formatNetworkSpeed(localNetworkSpeed), color = Color.White, fontSize = 13.sp) } - if (localBatteryPercent >= 0) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text("$localBatteryPercent%", color = Color.White, fontSize = 13.sp) - Spacer(Modifier.width(4.dp)) - BatteryIcon(level = localBatteryPercent, charging = localIsCharging, modifier = Modifier.size(22.dp, 12.dp)) - } + } + if (localBatteryPercent >= 0) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text("$localBatteryPercent%", color = Color.White, fontSize = 13.sp) + Spacer(Modifier.width(4.dp)) + BatteryIcon(level = localBatteryPercent, charging = localIsCharging, modifier = Modifier.size(22.dp, 12.dp)) } } }