simplify top buttons, move to overflow
add skip buttons add gradient overlays and double-tap gesture support
This commit is contained in:
+57
-10
@@ -21,8 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.service.playback.composable
|
package com.vitorpamplona.amethyst.service.playback.composable
|
||||||
|
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
@@ -31,16 +30,22 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Size
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.ui.compose.ContentFrame
|
import androidx.media3.ui.compose.ContentFrame
|
||||||
import androidx.media3.ui.compose.SURFACE_TYPE_TEXTURE_VIEW
|
import androidx.media3.ui.compose.SURFACE_TYPE_TEXTURE_VIEW
|
||||||
|
import com.vitorpamplona.amethyst.service.playback.composable.controls.BottomGradientOverlay
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.controls.RenderAnimatedBottomInfo
|
import com.vitorpamplona.amethyst.service.playback.composable.controls.RenderAnimatedBottomInfo
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.controls.RenderCenterButtons
|
import com.vitorpamplona.amethyst.service.playback.composable.controls.RenderCenterButtons
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.controls.RenderTopButtons
|
import com.vitorpamplona.amethyst.service.playback.composable.controls.RenderTopButtons
|
||||||
|
import com.vitorpamplona.amethyst.service.playback.composable.controls.TopGradientOverlay
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.LoadedMediaItem
|
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.LoadedMediaItem
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.wavefront.Waveform
|
import com.vitorpamplona.amethyst.service.playback.composable.wavefront.Waveform
|
||||||
|
import com.vitorpamplona.amethyst.service.playback.diskCache.isLiveStreaming
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
|
||||||
private fun getVideoSizeDp(player: Player): Size? {
|
private fun getVideoSizeDp(player: Player): Size? {
|
||||||
@@ -71,21 +76,57 @@ fun RenderVideoPlayer(
|
|||||||
controllerVisible: MutableState<Boolean> = remember { mutableStateOf(false) },
|
controllerVisible: MutableState<Boolean> = remember { mutableStateOf(false) },
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
) {
|
) {
|
||||||
Box(modifier = borderModifier) {
|
val containerSize = remember { mutableStateOf(IntSize.Zero) }
|
||||||
|
val isLive = isLiveStreaming(mediaItem.src.videoUri)
|
||||||
|
val skipSeconds = if (controllerState.controller.duration in 1..30000) 5 else 10
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
borderModifier
|
||||||
|
.onSizeChanged { containerSize.value = it }
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectTapGestures(
|
||||||
|
onTap = { controllerVisible.value = !controllerVisible.value },
|
||||||
|
onDoubleTap = { offset ->
|
||||||
|
if (!isLive) {
|
||||||
|
val isLeftSide = offset.x < containerSize.value.width / 2
|
||||||
|
if (isLeftSide) {
|
||||||
|
val newPosition =
|
||||||
|
(controllerState.controller.currentPosition - skipSeconds * 1000)
|
||||||
|
.coerceAtLeast(0)
|
||||||
|
controllerState.controller.seekTo(newPosition)
|
||||||
|
} else {
|
||||||
|
val duration = controllerState.controller.duration
|
||||||
|
val newPosition =
|
||||||
|
(controllerState.controller.currentPosition + skipSeconds * 1000)
|
||||||
|
.coerceAtMost(duration)
|
||||||
|
controllerState.controller.seekTo(newPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
ContentFrame(
|
ContentFrame(
|
||||||
player = controllerState.controller,
|
player = controllerState.controller,
|
||||||
modifier =
|
modifier = videoModifier,
|
||||||
videoModifier.clickable(
|
surfaceType = SURFACE_TYPE_TEXTURE_VIEW,
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
|
||||||
indication = null, // to prevent the ripple from the tap
|
|
||||||
) { controllerVisible.value = !controllerVisible.value },
|
|
||||||
surfaceType = SURFACE_TYPE_TEXTURE_VIEW, // texture view is better inside lazy layouts.
|
|
||||||
contentScale = contentScale,
|
contentScale = contentScale,
|
||||||
)
|
)
|
||||||
|
|
||||||
mediaItem.src.waveformData?.let { Waveform(it, controllerState, Modifier.align(Alignment.Center)) }
|
mediaItem.src.waveformData?.let { Waveform(it, controllerState, Modifier.align(Alignment.Center)) }
|
||||||
|
|
||||||
if (showControls) {
|
if (showControls) {
|
||||||
|
TopGradientOverlay(
|
||||||
|
controllerVisible = controllerVisible,
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
|
)
|
||||||
|
|
||||||
|
BottomGradientOverlay(
|
||||||
|
controllerVisible = controllerVisible,
|
||||||
|
modifier = Modifier.align(Alignment.BottomCenter),
|
||||||
|
)
|
||||||
|
|
||||||
RenderTopButtons(
|
RenderTopButtons(
|
||||||
mediaData = mediaItem.src,
|
mediaData = mediaItem.src,
|
||||||
controllerState = controllerState,
|
controllerState = controllerState,
|
||||||
@@ -95,7 +136,13 @@ fun RenderVideoPlayer(
|
|||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
RenderCenterButtons(controllerState, controllerVisible, Modifier.align(Alignment.Center))
|
RenderCenterButtons(
|
||||||
|
controllerState = controllerState,
|
||||||
|
controllerVisible = controllerVisible,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
isLiveStream = isLive,
|
||||||
|
videoDurationMs = controllerState.controller.duration,
|
||||||
|
)
|
||||||
|
|
||||||
RenderAnimatedBottomInfo(controllerState, controllerVisible, Modifier.align(Alignment.BottomCenter))
|
RenderAnimatedBottomInfo(controllerState, controllerVisible, Modifier.align(Alignment.BottomCenter))
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -98,10 +98,10 @@ private fun HorizontalLinearProgressIndicator(
|
|||||||
onLayoutWidthChanged: (Int) -> Unit = {},
|
onLayoutWidthChanged: (Int) -> Unit = {},
|
||||||
onSeek: (Float) -> Unit,
|
onSeek: (Float) -> Unit,
|
||||||
playedColor: Color = Color.White,
|
playedColor: Color = Color.White,
|
||||||
bufferedColor: Color = Color.LightGray,
|
bufferedColor: Color = Color.White.copy(alpha = 0.5f),
|
||||||
unplayedColor: Color = Color.DarkGray,
|
unplayedColor: Color = Color.White.copy(alpha = 0.3f),
|
||||||
scrubberColor: Color = playedColor,
|
scrubberColor: Color = playedColor,
|
||||||
rectHeightDp: Dp = 3.dp,
|
rectHeightDp: Dp = 4.dp,
|
||||||
) {
|
) {
|
||||||
Canvas(
|
Canvas(
|
||||||
Modifier
|
Modifier
|
||||||
@@ -125,7 +125,7 @@ private fun HorizontalLinearProgressIndicator(
|
|||||||
|
|
||||||
drawCircle(
|
drawCircle(
|
||||||
color = scrubberColor,
|
color = scrubberColor,
|
||||||
radius = size.height * 1.5f,
|
radius = size.height * 2f,
|
||||||
center = Offset(x = positionX, y = size.height / 2.0f),
|
center = Offset(x = positionX, y = size.height / 2.0f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
-26
@@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Fullscreen
|
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.PictureInPicture
|
import androidx.compose.material.icons.filled.PictureInPicture
|
||||||
import androidx.compose.material.icons.filled.SaveAlt
|
import androidx.compose.material.icons.filled.SaveAlt
|
||||||
@@ -64,11 +63,9 @@ fun OverflowMenuButtonPreview() {
|
|||||||
OverflowMenuButton(
|
OverflowMenuButton(
|
||||||
showShare = true,
|
showShare = true,
|
||||||
showSave = true,
|
showSave = true,
|
||||||
showFullscreen = true,
|
|
||||||
showPip = true,
|
showPip = true,
|
||||||
onShareClick = {},
|
onShareClick = {},
|
||||||
onSaveClick = {},
|
onSaveClick = {},
|
||||||
onFullscreenClick = {},
|
|
||||||
onPipClick = {},
|
onPipClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -81,11 +78,9 @@ fun AnimatedOverflowMenuButton(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
showShare: Boolean = true,
|
showShare: Boolean = true,
|
||||||
showSave: Boolean = true,
|
showSave: Boolean = true,
|
||||||
showFullscreen: Boolean = false,
|
|
||||||
showPip: Boolean = false,
|
showPip: Boolean = false,
|
||||||
onShareClick: () -> Unit,
|
onShareClick: () -> Unit,
|
||||||
onSaveClick: () -> Unit,
|
onSaveClick: () -> Unit,
|
||||||
onFullscreenClick: () -> Unit,
|
|
||||||
onPipClick: () -> Unit,
|
onPipClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
@@ -97,11 +92,9 @@ fun AnimatedOverflowMenuButton(
|
|||||||
OverflowMenuButton(
|
OverflowMenuButton(
|
||||||
showShare = showShare,
|
showShare = showShare,
|
||||||
showSave = showSave,
|
showSave = showSave,
|
||||||
showFullscreen = showFullscreen,
|
|
||||||
showPip = showPip,
|
showPip = showPip,
|
||||||
onShareClick = onShareClick,
|
onShareClick = onShareClick,
|
||||||
onSaveClick = onSaveClick,
|
onSaveClick = onSaveClick,
|
||||||
onFullscreenClick = onFullscreenClick,
|
|
||||||
onPipClick = onPipClick,
|
onPipClick = onPipClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -111,11 +104,9 @@ fun AnimatedOverflowMenuButton(
|
|||||||
fun OverflowMenuButton(
|
fun OverflowMenuButton(
|
||||||
showShare: Boolean,
|
showShare: Boolean,
|
||||||
showSave: Boolean,
|
showSave: Boolean,
|
||||||
showFullscreen: Boolean,
|
|
||||||
showPip: Boolean,
|
showPip: Boolean,
|
||||||
onShareClick: () -> Unit,
|
onShareClick: () -> Unit,
|
||||||
onSaveClick: () -> Unit,
|
onSaveClick: () -> Unit,
|
||||||
onFullscreenClick: () -> Unit,
|
|
||||||
onPipClick: () -> Unit,
|
onPipClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val menuExpanded = remember { mutableStateOf(false) }
|
val menuExpanded = remember { mutableStateOf(false) }
|
||||||
@@ -180,23 +171,6 @@ fun OverflowMenuButton(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showFullscreen) {
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(stringRes(R.string.fullscreen), color = Color.White) },
|
|
||||||
onClick = {
|
|
||||||
menuExpanded.value = false
|
|
||||||
onFullscreenClick()
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Fullscreen,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = Color.White,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showPip) {
|
if (showPip) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringRes(R.string.picture_in_picture), color = Color.White) },
|
text = { Text(stringRes(R.string.picture_in_picture), color = Color.White) },
|
||||||
|
|||||||
+39
-3
@@ -21,10 +21,14 @@
|
|||||||
package com.vitorpamplona.amethyst.service.playback.composable.controls
|
package com.vitorpamplona.amethyst.service.playback.composable.controls
|
||||||
|
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.ui.compose.state.rememberPlayPauseButtonState
|
import androidx.media3.ui.compose.state.rememberPlayPauseButtonState
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.MediaControllerState
|
import com.vitorpamplona.amethyst.service.playback.composable.MediaControllerState
|
||||||
@@ -36,16 +40,48 @@ fun RenderCenterButtons(
|
|||||||
controllerState: MediaControllerState,
|
controllerState: MediaControllerState,
|
||||||
controllerVisible: MutableState<Boolean>,
|
controllerVisible: MutableState<Boolean>,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
|
isLiveStream: Boolean = false,
|
||||||
|
videoDurationMs: Long = 0L,
|
||||||
) {
|
) {
|
||||||
val state = rememberPlayPauseButtonState(controllerState.controller)
|
val state = rememberPlayPauseButtonState(controllerState.controller)
|
||||||
|
val skipSeconds = if (videoDurationMs in 1..30000) 5 else 10
|
||||||
|
|
||||||
AnimatedPlayPauseButton(controllerVisible, modifier, !state.showPlay) {
|
Row(
|
||||||
state.onClick()
|
modifier = modifier,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(32.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
if (!isLiveStream) {
|
||||||
|
AnimatedSkipButton(
|
||||||
|
controllerVisible = controllerVisible,
|
||||||
|
isForward = false,
|
||||||
|
skipSeconds = skipSeconds,
|
||||||
|
) {
|
||||||
|
val newPosition = (controllerState.controller.currentPosition - skipSeconds * 1000).coerceAtLeast(0)
|
||||||
|
controllerState.controller.seekTo(newPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedPlayPauseButton(controllerVisible, Modifier, !state.showPlay) {
|
||||||
|
state.onClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLiveStream) {
|
||||||
|
AnimatedSkipButton(
|
||||||
|
controllerVisible = controllerVisible,
|
||||||
|
isForward = true,
|
||||||
|
skipSeconds = skipSeconds,
|
||||||
|
) {
|
||||||
|
val duration = controllerState.controller.duration
|
||||||
|
val newPosition = (controllerState.controller.currentPosition + skipSeconds * 1000).coerceAtMost(duration)
|
||||||
|
controllerState.controller.seekTo(newPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.showPlay) {
|
if (!state.showPlay) {
|
||||||
LaunchedEffect(state.showPlay) {
|
LaunchedEffect(state.showPlay) {
|
||||||
delay(2000)
|
delay(3000)
|
||||||
controllerVisible.value = false
|
controllerVisible.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-47
@@ -20,6 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.service.playback.composable.controls
|
package com.vitorpamplona.amethyst.service.playback.composable.controls
|
||||||
|
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -52,9 +54,11 @@ fun RenderTopButtonsPreview() {
|
|||||||
mediaData = MediaItemData("http://test.mp4"),
|
mediaData = MediaItemData("http://test.mp4"),
|
||||||
controllerVisible = remember { mutableStateOf(true) },
|
controllerVisible = remember { mutableStateOf(true) },
|
||||||
startingMuteState = false,
|
startingMuteState = false,
|
||||||
onMuteClick = { },
|
isLive = false,
|
||||||
onPictureInPictureClick = { },
|
pipSupported = true,
|
||||||
onZoomClick = { },
|
onMuteClick = {},
|
||||||
|
onPictureInPictureClick = {},
|
||||||
|
onZoomClick = {},
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
accountViewModel = mockAccountViewModel(),
|
accountViewModel = mockAccountViewModel(),
|
||||||
)
|
)
|
||||||
@@ -71,20 +75,30 @@ fun RenderTopButtons(
|
|||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current.getActivity()
|
val context = LocalContext.current
|
||||||
|
val isLive = isLiveStreaming(mediaData.videoUri)
|
||||||
|
val pipSupported =
|
||||||
|
remember {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RenderTopButtons(
|
RenderTopButtons(
|
||||||
mediaData = mediaData,
|
mediaData = mediaData,
|
||||||
controllerVisible = controllerVisible,
|
controllerVisible = controllerVisible,
|
||||||
startingMuteState = controllerState.controller.volume < 0.001,
|
startingMuteState = controllerState.controller.volume < 0.001,
|
||||||
|
isLive = isLive,
|
||||||
|
pipSupported = pipSupported,
|
||||||
onMuteClick = { mute ->
|
onMuteClick = { mute ->
|
||||||
// makes the new setting the default for new creations.
|
|
||||||
DEFAULT_MUTED_SETTING.value = mute
|
DEFAULT_MUTED_SETTING.value = mute
|
||||||
controllerState.controller.volume = if (mute) 0f else 1f
|
controllerState.controller.volume = if (mute) 0f else 1f
|
||||||
},
|
},
|
||||||
onPictureInPictureClick = {
|
onPictureInPictureClick = {
|
||||||
controllerState.controller.pause()
|
controllerState.controller.pause()
|
||||||
PipVideoActivity.callIn(mediaData, controllerState.visibility.bounds, context)
|
PipVideoActivity.callIn(mediaData, controllerState.visibility.bounds, context.getActivity())
|
||||||
},
|
},
|
||||||
onZoomClick =
|
onZoomClick =
|
||||||
onZoomClick?.let {
|
onZoomClick?.let {
|
||||||
@@ -103,49 +117,18 @@ fun RenderTopButtons(
|
|||||||
mediaData: MediaItemData,
|
mediaData: MediaItemData,
|
||||||
controllerVisible: MutableState<Boolean>,
|
controllerVisible: MutableState<Boolean>,
|
||||||
startingMuteState: Boolean,
|
startingMuteState: Boolean,
|
||||||
|
isLive: Boolean,
|
||||||
|
pipSupported: Boolean,
|
||||||
onMuteClick: (Boolean) -> Unit,
|
onMuteClick: (Boolean) -> Unit,
|
||||||
onPictureInPictureClick: () -> Unit,
|
onPictureInPictureClick: () -> Unit,
|
||||||
onZoomClick: (() -> Unit)?,
|
onZoomClick: (() -> Unit)?,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val shareDialogVisible = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Row(modifier) {
|
Row(modifier) {
|
||||||
if (!isLiveStreaming(mediaData.videoUri)) {
|
|
||||||
AnimatedShareButton(controllerVisible) { popupExpanded, toggle ->
|
|
||||||
ShareMediaAction(
|
|
||||||
popupExpanded = popupExpanded,
|
|
||||||
videoUri = mediaData.videoUri,
|
|
||||||
postNostrUri = mediaData.callbackUri,
|
|
||||||
blurhash = null,
|
|
||||||
dim = null,
|
|
||||||
hash = null,
|
|
||||||
mimeType = mediaData.mimeType,
|
|
||||||
onDismiss = toggle,
|
|
||||||
content = MediaUrlVideo(url = mediaData.videoUri, mimeType = mediaData.mimeType, artworkUri = mediaData.artworkUri, authorName = mediaData.authorName, description = mediaData.title, uri = mediaData.callbackUri),
|
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedSaveButton(controllerVisible) { context ->
|
|
||||||
accountViewModel.saveMediaToGallery(mediaData.videoUri, mediaData.mimeType, context)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AnimatedShareButton(controllerVisible) { popupExpanded, toggle ->
|
|
||||||
ShareMediaAction(
|
|
||||||
popupExpanded = popupExpanded,
|
|
||||||
videoUri = mediaData.videoUri,
|
|
||||||
postNostrUri = mediaData.callbackUri,
|
|
||||||
blurhash = null,
|
|
||||||
dim = null,
|
|
||||||
hash = null,
|
|
||||||
mimeType = mediaData.mimeType,
|
|
||||||
onDismiss = toggle,
|
|
||||||
content = MediaUrlVideo(url = mediaData.videoUri, mimeType = mediaData.mimeType, artworkUri = mediaData.artworkUri, authorName = mediaData.authorName, description = mediaData.title, uri = mediaData.callbackUri),
|
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onZoomClick != null) {
|
if (onZoomClick != null) {
|
||||||
FullScreenButton(
|
FullScreenButton(
|
||||||
controllerVisible = controllerVisible,
|
controllerVisible = controllerVisible,
|
||||||
@@ -153,15 +136,43 @@ fun RenderTopButtons(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PictureInPictureButton(
|
|
||||||
controllerVisible = controllerVisible,
|
|
||||||
onClick = onPictureInPictureClick,
|
|
||||||
)
|
|
||||||
|
|
||||||
MuteButton(
|
MuteButton(
|
||||||
controllerVisible = controllerVisible,
|
controllerVisible = controllerVisible,
|
||||||
startingMuteState = startingMuteState,
|
startingMuteState = startingMuteState,
|
||||||
toggle = onMuteClick,
|
toggle = onMuteClick,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AnimatedOverflowMenuButton(
|
||||||
|
controllerVisible = controllerVisible,
|
||||||
|
showShare = true,
|
||||||
|
showSave = !isLive,
|
||||||
|
showPip = pipSupported,
|
||||||
|
onShareClick = { shareDialogVisible.value = true },
|
||||||
|
onSaveClick = {
|
||||||
|
accountViewModel.saveMediaToGallery(mediaData.videoUri, mediaData.mimeType, context)
|
||||||
|
},
|
||||||
|
onPipClick = onPictureInPictureClick,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShareMediaAction(
|
||||||
|
popupExpanded = shareDialogVisible,
|
||||||
|
videoUri = mediaData.videoUri,
|
||||||
|
postNostrUri = mediaData.callbackUri,
|
||||||
|
blurhash = null,
|
||||||
|
dim = null,
|
||||||
|
hash = null,
|
||||||
|
mimeType = mediaData.mimeType,
|
||||||
|
onDismiss = { shareDialogVisible.value = false },
|
||||||
|
content =
|
||||||
|
MediaUrlVideo(
|
||||||
|
url = mediaData.videoUri,
|
||||||
|
mimeType = mediaData.mimeType,
|
||||||
|
artworkUri = mediaData.artworkUri,
|
||||||
|
authorName = mediaData.authorName,
|
||||||
|
description = mediaData.title,
|
||||||
|
uri = mediaData.callbackUri,
|
||||||
|
),
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -903,7 +903,6 @@
|
|||||||
<string name="mute_button">Sound on. Click to mute</string>
|
<string name="mute_button">Sound on. Click to mute</string>
|
||||||
<string name="skip_back">Skip back %d seconds</string>
|
<string name="skip_back">Skip back %d seconds</string>
|
||||||
<string name="skip_forward">Skip forward %d seconds</string>
|
<string name="skip_forward">Skip forward %d seconds</string>
|
||||||
<string name="fullscreen">Fullscreen</string>
|
|
||||||
<string name="picture_in_picture">Picture-in-Picture</string>
|
<string name="picture_in_picture">Picture-in-Picture</string>
|
||||||
<string name="live">LIVE</string>
|
<string name="live">LIVE</string>
|
||||||
<string name="tap_to_retry">Tap to retry</string>
|
<string name="tap_to_retry">Tap to retry</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user