diff --git a/core/src/main/kotlin/plus/rua/project/ui/DogParkScreen.kt b/core/src/main/kotlin/plus/rua/project/ui/DogParkScreen.kt new file mode 100644 index 0000000..155f346 --- /dev/null +++ b/core/src/main/kotlin/plus/rua/project/ui/DogParkScreen.kt @@ -0,0 +1,92 @@ +package plus.rua.project.ui + +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.media3.common.MediaItem +import androidx.media3.common.Player +import androidx.media3.common.PlaybackException +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.ui.AspectRatioFrameLayout +import androidx.media3.ui.PlayerView + +/** + * 小狗乐园彩蛋页面,全屏循环播放彩蛋视频。 + * + * 特性: + * - 视频源:`asset:///video/enter_screen_bg1.mp4` + * - 等比裁剪铺满全屏(`RESIZE_MODE_ZOOM`) + * - 静音、无播放控件、循环播放 + * - 跟随生命周期自动 play / pause,离开页面时自动释放 + * + * @param onPlaybackError 播放出错时的回调(通常由调用方 finish Activity) + * @param modifier 布局修饰符 + */ +@Composable +fun DogParkScreen( + onPlaybackError: () -> Unit, + modifier: Modifier = Modifier +) { + val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current + val player = remember(context) { + ExoPlayer.Builder(context).build().apply { + volume = 0f + repeatMode = Player.REPEAT_MODE_ONE + setMediaItem(MediaItem.fromUri("asset:///video/enter_screen_bg1.mp4")) + addListener(object : Player.Listener { + override fun onPlayerError(error: PlaybackException) { + onPlaybackError() + } + }) + prepare() + if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { + play() + } + } + } + + DisposableEffect(player, lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + when (event) { + Lifecycle.Event.ON_RESUME -> player.play() + Lifecycle.Event.ON_PAUSE -> player.pause() + else -> Unit + } + } + lifecycleOwner.lifecycle.addObserver(observer) + + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + player.release() + } + } + + AndroidView( + factory = { ctx -> + PlayerView(ctx).apply { + layoutParams = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + useController = false + resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM + } + }, + update = { playerView -> + if (playerView.player != player) { + playerView.player = player + } + }, + modifier = modifier.fillMaxSize() + ) +}