Назад к задачам
Junior — Middle+
6

Обзор кода экрана видеоплеера, построенного с помощью Jetpack Compose

Компании, где спрашивали:

Salmon
Получайте помощь с лайвкодингом в реальном времени с Sobes Copilot
Условие задачи

Необходимо выполнить ревью реализации экрана видеоплеера, написанного на Jetpack Compose.

sealed interface VideoPlayerState {
    data object Empty : VideoPlayerState
    data object Loading : VideoPlayerState
    data class Content(
        val currentVideoUrl: String,
        val videoList: List<Video>
    ) : VideoPlayerState }
@Composable
fun VideoPlayerScreen(
    category: String,
    viewModel: VideoPlayerViewModel = hiltViewModel()
) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    val currentState = uiState
    val context = LocalContext.current
    val isPreview = LocalInspectionMode.current
    LaunchedEffect(Unit) {
        viewModel.getVideo(category) }
    if (isPreview) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .aspectRatio(3f / 4f),
            contentAlignment = Alignment.Center
        ) {
            Text("ExoPlayer Placeholder", color = Color.White) }
    } else {
        when (currentState) {
            is VideoPlayerState.Content -> {
                Column(
                    modifier = Modifier.fillMaxSize(),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    ExoPlayer(url = currentState.currentVideoUrl)
                    BasicButton(
                        modifier = Modifier
                            .padding(50.dp)
                            .align(Alignment.CenterHorizontally),
                        onClick = { },
                        text = "Continue" )
                    BasicButton(
                        modifier = Modifier
                            .padding(50.dp)
                            .align(Alignment.CenterHorizontally),
                        onClick = {
                            val fileName = "video_${System.currentTimeMillis()}.mp4"
                            saveVideoToStorage(context, currentState.currentVideoUrl, fileName)
                        },
                        text = "Save Video"
                    )
                    NavItem(
                        videoList = currentState.videoList,
                        onItemSelected = { viewModel.selectVideo(it) }
                    ) } }
            is VideoPlayerState.Loading -> {
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center
                ) {
                    CircularProgressIndicator() } }
            is VideoPlayerState.Empty -> Unit
} } }
@OptIn(UnstableApi::class)
@Composable
fun ExoPlayer(
    url: String,
    modifier: Modifier = Modifier,
    viewModel: PlayerViewModel = hiltViewModel(),
    listener: Player.Listener? = null
) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val exoPlayer by viewModel.playerFlow.collectAsState()
    if (listener != null) {
        exoPlayer!!.addListener(listener)
    }
    LaunchedEffect(url) {
        viewModel.setVideoUrl(url)
    }
    DisposableEffect(lifecycleOwner) {
        val lifecycleObserver = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_PAUSE -> viewModel.savePosition()
                Lifecycle.Event.ON_RESUME -> exoPlayer?.seekTo(viewModel.currentPosition)
                else -> Unit
            }
        }
        lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
        }
    }
    AndroidView(
        factory = {
            PlayerView(context).apply {
                resizeMode = RESIZE_MODE_ZOOM
                player = exoPlayer
            }
        },
        update = { it.player = exoPlayer },
        modifier = modifier.aspectRatio(16f / 9f)
    )
}
suspend fun saveVideoToStorage(
    context: Context,
    videoUrl: String,
    fileName: String
): Uri? {
    // TODO: реализация сохранения в External Storage
    return null
}