Thorium-android-app/app/src/main/java/net/schueller/peertube/feature_video/presentation/video/components/videoPlay/VideoPlayScreen.kt

385 lines
16 KiB
Kotlin

package net.schueller.peertube.feature_video.presentation.video.components.videoPlay
import android.content.res.Configuration
import android.widget.Toast
import androidx.compose.animation.*
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.*
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemsIndexed
import coil.annotation.ExperimentalCoilApi
import com.google.accompanist.systemuicontroller.SystemUiController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.flow.collectLatest
import net.schueller.peertube.feature_video.domain.model.Video
import net.schueller.peertube.feature_video.presentation.video.VideoPlayViewModel
import net.schueller.peertube.feature_video.presentation.video.components.VideoListItem
import net.schueller.peertube.feature_video.presentation.video.events.VideoPlayEvent
import net.schueller.peertube.feature_video.presentation.video.player.ExoPlayerHolder
@OptIn(ExperimentalMotionApi::class)
@ExperimentalCoilApi
@ExperimentalMaterialApi
@Composable
fun VideoPlayScreen(
exoPlayerHolder: ExoPlayerHolder,
videoPlayViewModel: VideoPlayViewModel = hiltViewModel()
) {
val state = videoPlayViewModel.state.value
val context = LocalContext.current
var descriptionVisible by remember { mutableStateOf(false) }
var moreVisible by remember { mutableStateOf(false) }
// Show toasts
LaunchedEffect(key1 = true) {
videoPlayViewModel.eventFlow.collectLatest { event ->
when(event) {
is VideoPlayViewModel.UiEvent.ShowToast -> {
Toast.makeText(
context,
event.message,
event.length
).show()
}
is VideoPlayViewModel.UiEvent.ShowDescription -> {
descriptionVisible = true
}
is VideoPlayViewModel.UiEvent.HideDescription -> {
descriptionVisible = false
}
is VideoPlayViewModel.UiEvent.ShowMore -> {
moreVisible = true
}
is VideoPlayViewModel.UiEvent.HideMore -> {
moreVisible = false
}
}
}
}
// Related Videos
val lazyRelatedVideoItems: LazyPagingItems<Video> = videoPlayViewModel.relatedVideos.collectAsLazyPagingItems()
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
) {
val configuration = LocalConfiguration.current
var animateToEnd by remember { mutableStateOf(false) }
val progress by animateFloatAsState(
targetValue = if (animateToEnd) 1f else 0f,
animationSpec = tween(250)
)
state.video?.let { video ->
val systemUiController: SystemUiController = rememberSystemUiController()
when(configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
// Full screen
// TODO: bottom min bar show on click of screen (Asus Zenfone / Pixel 4)
systemUiController.isStatusBarVisible = false
systemUiController.isNavigationBarVisible = false
systemUiController.isSystemBarsVisible = false
VideoScreen(exoPlayerHolder, video, Modifier)
} else -> {
systemUiController.isStatusBarVisible = true
systemUiController.isNavigationBarVisible = true
systemUiController.isSystemBarsVisible = true
Column(
modifier = Modifier.background(Color.Transparent).fillMaxSize()
) {
MotionLayout(
// Large
start = ConstraintSet(
""" {
background: {
width: "spread",
height: 400,
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
top: ['parent', 'top', 0]
},
v1: {
width: "spread",
height: 250,
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
top: ['parent', 'top', 0]
},
title: {
width: 0,
height: 0,
start: ['parent', 'start', 16],
top: ['v1', 'bottom', 16],
end: ['parent', 'end', 16],
custom: {
textSize: 20
}
},
description: {
height: 0,
width: 0,
start: ['parent', 'start', 16],
top: ['title', 'bottom', 8],
end: ['parent', 'end', 16],
custom: {
textSize: 16
}
},
list: {
width: "spread",
height: 500,
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
top: ['description', 'bottom', 0]
},
play: {
start: ['parent', 'end', 8],
top: ['v1', 'top', 0],
bottom: ['v1', 'bottom', 0]
},
close: {
start: ['parent', 'end', 8],
top: ['v1', 'top', 0],
bottom: ['v1', 'bottom', 0]
}
} """
),
// small
end = ConstraintSet(
""" {
background: {
width: "spread",
height: 60,
start: ['parent', 'start', 0],
bottom: ['parent', 'bottom', 56],
end: ['parent', 'end', 0]
},
v1: {
width: 100,
height: 60,
start: ['parent', 'start', 0],
bottom: ['parent', 'bottom', 56]
},
title: {
width: "spread",
start: ['v1', 'end', 8],
top: ['v1', 'top', 8],
end: ['parent', 'end', 8],
custom: {
textSize: 16
}
},
description: {
start: ['v1', 'end', 8],
top: ['title', 'bottom', 0],
custom: {
textSize: 14
}
},
list: {
width: "spread",
height: 0,
start: ['parent', 'start', 0],
end: ['parent', 'end', 0],
top: ['parent', 'bottom', 0]
},
play: {
end: ['close', 'start', 8],
top: ['v1', 'top', 0],
bottom: ['v1', 'bottom', 0]
},
close: {
end: ['parent', 'end', 24],
top: ['v1', 'top', 0],
bottom: ['v1', 'bottom', 0]
}
} """
),
progress = progress,
modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
) {
Box(
modifier = Modifier
.layoutId("background", "box")
.background(Color.Blue)
.clickable(onClick = { animateToEnd = !animateToEnd })
)
VideoScreen(exoPlayerHolder, video,
modifier = Modifier
.layoutId("v1", "box")
.background(Color.Black)
)
Text(
text = "MotionLayout in Compose",
modifier = Modifier.layoutId("title")
.clickable(onClick = { animateToEnd = !animateToEnd }),
color = MaterialTheme.colors.onBackground,
fontSize = motionProperties("title").value.fontSize("textSize")
)
Text(
text = "Demo screen 17",
modifier = Modifier.layoutId("description"),
color = MaterialTheme.colors.onBackground,
fontSize = motionProperties("description").value.fontSize("textSize")
)
Box(
modifier = Modifier
.layoutId("list", "box")
.background(MaterialTheme.colors.background)
) {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
VideoMeta(video)
}
itemsIndexed(lazyRelatedVideoItems) { _, video ->
if (video != null) {
VideoListItem(
video = video,
onItemClick = {
videoPlayViewModel.onEvent(VideoPlayEvent.PlayVideo(video))
}
)
}
}
}
Column(Modifier.fillMaxSize()) {
AnimatedVisibility(
visible = descriptionVisible,
modifier = Modifier.fillMaxSize(),
enter = slideInVertically(
initialOffsetY = { it }, // it == fullWidth
animationSpec = tween(
durationMillis = 150,
easing = LinearEasing
)
),
exit = slideOutVertically(
targetOffsetY = { it },
animationSpec = tween(
durationMillis = 150,
easing = LinearEasing
)
)
) {
VideoDescriptionScreen()
}
}
}
Icon(
Icons.Filled.PlayArrow,
contentDescription = "Play",
tint = MaterialTheme.colors.onBackground,
modifier = Modifier.layoutId("play")
)
Icon(
Icons.Filled.Close,
contentDescription = "Close",
tint = MaterialTheme.colors.onBackground,
modifier = Modifier.layoutId("close").clickable(
onClick = {
videoPlayViewModel.onEvent(VideoPlayEvent.CloseVideo)
}
)
)
}
}
}
}
Column(
Modifier
.fillMaxSize()
.background(Color.Transparent)
) {
AnimatedVisibility(
visible = moreVisible,
modifier = Modifier.fillMaxSize(),
enter = slideInVertically(
initialOffsetY = { it }, // it == fullWidth
animationSpec = tween(
durationMillis = 150,
easing = LinearEasing
)
),
exit = slideOutVertically(
targetOffsetY = { it },
animationSpec = tween(
durationMillis = 150,
easing = LinearEasing
)
)
) {
VideoMoreScreen()
}
}
}
if(state.error.isNotBlank()) {
Text(
text = state.error,
color = MaterialTheme.colors.error,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.align(Alignment.Center)
)
}
if(state.isLoading) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
// BackHandler(enabled = true) {
// Log.v("back", "back pressed")
//
// enterPIPMode(activity)
// }
}