feat(player): enhance video aspect ratio handling and resize mode logic

This commit is contained in:
2026-05-14 21:44:50 +02:00
parent deacb89adb
commit b7186be46a

View File

@@ -12,7 +12,6 @@ import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -24,6 +23,7 @@ import androidx.compose.material.icons.outlined.VolumeUp
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -39,6 +39,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.media3.common.Player
import androidx.media3.common.VideoSize
import androidx.media3.common.util.UnstableApi
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
@@ -61,6 +63,7 @@ import kotlin.math.abs
import kotlin.math.roundToInt
private const val CONTROLS_VISIBLE_SUBTITLE_BOTTOM_PADDING_FRACTION = 0.32f
private const val WIDE_VIDEO_ASPECT_RATIO = 16f / 9f
@OptIn(UnstableApi::class)
@Composable
@@ -80,12 +83,25 @@ fun PlayerScreen(
var showQueuePanel by remember { mutableStateOf(false) }
var horizontalSeekFeedback by remember { mutableStateOf<Long?>(null) }
var horizontalSeekPreviewPositionMs by remember { mutableStateOf<Long?>(null) }
var videoAspectRatio by remember { mutableStateOf(calculateVideoAspectRatio(viewModel.player.videoSize)) }
val overlayController = rememberPersistentOverlayController()
LifecycleEventEffect(Lifecycle.Event.ON_STOP) {
viewModel.pausePlayback()
}
DisposableEffect(viewModel.player) {
val listener = object : Player.Listener {
override fun onVideoSizeChanged(videoSize: VideoSize) {
videoAspectRatio = calculateVideoAspectRatio(videoSize)
}
}
viewModel.player.addListener(listener)
onDispose {
viewModel.player.removeListener(listener)
}
}
LaunchedEffect(uiState.isPlaying) {
if (uiState.isPlaying) {
showQueuePanel = false
@@ -93,6 +109,7 @@ fun PlayerScreen(
}
val playerControlsVisible = controlsVisible || uiState.isEnded || uiState.error != null
val playerResizeMode = playerResizeMode(videoAspectRatio)
val subtitleBottomPaddingFraction =
if (playerControlsVisible) {
CONTROLS_VISIBLE_SUBTITLE_BOTTOM_PADDING_FRACTION
@@ -109,18 +126,18 @@ fun PlayerScreen(
factory = { ctx ->
PlayerView(ctx).apply {
useController = false
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
resizeMode = playerResizeMode
player = viewModel.player
subtitleView?.setBottomPaddingFraction(subtitleBottomPaddingFraction)
}
},
update = {
it.player = viewModel.player
it.resizeMode = playerResizeMode
it.subtitleView?.setBottomPaddingFraction(subtitleBottomPaddingFraction)
},
modifier = Modifier
.fillMaxHeight()
.align(Alignment.Center)
.fillMaxSize()
)
PlayerGesturesLayer(
@@ -294,6 +311,21 @@ fun PlayerScreen(
}
}
@OptIn(UnstableApi::class)
private fun playerResizeMode(videoAspectRatio: Float): Int =
if (videoAspectRatio >= WIDE_VIDEO_ASPECT_RATIO) {
AspectRatioFrameLayout.RESIZE_MODE_ZOOM
} else {
AspectRatioFrameLayout.RESIZE_MODE_FIT
}
private fun calculateVideoAspectRatio(videoSize: VideoSize): Float {
if (videoSize.width <= 0 || videoSize.height <= 0) {
return 0f
}
return videoSize.width * videoSize.pixelWidthHeightRatio / videoSize.height
}
@Composable
private fun HiddenSeekTimeline(
positionMs: Long,