From 7829595ae55fa5862864744bd5f3536687781a4e Mon Sep 17 00:00:00 2001 From: DatLag Date: Sun, 17 Dec 2023 16:25:22 +0100 Subject: [PATCH] fix no tv start option, fix crash on tv when clicking cast button, added volume slider on desktop --- .../src/androidMain/AndroidManifest.xml | 4 +- .../burningseries/shared/App.android.kt | 2 +- .../ui/screen/video/VideoScreen.android.kt | 8 +++ .../video/dialog/cast/CastDialog.android.kt | 2 +- .../dev/datlag/burningseries/shared/App.kt | 2 +- .../shared/common/ExtendCompose.kt | 68 +++++++++---------- .../shared/ui/screen/initial/InitialScreen.kt | 4 +- .../screen/initial/favorite/FavoriteScreen.kt | 4 +- .../ui/screen/initial/home/HomeScreen.kt | 7 +- .../ui/screen/initial/search/SearchScreen.kt | 4 +- .../commonMain/resources/MR/base/strings.xml | 2 + .../commonMain/resources/MR/de/strings.xml | 4 ++ .../burningseries/shared/App.desktop.kt | 6 +- .../shared/ui/screen/video/MediaPlayer.kt | 6 ++ .../shared/ui/screen/video/VideoControls.kt | 38 +++++++++-- .../shared/ui/screen/video/VideoPlayer.kt | 42 ++++++++++++ 16 files changed, 147 insertions(+), 56 deletions(-) diff --git a/app/android/src/androidMain/AndroidManifest.xml b/app/android/src/androidMain/AndroidManifest.xml index 90c7efd5..d899b2f3 100644 --- a/app/android/src/androidMain/AndroidManifest.xml +++ b/app/android/src/androidMain/AndroidManifest.xml @@ -17,6 +17,7 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" + android:banner="@mipmap/tv_banner" android:supportsRtl="true" android:theme="@style/SplashScreenTheme" android:name=".App" @@ -40,6 +41,7 @@ + @@ -49,7 +51,7 @@ + android:exported="true" /> \ No newline at end of file diff --git a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/App.android.kt b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/App.android.kt index c4420bf1..b4813bae 100644 --- a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/App.android.kt +++ b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/App.android.kt @@ -11,7 +11,7 @@ actual fun SystemProvider(content: @Composable () -> Unit) { } @Composable -actual fun isTv(): Boolean { +actual fun rememberIsTv(): Boolean { val context = LocalContext.current return remember { context.isTv() } } \ No newline at end of file diff --git a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoScreen.android.kt b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoScreen.android.kt index 0b9af95f..3db1cf61 100644 --- a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoScreen.android.kt +++ b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoScreen.android.kt @@ -48,6 +48,7 @@ import dev.datlag.burningseries.shared.common.findWindow import dev.datlag.burningseries.shared.common.lifecycle.collectAsStateWithLifecycle import dev.datlag.burningseries.shared.common.withIOContext import dev.datlag.burningseries.shared.common.withMainContext +import dev.datlag.burningseries.shared.rememberIsTv import dev.datlag.burningseries.shared.ui.* import dev.datlag.kast.ConnectionState import dev.datlag.kast.Kast @@ -324,6 +325,7 @@ actual fun VideoScreen(component: VideoComponent) { val progressColor = MaterialTheme.colorScheme.primary.toArgb() val episode by component.episode.collectAsStateWithLifecycle() + val isTv = rememberIsTv() AndroidView( modifier = Modifier.fillMaxSize().background(Color.Black), @@ -354,6 +356,12 @@ actual fun VideoScreen(component: VideoComponent) { usingPlayer.pause() component.selectCast() } + mediaRouteButton.visibility = if (isTv) { + View.GONE + } else { + View.VISIBLE + } + mediaRouteButton.isEnabled = !isTv subtitleButton.setOnClickListener { playerView.player?.pause() diff --git a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/dialog/cast/CastDialog.android.kt b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/dialog/cast/CastDialog.android.kt index 5bb4abd5..a89bfe13 100644 --- a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/dialog/cast/CastDialog.android.kt +++ b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/dialog/cast/CastDialog.android.kt @@ -52,7 +52,7 @@ actual fun CastDialog(component: CastComponent) { icon = { Icon( imageVector = Icons.Default.Cast, - contentDescription = null + contentDescription = stringResource(SharedRes.strings.casting) ) }, title = { diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/App.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/App.kt index b4b60269..3f3393fa 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/App.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/App.kt @@ -64,4 +64,4 @@ fun App( expect fun SystemProvider(content: @Composable () -> Unit) @Composable -expect fun isTv(): Boolean \ No newline at end of file +expect fun rememberIsTv(): Boolean \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/common/ExtendCompose.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/common/ExtendCompose.kt index 23f33687..8aa0e97f 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/common/ExtendCompose.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/common/ExtendCompose.kt @@ -1,10 +1,6 @@ package dev.datlag.burningseries.shared.common -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.AnimationSpec -import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.spring import androidx.compose.foundation.focusable import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.waitForUpOrCancellation @@ -16,13 +12,11 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyGridItemScope import androidx.compose.foundation.lazy.grid.LazyGridScope -import androidx.compose.material3.ColorScheme import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.vector.PathBuilder import androidx.compose.ui.input.pointer.pointerInput @@ -112,13 +106,13 @@ fun LazyListState.OnBottomReached(enabled: Boolean = true, buffer: Int = 0, bloc } } +@Composable fun Modifier.isFocused( hoverable: Boolean = true, focusable: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, builder: Modifier.() -> Modifier ): Modifier = composed { - val interactionSource = remember { MutableInteractionSource() } - val isHovered by interactionSource.collectIsHoveredAsState() val isFocused by interactionSource.collectIsFocusedAsState() @@ -133,14 +127,42 @@ fun Modifier.isFocused( ) } +@Composable +fun Modifier.onFocusChanged( + hoverable: Boolean = true, + focusable: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + onChanged: (Boolean) -> Unit +): Modifier = composed { + val isHovered by interactionSource.collectIsHoveredAsState() + val isFocused by interactionSource.collectIsFocusedAsState() + + val latestValue = remember(isHovered, isFocused) { isHovered || isFocused } + + LaunchedEffect(latestValue) { + onChanged(latestValue) + } + + this.hoverable( + interactionSource = interactionSource, + enabled = hoverable + ).focusable( + interactionSource = interactionSource, + enabled = focusable + ) +} + +@Composable fun Modifier.focusScale( scale: Float = 1.1F, hoverable: Boolean = true, - focusable: Boolean = true + focusable: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) = composed { isFocused( hoverable, - focusable + focusable, + interactionSource ) { graphicsLayer { scaleX = scale @@ -149,32 +171,6 @@ fun Modifier.focusScale( } } -@Composable -fun ColorScheme.animate(spec: AnimationSpec = spring(stiffness = Spring.StiffnessLow)): ColorScheme { - return this.copy( - primary = animateColorAsState(this.primary, spec).value, - onPrimary = animateColorAsState(this.onPrimary, spec).value, - primaryContainer = animateColorAsState(this.primaryContainer, spec).value, - onPrimaryContainer = animateColorAsState(this.onPrimaryContainer, spec).value, - secondary = animateColorAsState(this.secondary, spec).value, - onSecondary = animateColorAsState(this.onSecondary, spec).value, - secondaryContainer = animateColorAsState(this.secondaryContainer, spec).value, - onSecondaryContainer = animateColorAsState(this.onSecondaryContainer, spec).value, - tertiary = animateColorAsState(this.tertiary, spec).value, - onTertiary = animateColorAsState(this.onTertiary, spec).value, - tertiaryContainer = animateColorAsState(this.tertiaryContainer, spec).value, - onTertiaryContainer = animateColorAsState(this.onTertiaryContainer, spec).value, - background = animateColorAsState(this.background, spec).value, - onBackground = animateColorAsState(this.onBackground, spec).value, - surface = animateColorAsState(this.surface, spec).value, - onSurface = animateColorAsState(this.onSurface, spec).value, - error = animateColorAsState(this.error, spec).value, - onError = animateColorAsState(this.onError, spec).value, - errorContainer = animateColorAsState(this.errorContainer, spec).value, - onErrorContainer = animateColorAsState(this.onErrorContainer, spec).value - ) -} - fun PathBuilder.drawPathFromSvgData(data: String) { val pathCommands = data.split("(?=[a-zA-Z])".toRegex()).filterNot { it.isBlank() } val numberRegex = "[-+]?\\d*\\.?\\d+".toRegex() diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/InitialScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/InitialScreen.kt index 84f69187..de47d50f 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/InitialScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/InitialScreen.kt @@ -15,7 +15,7 @@ import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.extensions.compose.jetbrains.pages.Pages import com.moriatsushi.insetsx.ExperimentalSoftwareKeyboardApi import dev.datlag.burningseries.shared.common.lifecycle.collectAsStateWithLifecycle -import dev.datlag.burningseries.shared.isTv +import dev.datlag.burningseries.shared.rememberIsTv import dev.datlag.burningseries.shared.ui.custom.ExpandedPages import dev.icerock.moko.resources.compose.stringResource @@ -29,7 +29,7 @@ fun InitialScreen(component: InitialComponent) { WindowWidthSizeClass.Compact -> CompactScreen(component) WindowWidthSizeClass.Medium -> MediumScreen(component) WindowWidthSizeClass.Expanded -> { - if (isTv()) { + if (rememberIsTv()) { MediumScreen(component) } else { ExpandedScreen(component) diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/favorite/FavoriteScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/favorite/FavoriteScreen.kt index da897aa5..3ee7d06f 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/favorite/FavoriteScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/favorite/FavoriteScreen.kt @@ -26,7 +26,7 @@ import dev.datlag.burningseries.shared.SharedRes import dev.datlag.burningseries.shared.common.header import dev.datlag.burningseries.shared.common.lifecycle.collectAsStateWithLifecycle import dev.datlag.burningseries.shared.common.onClick -import dev.datlag.burningseries.shared.isTv +import dev.datlag.burningseries.shared.rememberIsTv import dev.datlag.burningseries.shared.ui.custom.VerticalScrollbar import dev.datlag.burningseries.shared.ui.custom.rememberScrollbarAdapter import dev.datlag.burningseries.shared.ui.screen.initial.home.component.SeriesItem @@ -37,7 +37,7 @@ import dev.icerock.moko.resources.compose.stringResource fun FavoriteScreen(component: FavoriteComponent) { when (calculateWindowSizeClass().widthSizeClass) { WindowWidthSizeClass.Expanded -> { - if (isTv()) { + if (rememberIsTv()) { DefaultView(component) } else { ExpandedView(component) diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/home/HomeScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/home/HomeScreen.kt index 14eb8624..5ba55805 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/home/HomeScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/home/HomeScreen.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.rememberLazyGridState -import androidx.compose.material.icons.Icons import androidx.compose.material3.* import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass @@ -23,8 +22,8 @@ import dev.datlag.burningseries.model.state.HomeState import dev.datlag.burningseries.shared.SharedRes import dev.datlag.burningseries.shared.common.header import dev.datlag.burningseries.shared.common.lifecycle.collectAsStateWithLifecycle -import dev.datlag.burningseries.shared.isTv import dev.datlag.burningseries.shared.other.StateSaver +import dev.datlag.burningseries.shared.rememberIsTv import dev.datlag.burningseries.shared.ui.custom.VerticalScrollbar import dev.datlag.burningseries.shared.ui.custom.rememberScrollbarAdapter import dev.datlag.burningseries.shared.ui.custom.state.ErrorState @@ -53,7 +52,7 @@ fun HomeScreen(component: HomeComponent) { is HomeState.Success -> { when (calculateWindowSizeClass().widthSizeClass) { WindowWidthSizeClass.Expanded -> { - if (isTv()) { + if (rememberIsTv()) { DefaultView(currentState.home, component) } else { ExpandedView(currentState.home, component) @@ -142,7 +141,7 @@ private fun MainView(home: Home, component: HomeComponent, modifier: Modifier = ) { Icon( imageVector = MaterialSymbols.rememberDeployedCodeAlert(), - contentDescription = null + contentDescription = stringResource(SharedRes.strings.sekret_unavailable_title) ) } } diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/search/SearchScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/search/SearchScreen.kt index 6703e9b8..c9c8e516 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/search/SearchScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/shared/ui/screen/initial/search/SearchScreen.kt @@ -30,7 +30,7 @@ import dev.datlag.burningseries.shared.SharedRes import dev.datlag.burningseries.shared.common.OnBottomReached import dev.datlag.burningseries.shared.common.lifecycle.collectAsStateWithLifecycle import dev.datlag.burningseries.shared.common.onClick -import dev.datlag.burningseries.shared.isTv +import dev.datlag.burningseries.shared.rememberIsTv import dev.datlag.burningseries.shared.ui.custom.VerticalScrollbar import dev.datlag.burningseries.shared.ui.custom.rememberScrollbarAdapter import dev.datlag.burningseries.shared.ui.custom.state.ErrorState @@ -54,7 +54,7 @@ fun SearchScreen(component: SearchComponent) { is SearchState.Success -> { when (calculateWindowSizeClass().widthSizeClass) { WindowWidthSizeClass.Expanded -> { - if (isTv()) { + if (rememberIsTv()) { DefaultView(component) } else { ExpandedView(component) diff --git a/app/shared/src/commonMain/resources/MR/base/strings.xml b/app/shared/src/commonMain/resources/MR/base/strings.xml index f5c3f17a..5c9036b5 100644 --- a/app/shared/src/commonMain/resources/MR/base/strings.xml +++ b/app/shared/src/commonMain/resources/MR/base/strings.xml @@ -70,4 +70,6 @@ GitHub Sekret unavailable Some features do not work currently.\nYou should restart the app and check if this issue persists. + Muted + Unmuted \ No newline at end of file diff --git a/app/shared/src/commonMain/resources/MR/de/strings.xml b/app/shared/src/commonMain/resources/MR/de/strings.xml index cdc2a6c2..53855b54 100644 --- a/app/shared/src/commonMain/resources/MR/de/strings.xml +++ b/app/shared/src/commonMain/resources/MR/de/strings.xml @@ -68,4 +68,8 @@ Neue Version verfügbar Eine neue Version ist verfügbar "%s".\nDas solltest du dir ansehen! GitHub + Sekret nicht verfügbar + Einige Funktionen sind momentan nicht verfügbar.\nDu solltest die App neu starten und prüfen, ob das Problem bestehen bleibt. + Stumm + Nicht stumm \ No newline at end of file diff --git a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/App.desktop.kt b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/App.desktop.kt index 3cfffeed..e6a7dff6 100644 --- a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/App.desktop.kt +++ b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/App.desktop.kt @@ -5,7 +5,9 @@ import androidx.compose.foundation.LocalContextMenuRepresentation import androidx.compose.foundation.text.LocalTextContextMenu import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.awt.ComposeWindow import com.dzirbel.contextmenu.ContextMenuColors @@ -35,6 +37,6 @@ fun ContextMenuColors(scheme: ColorScheme = MaterialTheme.colorScheme) = Context ) @Composable -actual fun isTv(): Boolean { +actual fun rememberIsTv(): Boolean { return false } \ No newline at end of file diff --git a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/MediaPlayer.kt b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/MediaPlayer.kt index 430ea852..aa8b4d5b 100644 --- a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/MediaPlayer.kt +++ b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/MediaPlayer.kt @@ -1,5 +1,6 @@ package dev.datlag.burningseries.shared.ui.screen.video +import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.MutableLongState import androidx.compose.runtime.MutableState @@ -7,9 +8,14 @@ interface MediaPlayer { val isPlaying: MutableState val time: MutableLongState val length: MutableLongState + val isMuted: MutableState + val volume: MutableFloatState fun play() fun pause() fun rewind() fun forward() fun seekTo(millis: Long) + fun mute() + fun unmute() + fun setVolume(volume: Float) } \ No newline at end of file diff --git a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoControls.kt b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoControls.kt index a7d87e39..c8aec845 100644 --- a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoControls.kt +++ b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoControls.kt @@ -1,10 +1,7 @@ package dev.datlag.burningseries.shared.ui.screen.video import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* @@ -137,6 +134,39 @@ fun VideoControls( ) } } + + if (mediaPlayer.isMuted.value) { + IconButton( + onClick = { + mediaPlayer.unmute() + } + ) { + Icon( + imageVector = Icons.Default.VolumeOff, + contentDescription = stringResource(SharedRes.strings.muted) + ) + } + } else { + IconButton( + onClick = { + mediaPlayer.mute() + } + ) { + Icon( + imageVector = Icons.Default.VolumeUp, + contentDescription = stringResource(SharedRes.strings.unmuted) + ) + } + } + + Slider( + value = mediaPlayer.volume.value, + onValueChange = { + mediaPlayer.setVolume(it) + }, + valueRange = 0F..100F, + modifier = Modifier.width(100.dp) + ) } DisposableEffect(window) { diff --git a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoPlayer.kt b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoPlayer.kt index 935b340a..f602e4da 100644 --- a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoPlayer.kt +++ b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/shared/ui/screen/video/VideoPlayer.kt @@ -14,6 +14,7 @@ import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent import uk.co.caprica.vlcj.player.component.MediaPlayerComponent +import kotlin.math.roundToInt @Composable fun VideoPlayer( @@ -56,6 +57,21 @@ fun VideoPlayer( val isPlaying = remember { mutableStateOf(false) } val length = remember { mutableLongStateOf(0) } val time = remember { mutableLongStateOf(0) } + val isMuted = remember { mutableStateOf(mediaPlayerComponent.mediaPlayer()?.audio()?.isMute ?: false) } + val volumeState = remember { + val current = mediaPlayerComponent.mediaPlayer()?.audio()?.volume()?.toFloat() ?: 0F + val set = if (current <= 0F) { + if (isMuted.value) { + 0F + } else { + 1F + } + } else { + current + } + + mutableFloatStateOf(set) + } val eventListener = remember { object : MediaPlayerEventAdapter() { override fun error(mediaPlayer: MediaPlayer?) { @@ -105,6 +121,18 @@ fun VideoPlayer( (mediaPlayer ?: mediaPlayerComponent.mediaPlayer())?.controls()?.setTime(startingPos) } + + override fun muted(mediaPlayer: MediaPlayer?, muted: Boolean) { + super.muted(mediaPlayer, muted) + + isMuted.value = muted + } + + override fun volumeChanged(mediaPlayer: MediaPlayer?, volume: Float) { + super.volumeChanged(mediaPlayer, volume) + + volumeState.value = volume * 100 + } } } LaunchedEffect(mediaPlayerComponent, eventListener) { @@ -126,6 +154,8 @@ fun VideoPlayer( override val isPlaying: MutableState = isPlaying override val length: MutableLongState = length override val time: MutableLongState = time + override val isMuted: MutableState = isMuted + override val volume: MutableFloatState = volumeState override fun play() { mediaPlayerComponent.mediaPlayer()?.controls()?.play() @@ -146,6 +176,18 @@ fun VideoPlayer( override fun seekTo(millis: Long) { mediaPlayerComponent.mediaPlayer()?.controls()?.setTime(millis) } + + override fun mute() { + mediaPlayerComponent.mediaPlayer()?.audio()?.isMute = true + } + + override fun unmute() { + mediaPlayerComponent.mediaPlayer()?.audio()?.isMute = false + } + + override fun setVolume(volume: Float) { + mediaPlayerComponent.mediaPlayer()?.audio()?.setVolume(volume.roundToInt()) + } } } } return null