diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/Theme.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/Theme.kt index 16e22fd..6daf03f 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/Theme.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/Theme.kt @@ -1,30 +1,45 @@ package nl.jknaapen.fladder +import android.os.Build +import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import com.materialkolor.PaletteStyle import com.materialkolor.dynamiccolor.ColorSpec import com.materialkolor.rememberDynamicColorScheme +import nl.jknaapen.fladder.objects.PlayerSettingsObject @Composable fun VideoPlayerTheme( content: @Composable () -> Unit ) { - val colorScheme = rememberDynamicColorScheme( - seedColor = Color(0xFFFF9800), + val context = LocalContext.current + + val themeColor by PlayerSettingsObject.themeColor.collectAsState(null) + + val generatedScheme = rememberDynamicColorScheme( + seedColor = themeColor ?: Color(0xFFFF9800), isDark = true, specVersion = ColorSpec.SpecVersion.SPEC_2025, style = PaletteStyle.Expressive, ) - - MaterialTheme( - colorScheme = colorScheme, - ) { - CompositionLocalProvider { - content() + + val chosenScheme: ColorScheme = + if (themeColor == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + dynamicLightColorScheme(context) + } else { + generatedScheme } + + MaterialTheme( + colorScheme = chosenScheme, + ) { + content() } } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/VideoPlayerActivity.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/VideoPlayerActivity.kt index 2714d6c..90ca7c2 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/VideoPlayerActivity.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/VideoPlayerActivity.kt @@ -47,7 +47,7 @@ fun VideoPlayerScreen( ) { val leanBackEnabled = leanBackEnabled(LocalContext.current) ExoPlayer { player -> - ScaledContent(if (leanBackEnabled) 0.50f else 1f) { + ScaledContent(if (leanBackEnabled) 0.75f else 1f) { CustomVideoControls(player) } } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/api/PlayerSettingsHelper.g.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/api/PlayerSettingsHelper.g.kt index ca1f068..06c3e89 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/api/PlayerSettingsHelper.g.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/api/PlayerSettingsHelper.g.kt @@ -95,6 +95,7 @@ enum class SegmentSkip(val raw: Int) { data class PlayerSettings ( val enableTunneling: Boolean, val skipTypes: Map, + val themeColor: Long? = null, val skipForward: Long, val skipBackward: Long ) @@ -103,15 +104,17 @@ data class PlayerSettings ( fun fromList(pigeonVar_list: List): PlayerSettings { val enableTunneling = pigeonVar_list[0] as Boolean val skipTypes = pigeonVar_list[1] as Map - val skipForward = pigeonVar_list[2] as Long - val skipBackward = pigeonVar_list[3] as Long - return PlayerSettings(enableTunneling, skipTypes, skipForward, skipBackward) + val themeColor = pigeonVar_list[2] as Long? + val skipForward = pigeonVar_list[3] as Long + val skipBackward = pigeonVar_list[4] as Long + return PlayerSettings(enableTunneling, skipTypes, themeColor, skipForward, skipBackward) } } fun toList(): List { return listOf( enableTunneling, skipTypes, + themeColor, skipForward, skipBackward, ) diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/CustomIconButton.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/CustomIconButton.kt index f0258c3..59264aa 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/CustomIconButton.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/CustomIconButton.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable @@ -57,7 +56,6 @@ internal fun CustomIconButton( Box( modifier = modifier - .wrapContentSize() // parent expands to fit children .conditional(enableScaledFocus) { scale(if (isFocused) 1.05f else 1f) } @@ -69,11 +67,11 @@ internal fun CustomIconButton( indication = null, onClick = onClick ) - .alpha(if (enabled) 1f else 0.5f), + .alpha(if (enabled) 1f else 0.15f), contentAlignment = Alignment.Center ) { CompositionLocalProvider(LocalContentColor provides currentContentColor) { - Box(modifier = Modifier.padding(16.dp)) { + Box(modifier = Modifier.padding(8.dp)) { icon() } } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ItemHeader.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ItemHeader.kt index 7d9a56d..59b20cb 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ItemHeader.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ItemHeader.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -23,6 +24,7 @@ fun ItemHeader(state: PlayableData?) { Box( modifier = Modifier .fillMaxWidth() + .statusBarsPadding() .padding(16.dp), contentAlignment = Alignment.CenterStart ) { diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt index 6b7ca86..dcee415 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt @@ -83,6 +83,8 @@ internal fun ProgressBar( val position by VideoPlayerObject.position.collectAsState(0L) val duration by VideoPlayerObject.duration.collectAsState(0L) + val endTimeString by VideoPlayerObject.endTime.collectAsState(null) + var tempPosition by remember { mutableLongStateOf(position) } var scrubbingTimeLine by remember { mutableStateOf(false) } @@ -99,7 +101,7 @@ internal fun ProgressBar( } Column( - verticalArrangement = Arrangement.spacedBy(12.dp, alignment = Alignment.CenterVertically) + verticalArrangement = Arrangement.spacedBy(4.dp, alignment = Alignment.CenterVertically) ) { val playbackData by VideoPlayerObject.implementation.playbackData.collectAsState(null) if (scrubbingTimeLine) @@ -115,21 +117,25 @@ internal fun ProgressBar( Row( horizontalArrangement = Arrangement.spacedBy(16.dp) ) { - val subTitle = playableData?.subTitle - subTitle?.let { + val progressBarTopLabel = listOf( + playableData?.subTitle, + endTimeString, + ) + + val label = progressBarTopLabel.joinToString(separator = " - ") + if (label.isNotBlank()) { Text( - text = it, - style = MaterialTheme.typography.titleLarge.copy( + text = label, + style = MaterialTheme.typography.bodyLarge.copy( color = Color.White, fontWeight = FontWeight.Bold ), ) } - VideoEndTime() } Row( horizontalArrangement = Arrangement.spacedBy( - 16.dp, + 12.dp, alignment = Alignment.CenterHorizontally ), verticalAlignment = Alignment.CenterVertically, @@ -138,7 +144,7 @@ internal fun ProgressBar( Text( formatTime(currentPosition), color = Color.White, - style = MaterialTheme.typography.titleLarge.copy( + style = MaterialTheme.typography.bodyLarge.copy( fontWeight = FontWeight.Bold ) ) @@ -163,7 +169,7 @@ internal fun ProgressBar( ) ), color = Color.White, - style = MaterialTheme.typography.titleLarge.copy( + style = MaterialTheme.typography.bodyLarge.copy( fontWeight = FontWeight.Bold ) ) @@ -212,7 +218,7 @@ internal fun RowScope.SimpleProgressBar( width = it.size.width } ) - .heightIn(min = 42.dp) + .heightIn(min = 32.dp) .pointerInput(Unit) { detectTapGestures { offset -> onUserInteraction() @@ -253,7 +259,7 @@ internal fun RowScope.SimpleProgressBar( modifier = Modifier .focusable(enabled = false) .fillMaxWidth() - .height(10.dp) + .height(9.dp) .background( color = Color.White.copy( alpha = 0.15f @@ -307,10 +313,10 @@ internal fun RowScope.SimpleProgressBar( .focusable(enabled = false) .graphicsLayer { translationX = startPx - translationY = 20.dp.toPx() + translationY = 13.dp.toPx() } .width(segDp) - .height(8.dp) + .height(7.dp) .background( color = segment.color.copy(alpha = 0.75f), shape = RoundedCornerShape(8.dp) diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/SkipOverlay.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/SkipOverlay.kt index f576510..135d547 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/SkipOverlay.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/SkipOverlay.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -20,22 +19,17 @@ import androidx.compose.foundation.layout.safeContentPadding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight @@ -61,8 +55,6 @@ internal fun BoxScope.SegmentSkipOverlay( val player = VideoPlayerObject.implementation.player val skipMap by PlayerSettingsObject.skipMap.collectAsState(mapOf()) - var isFocused by remember { mutableStateOf(false) } - LaunchedEffect(segments, skipMap) { } if (segments.isEmpty() || player == null) return @@ -91,7 +83,7 @@ internal fun BoxScope.SegmentSkipOverlay( } } - val shape = RoundedCornerShape(8.dp) + RoundedCornerShape(8.dp) AnimatedVisibility( activeSegment != null && skip == SegmentSkip.ASK, @@ -100,69 +92,53 @@ internal fun BoxScope.SegmentSkipOverlay( .padding(16.dp) .safeContentPadding() ) { - Box { - FilledTonalButton( - modifier = modifier - .align(alignment = Alignment.CenterEnd) - .focusRequester(focusRequester) - .onFocusChanged { state -> - isFocused = state.isFocused - } - .border( - width = 2.dp, - color = if (isFocused) Color.White.copy(alpha = 0.4f) else Color.Transparent, - shape = shape, - ) - .defaultSelected(true), - contentPadding = PaddingValues(horizontal = 12.dp), - shape = shape, - colors = ButtonDefaults.filledTonalButtonColors( - containerColor = Color.White.copy(alpha = 0.75f), - contentColor = Color.Black, - ), - onClick = { - activeSegment?.let { - player.seekTo(it.end) - } + CustomIconButton( + modifier = modifier + .align(alignment = Alignment.CenterEnd) + .focusRequester(focusRequester) + .defaultSelected(true), + onClick = { + activeSegment?.let { + player.seekTo(it.end) } + } + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, ) { - Row( - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - if (isAndroidTV) { + if (isAndroidTV) { + Box( + modifier = Modifier + .size(24.dp) + .background( + color = Color.Black.copy(alpha = 0.15f), + shape = CircleShape, + ) + .border( + width = 1.5.dp, + color = Color.Black.copy(alpha = 0.15f), + shape = CircleShape, + ) + ) { Box( modifier = Modifier - .size(24.dp) + .padding(7.dp) + .fillMaxSize() .background( - color = Color.Black.copy(alpha = 0.15f), - shape = CircleShape, - ) - .border( - width = 1.5.dp, - color = Color.Black.copy(alpha = 0.15f), + color = Color.White, shape = CircleShape, ) ) { - Box( - modifier = Modifier - .padding(7.dp) - .fillMaxSize() - .background( - color = Color.White, - shape = CircleShape, - ) - ) { - } } } - activeSegment?.let { - Text( - "Skip ${it.name.lowercase()}", - style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold) - ) - } + } + activeSegment?.let { + Text( + "Skip ${it.name.lowercase()}", + style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold) + ) } } } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoEndTime.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoEndTime.kt deleted file mode 100644 index 89bf721..0000000 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoEndTime.kt +++ /dev/null @@ -1,48 +0,0 @@ -package nl.jknaapen.fladder.composables.controls - -import android.os.Build -import androidx.annotation.RequiresApi -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import nl.jknaapen.fladder.objects.VideoPlayerObject -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import kotlin.time.Clock -import kotlin.time.ExperimentalTime -import kotlin.time.toJavaInstant - -@RequiresApi(Build.VERSION_CODES.O) -@OptIn(ExperimentalTime::class) -@Composable -fun VideoEndTime() { - val startInstant = remember { Clock.System.now() } - val durationMs by VideoPlayerObject.duration.collectAsState(initial = 0L) - val zone = ZoneId.systemDefault() - - val javaInstant = remember(startInstant) { startInstant.toJavaInstant() } - val endJavaInstant = remember(javaInstant, durationMs) { - javaInstant.plusMillis(durationMs) - } - val endZoned = remember(endJavaInstant, zone) { - endJavaInstant.atZone(zone) - } - - val formatter = DateTimeFormatter.ofPattern("hh:mm a") - val formattedEnd = remember(endZoned, formatter) { - endZoned.format(formatter) - } - - Text( - text = "ends at $formattedEnd", - style = MaterialTheme.typography.titleLarge.copy( - color = Color.White, - fontWeight = FontWeight.Bold - ), - ) -} diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt index 12cfaa7..281c1bb 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt @@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -97,24 +98,31 @@ fun CustomVideoControls( val buffering by VideoPlayerObject.buffering.collectAsState(true) val playing by VideoPlayerObject.playing.collectAsState(false) - val controlsPadding = 32.dp + val controlsPadding = 16.dp ImmersiveSystemBars(isImmersive = !showControls) + val bottomControlFocusRequester = remember { FocusRequester() } + + fun hideControls() { + showControls = false + bottomControlFocusRequester.requestFocus() + } + + BackHandler( enabled = showControls ) { - showControls = false + hideControls() } + // Restart the hide timer whenever `lastInteraction` changes. LaunchedEffect(lastInteraction.longValue) { delay(5.seconds) - showControls = false + hideControls() } - val bottomControlFocusRequester = remember { FocusRequester() } - fun updateLastInteraction() { showControls = true lastInteraction.longValue = System.currentTimeMillis() @@ -167,7 +175,7 @@ fun CustomVideoControls( .background( brush = Brush.linearGradient( colors = listOf( - Color.Black.copy(alpha = 0.85f), + Color.Black.copy(alpha = 0.90f), Color.Black.copy(alpha = 0f), ), start = Offset(0f, 0f), @@ -175,10 +183,11 @@ fun CustomVideoControls( ), ) .safeContentPadding(), - horizontalArrangement = Arrangement.spacedBy(16.dp, alignment = Alignment.Start) + horizontalArrangement = Arrangement.spacedBy(12.dp, alignment = Alignment.Start) ) { Column( - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f), ) { val state by VideoPlayerObject.implementation.playbackData.collectAsState( null @@ -196,7 +205,7 @@ fun CustomVideoControls( Icon( Iconsax.Outline.CloseSquare, modifier = Modifier - .size(48.dp) + .size(38.dp) .focusable(false), contentDescription = "Close icon", tint = Color.White, @@ -209,40 +218,39 @@ fun CustomVideoControls( Column( modifier = Modifier .padding(horizontal = controlsPadding) - .displayCutoutPadding(), - ) { - ProgressBar( - modifier = Modifier, - exoPlayer, bottomControlFocusRequester, ::updateLastInteraction - ) - } - Row( - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() .background( brush = Brush.linearGradient( colors = listOf( Color.Black.copy(alpha = 0f), - Color.Black.copy(alpha = 0.85f), + Color.Black.copy(alpha = 0.9f), ), start = Offset(0f, 0f), end = Offset(0f, Float.POSITIVE_INFINITY) ), ) .displayCutoutPadding() - .padding(horizontal = controlsPadding) .padding(top = 8.dp, bottom = controlsPadding) ) { - LeftButtons( - openChapterSelection = { - showChapterDialog = true - } + ProgressBar( + modifier = Modifier, + exoPlayer, bottomControlFocusRequester, ::updateLastInteraction ) - PlaybackButtons(exoPlayer, bottomControlFocusRequester) - RightButtons(showAudioDialog, showSubDialog) + Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + ) { + LeftButtons( + openChapterSelection = { + showChapterDialog = true + } + ) + PlaybackButtons(exoPlayer, bottomControlFocusRequester) + RightButtons(showAudioDialog, showSubDialog) + } } + } } SegmentSkipOverlay() @@ -252,7 +260,7 @@ fun CustomVideoControls( .align(alignment = Alignment.Center) .size(64.dp), strokeCap = StrokeCap.Round, - strokeWidth = 12.dp + strokeWidth = 10.dp ) } } @@ -310,7 +318,7 @@ fun PlaybackButtons( .padding(horizontal = 4.dp, vertical = 6.dp) .wrapContentWidth(), horizontalArrangement = Arrangement.spacedBy( - 16.dp, + 12.dp, alignment = Alignment.CenterHorizontally ), verticalAlignment = Alignment.CenterVertically, @@ -321,7 +329,6 @@ fun PlaybackButtons( ) { Icon( Iconsax.Filled.Backward, - modifier = Modifier.size(32.dp), contentDescription = previousVideo, ) } @@ -339,11 +346,13 @@ fun PlaybackButtons( ) { Icon( Iconsax.Outline.Refresh, + modifier = Modifier.size(38.dp), contentDescription = "Forward", - modifier = Modifier - .size(48.dp), ) - Text("-${backwardSpeed.inWholeSeconds}") + Text( + "-${backwardSpeed.inWholeSeconds}", + style = MaterialTheme.typography.bodySmall + ) } } CustomIconButton( @@ -357,7 +366,7 @@ fun PlaybackButtons( ) { Icon( if (isPlaying) Iconsax.Filled.Pause else Iconsax.Filled.Play, - modifier = Modifier.size(55.dp), + modifier = Modifier.size(42.dp), contentDescription = if (isPlaying) "Pause" else "Play", ) } @@ -377,10 +386,13 @@ fun PlaybackButtons( Iconsax.Outline.Refresh, contentDescription = "Forward", modifier = Modifier - .size(48.dp) + .size(38.dp) .scale(scaleX = -1f, scaleY = 1f), ) - Text(forwardSpeed.inWholeSeconds.toString()) + Text( + forwardSpeed.inWholeSeconds.toString(), + style = MaterialTheme.typography.bodySmall + ) } } @@ -390,7 +402,6 @@ fun PlaybackButtons( ) { Icon( Iconsax.Filled.Forward, - modifier = Modifier.size(32.dp), contentDescription = nextVideo, ) } @@ -405,7 +416,7 @@ internal fun RowScope.LeftButtons( Row( modifier = Modifier.weight(1f), - horizontalArrangement = Arrangement.Start + horizontalArrangement = Arrangement.spacedBy(12.dp, alignment = Alignment.Start) ) { CustomIconButton( onClick = openChapterSelection, @@ -413,7 +424,6 @@ internal fun RowScope.LeftButtons( ) { Icon( Iconsax.Filled.Check, - modifier = Modifier.size(32.dp), contentDescription = "Show chapters", ) } @@ -427,7 +437,7 @@ internal fun RowScope.RightButtons( ) { Row( modifier = Modifier.weight(1f), - horizontalArrangement = Arrangement.End + horizontalArrangement = Arrangement.spacedBy(12.dp, alignment = Alignment.End) ) { CustomIconButton( onClick = { @@ -436,7 +446,6 @@ internal fun RowScope.RightButtons( ) { Icon( Iconsax.Filled.AudioSquare, - modifier = Modifier.size(32.dp), contentDescription = "Audio Track", ) } @@ -447,7 +456,6 @@ internal fun RowScope.RightButtons( ) { Icon( Iconsax.Filled.Subtitle, - modifier = Modifier.size(32.dp), contentDescription = "Subtitles", ) } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt index 60eed29..9873af7 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt @@ -1,7 +1,6 @@ package nl.jknaapen.fladder.composables.dialogs import androidx.annotation.OptIn -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -48,7 +47,6 @@ fun AudioPicker( modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp, vertical = 16.dp), - verticalArrangement = Arrangement.spacedBy(6.dp) ) { item { val selectedOff = -1 == selectedIndex diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/CustomModalBottomSheet.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/CustomModalBottomSheet.kt index 65a78af..c72907d 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/CustomModalBottomSheet.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/CustomModalBottomSheet.kt @@ -6,12 +6,13 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.displayCutoutPadding import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush @@ -31,6 +32,10 @@ internal fun CustomModalBottomSheet( skipPartiallyExpanded = true ) + LaunchedEffect(Unit) { + modalBottomSheetState.expand() + } + ModalBottomSheet( onDismissRequest, dragHandle = null, @@ -43,13 +48,13 @@ internal fun CustomModalBottomSheet( modifier = Modifier .fillMaxWidth() .wrapContentHeight() - .systemBarsPadding() .displayCutoutPadding() .background( + shape = RoundedCornerShape(16.dp), brush = Brush.linearGradient( colors = listOf( - Color.Black.copy(alpha = 0f), - Color.Black.copy(alpha = 0.8f), + Color.Black.copy(alpha = 0.65f), + Color.Black.copy(alpha = 0.85f), ), start = Offset(0f, 0f), end = Offset(0f, Float.POSITIVE_INFINITY) diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt index ab65be3..28213f5 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt @@ -1,10 +1,9 @@ package nl.jknaapen.fladder.composables.dialogs import androidx.annotation.OptIn -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Text @@ -12,7 +11,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.media3.common.util.UnstableApi @@ -48,9 +46,8 @@ fun SubtitlePicker( LazyColumn( state = listState, modifier = Modifier - .fillMaxWidth() + .wrapContentWidth() .padding(horizontal = 8.dp, vertical = 16.dp), - verticalArrangement = Arrangement.spacedBy(6.dp) ) { item { val selectedOff = -1 == selectedIndex @@ -64,17 +61,9 @@ fun SubtitlePicker( }, selected = selectedOff ) { - Column( - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy( - 8.dp, - alignment = Alignment.CenterVertically - ) - ) { - Text( - text = "Off", - ) - } + Text( + text = "Off", + ) } } internalSubTracks.forEachIndexed { index, subtitle -> @@ -93,17 +82,9 @@ fun SubtitlePicker( }, selected = selected, ) { - Column( - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy( - 8.dp, - alignment = Alignment.CenterVertically - ) - ) { - Text( - text = serverSub?.name ?: "", - ) - } + Text( + text = serverSub?.name ?: "", + ) } } } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/TrackButton.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/TrackButton.kt index 68a7923..0bfb090 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/TrackButton.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/TrackButton.kt @@ -1,20 +1,23 @@ package nl.jknaapen.fladder.composables.dialogs -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import io.github.rabehx.iconsax.Iconsax +import io.github.rabehx.iconsax.filled.TickSquare +import nl.jknaapen.fladder.composables.controls.CustomIconButton +import nl.jknaapen.fladder.utility.defaultSelected @Composable internal fun TrackButton( @@ -23,30 +26,30 @@ internal fun TrackButton( selected: Boolean = false, content: @Composable () -> Unit, ) { - val backgroundColor = if (selected) Color.White else Color.Black - val textColor = if (selected) Color.Black else Color.White val textStyle = - MaterialTheme.typography.bodyLarge.copy(color = textColor, fontWeight = FontWeight.Bold) + MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold) - val interactionSource = remember { MutableInteractionSource() } - - TextButton( + CustomIconButton( + backgroundColor = Color.White.copy(alpha = 0.25f), modifier = modifier - .background( - color = backgroundColor.copy(alpha = 0.65f), - shape = RoundedCornerShape(12.dp), - ) - .padding(12.dp) - .clickable( - onClick = onClick, - interactionSource = interactionSource, - indication = null, - ), + .padding(vertical = 6.dp, horizontal = 12.dp) + .defaultMinSize(minHeight = 40.dp) + .defaultSelected(selected), onClick = onClick, - interactionSource = interactionSource, ) { - CompositionLocalProvider(LocalTextStyle provides textStyle) { - content() + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + if (selected) { + Icon( + imageVector = Iconsax.Filled.TickSquare, + contentDescription = "", + ) + } + CompositionLocalProvider(LocalTextStyle provides textStyle) { + content() + } } } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/PlayerSettingsObject.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/PlayerSettingsObject.kt index bfe2892..72fb3e3 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/PlayerSettingsObject.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/PlayerSettingsObject.kt @@ -2,6 +2,7 @@ package nl.jknaapen.fladder.objects import PlayerSettings import PlayerSettingsPigeon +import androidx.compose.ui.graphics.Color import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import kotlin.time.DurationUnit @@ -21,6 +22,12 @@ object PlayerSettingsObject : PlayerSettingsPigeon { ) } + val themeColor = settings.map { settings -> + settings?.themeColor.let { + if (it == null) null else Color(it) + } + } + override fun sendPlayerSettings(playerSettings: PlayerSettings) { settings.value = playerSettings } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt index 71a14c8..c5779f2 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt @@ -3,12 +3,20 @@ package nl.jknaapen.fladder.objects import PlaybackState import VideoPlayerControlsCallback import VideoPlayerListenerCallback +import android.os.Build +import androidx.annotation.RequiresApi import androidx.compose.runtime.mutableStateOf import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import nl.jknaapen.fladder.VideoPlayerActivity import nl.jknaapen.fladder.messengers.VideoPlayerImplementation import nl.jknaapen.fladder.utility.InternalTrack +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import kotlin.time.Clock +import kotlin.time.ExperimentalTime +import kotlin.time.toJavaInstant object VideoPlayerObject { val implementation: VideoPlayerImplementation = VideoPlayerImplementation() @@ -23,6 +31,20 @@ object VideoPlayerObject { val chapters = implementation.playbackData.map { it?.chapters } + @RequiresApi(Build.VERSION_CODES.O) + @OptIn(ExperimentalTime::class) + val endTime = combine(position, duration) { pos, dur -> + val startInstant = Clock.System.now() + val zone = ZoneId.systemDefault() + + val remainingMs = (dur - pos).coerceAtLeast(0L) + val endInstant = startInstant.toJavaInstant().plusMillis(remainingMs) + val endZoned = endInstant.atZone(zone) + val formatter = DateTimeFormatter.ofPattern("hh:mm a") + + "ends at ${endZoned.format(formatter)}" + } + val currentSubtitleTrackIndex = MutableStateFlow((implementation.playbackData.value?.defaultSubtrack ?: -1).toInt()) val currentAudioTrackIndex = diff --git a/lib/main.dart b/lib/main.dart index 71b1291..cefd400 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -98,7 +98,7 @@ void main(List args) async { applicationInfoProvider.overrideWith((ref) => applicationInfo), crashLogProvider.overrideWith((ref) => crashProvider), argumentsStateProvider.overrideWith((ref) => ArgumentsModel.fromArguments(args, leanBackEnabled)), - syncProvider.overrideWith((ref) => SyncNotifier(ref, applicationDirectory)) + syncProvider.overrideWith((ref) => SyncNotifier(ref, applicationDirectory)), ], child: AdaptiveLayoutBuilder( child: (context) => const Main(), diff --git a/lib/providers/settings/pigeon_player_settings_provider.dart b/lib/providers/settings/pigeon_player_settings_provider.dart new file mode 100644 index 0000000..55a78b8 --- /dev/null +++ b/lib/providers/settings/pigeon_player_settings_provider.dart @@ -0,0 +1,57 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:fladder/models/items/media_segments_model.dart'; +import 'package:fladder/providers/settings/client_settings_provider.dart'; +import 'package:fladder/providers/settings/video_player_settings_provider.dart'; +import 'package:fladder/providers/user_provider.dart'; +import 'package:fladder/src/player_settings_helper.g.dart' as pigeon; + +final pigeonPlayerSettingsSyncProvider = Provider((ref) { + void sendSettings() { + final userData = ref.read(userProvider); + final color = ref.read( + clientSettingsProvider.select( + (value) => value.themeColor?.color.toARGB32(), + ), + ); + final value = ref.read(videoPlayerSettingsProvider); + + if (!kIsWeb && Platform.isAndroid) { + pigeon.PlayerSettingsPigeon().sendPlayerSettings( + pigeon.PlayerSettings( + enableTunneling: value.enableTunneling, + skipTypes: value.segmentSkipSettings.map( + (key, value) => MapEntry( + switch (key) { + MediaSegmentType.unknown => pigeon.SegmentType.intro, + MediaSegmentType.commercial => pigeon.SegmentType.commercial, + MediaSegmentType.preview => pigeon.SegmentType.preview, + MediaSegmentType.recap => pigeon.SegmentType.recap, + MediaSegmentType.outro => pigeon.SegmentType.outro, + MediaSegmentType.intro => pigeon.SegmentType.intro, + }, + switch (value) { + SegmentSkip.none => pigeon.SegmentSkip.none, + SegmentSkip.askToSkip => pigeon.SegmentSkip.ask, + SegmentSkip.skip => pigeon.SegmentSkip.skip, + }, + ), + ), + themeColor: color, + skipBackward: (userData?.userSettings?.skipBackDuration ?? const Duration(seconds: 15)).inMilliseconds, + skipForward: (userData?.userSettings?.skipForwardDuration ?? const Duration(seconds: 30)).inMilliseconds, + ), + ); + } + } + + ref.listen(userProvider, (_, __) => sendSettings()); + ref.listen(clientSettingsProvider, (_, __) => sendSettings()); + ref.listen(videoPlayerSettingsProvider, (_, __) => sendSettings()); + + sendSettings(); +}); diff --git a/lib/providers/settings/video_player_settings_provider.dart b/lib/providers/settings/video_player_settings_provider.dart index ff649ee..2cd3480 100644 --- a/lib/providers/settings/video_player_settings_provider.dart +++ b/lib/providers/settings/video_player_settings_provider.dart @@ -1,6 +1,3 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -8,13 +5,10 @@ import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:screen_brightness/screen_brightness.dart'; -import 'package:fladder/models/items/media_segments_model.dart'; import 'package:fladder/models/settings/key_combinations.dart'; import 'package:fladder/models/settings/video_player_settings.dart'; import 'package:fladder/providers/shared_provider.dart'; -import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/providers/video_player_provider.dart'; -import 'package:fladder/src/player_settings_helper.g.dart' as pigeon; final videoPlayerSettingsProvider = StateNotifierProvider((ref) { @@ -33,34 +27,6 @@ class VideoPlayerSettingsProviderNotifier extends StateNotifier MapEntry( - switch (key) { - MediaSegmentType.unknown => pigeon.SegmentType.intro, - MediaSegmentType.commercial => pigeon.SegmentType.commercial, - MediaSegmentType.preview => pigeon.SegmentType.preview, - MediaSegmentType.recap => pigeon.SegmentType.recap, - MediaSegmentType.outro => pigeon.SegmentType.outro, - MediaSegmentType.intro => pigeon.SegmentType.intro, - }, - switch (value) { - SegmentSkip.none => pigeon.SegmentSkip.none, - SegmentSkip.askToSkip => pigeon.SegmentSkip.ask, - SegmentSkip.skip => pigeon.SegmentSkip.skip, - }, - ), - ), - skipBackward: (userData?.userSettings?.skipBackDuration ?? const Duration(seconds: 15)).inMilliseconds, - skipForward: (userData?.userSettings?.skipForwardDuration ?? const Duration(seconds: 30)).inMilliseconds, - ), - ); - } - if (!oldState.playerSame(value)) { ref.read(videoPlayerProvider.notifier).init(); } diff --git a/lib/providers/shared_provider.dart b/lib/providers/shared_provider.dart index 480c1ae..be94eac 100644 --- a/lib/providers/shared_provider.dart +++ b/lib/providers/shared_provider.dart @@ -15,6 +15,7 @@ import 'package:fladder/providers/settings/book_viewer_settings_provider.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/settings/home_settings_provider.dart'; import 'package:fladder/providers/settings/photo_view_settings_provider.dart'; +import 'package:fladder/providers/settings/pigeon_player_settings_provider.dart'; import 'package:fladder/providers/settings/subtitle_settings_provider.dart'; import 'package:fladder/providers/settings/video_player_settings_provider.dart'; import 'package:fladder/providers/user_provider.dart'; @@ -25,6 +26,9 @@ final sharedPreferencesProvider = Provider((ref) { final sharedUtilityProvider = Provider((ref) { final sharedPrefs = ref.watch(sharedPreferencesProvider); + + //Init pigeon settings sync for native + ref.read(pigeonPlayerSettingsSyncProvider); return SharedUtility(ref: ref, sharedPreferences: sharedPrefs); }); diff --git a/lib/screens/settings/client_sections/client_settings_theme.dart b/lib/screens/settings/client_sections/client_settings_theme.dart index 9bbab5c..999b5b6 100644 --- a/lib/screens/settings/client_sections/client_settings_theme.dart +++ b/lib/screens/settings/client_sections/client_settings_theme.dart @@ -24,15 +24,12 @@ List buildClientSettingsTheme(BuildContext context, WidgetRef ref) { items: ThemeMode.values, selected: [ref.read(clientSettingsProvider.select((value) => value.themeMode))], onChanged: (values) => ref.read(clientSettingsProvider.notifier).setThemeMode(values.first), - itemBuilder: (type, selected, tap) => RadioGroup( - groupValue: ref.read(clientSettingsProvider.select((value) => value.themeMode)), + itemBuilder: (type, selected, tap) => CheckboxListTile( + value: selected, onChanged: (value) => tap(), - child: RadioListTile( - value: type, - title: Text(type.label(context)), - contentPadding: EdgeInsets.zero, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - ), + title: Text(type.label(context)), + contentPadding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), ), ), ), @@ -41,43 +38,40 @@ List buildClientSettingsTheme(BuildContext context, WidgetRef ref) { subLabel: Text(clientSettings.themeColor?.name ?? context.localized.dynamicText), onTap: () => openMultiSelectOptions( context, - label: context.localized.settingsLayoutModesTitle, + label: context.localized.color, items: [null, ...ColorThemes.values], selected: [(ref.read(clientSettingsProvider.select((value) => value.themeColor)))], onChanged: (values) => ref.read(clientSettingsProvider.notifier).setThemeColor(values.first), - itemBuilder: (type, selected, tap) => RadioGroup( + itemBuilder: (type, selected, tap) => CheckboxListTile( + contentPadding: EdgeInsets.zero, + value: selected, onChanged: (value) => tap(), - groupValue: ref.read(clientSettingsProvider.select((value) => value.themeColor)), - child: RadioListTile( - contentPadding: EdgeInsets.zero, - value: type, - title: Row( - children: [ - Container( - height: 24, - width: 24, - decoration: BoxDecoration( - gradient: type == null - ? const SweepGradient( - center: FractionalOffset.center, - colors: [ - Color(0xFF4285F4), // blue - Color(0xFF34A853), // green - Color(0xFFFBBC05), // yellow - Color(0xFFEA4335), // red - Color(0xFF4285F4), // blue again to seamlessly transition to the start - ], - stops: [0.0, 0.25, 0.5, 0.75, 1.0], - ) - : null, - color: type?.color, - borderRadius: BorderRadius.circular(4), - ), + title: Row( + children: [ + Container( + height: 24, + width: 24, + decoration: BoxDecoration( + gradient: type == null + ? const SweepGradient( + center: FractionalOffset.center, + colors: [ + Color(0xFF4285F4), // blue + Color(0xFF34A853), // green + Color(0xFFFBBC05), // yellow + Color(0xFFEA4335), // red + Color(0xFF4285F4), // blue again to seamlessly transition to the start + ], + stops: [0.0, 0.25, 0.5, 0.75, 1.0], + ) + : null, + color: type?.color, + borderRadius: BorderRadius.circular(4), ), - const SizedBox(width: 8), - Text(type?.name ?? context.localized.dynamicText), - ], - ), + ), + const SizedBox(width: 8), + Text(type?.name ?? context.localized.dynamicText), + ], ), ), ), @@ -88,18 +82,15 @@ List buildClientSettingsTheme(BuildContext context, WidgetRef ref) { onTap: () async { await openMultiSelectOptions( context, - label: context.localized.settingsLayoutModesTitle, + label: context.localized.clientSettingsSchemeVariantTitle, items: DynamicSchemeVariant.values, selected: [(ref.read(clientSettingsProvider.select((value) => value.schemeVariant)))], onChanged: (values) => ref.read(clientSettingsProvider.notifier).setSchemeVariant(values.first), - itemBuilder: (type, selected, tap) => RadioGroup( + itemBuilder: (type, selected, tap) => CheckboxListTile( + contentPadding: EdgeInsets.zero, + value: selected, onChanged: (value) => tap(), - groupValue: selected ? type : null, - child: RadioListTile( - contentPadding: EdgeInsets.zero, - value: type, - title: Text(type.label(context)), - ), + title: Text(type.label(context)), ), ); }, diff --git a/lib/src/player_settings_helper.g.dart b/lib/src/player_settings_helper.g.dart index 42e1d23..e71e63b 100644 --- a/lib/src/player_settings_helper.g.dart +++ b/lib/src/player_settings_helper.g.dart @@ -47,6 +47,7 @@ class PlayerSettings { PlayerSettings({ required this.enableTunneling, required this.skipTypes, + this.themeColor, required this.skipForward, required this.skipBackward, }); @@ -55,6 +56,8 @@ class PlayerSettings { Map skipTypes; + int? themeColor; + int skipForward; int skipBackward; @@ -63,6 +66,7 @@ class PlayerSettings { return [ enableTunneling, skipTypes, + themeColor, skipForward, skipBackward, ]; @@ -76,8 +80,9 @@ class PlayerSettings { return PlayerSettings( enableTunneling: result[0]! as bool, skipTypes: (result[1] as Map?)!.cast(), - skipForward: result[2]! as int, - skipBackward: result[3]! as int, + themeColor: result[2] as int?, + skipForward: result[3]! as int, + skipBackward: result[4]! as int, ); } diff --git a/lib/util/option_dialogue.dart b/lib/util/option_dialogue.dart index 9544e6c..530e5ed 100644 --- a/lib/util/option_dialogue.dart +++ b/lib/util/option_dialogue.dart @@ -19,7 +19,7 @@ Future> openMultiSelectOptions( builder: (context, setState) => AlertDialog( title: Text(label), content: SizedBox( - width: MediaQuery.of(context).size.width * 0.65, + width: MediaQuery.of(context).size.width * 0.85, child: ListView( physics: const AlwaysScrollableScrollPhysics(), shrinkWrap: true, diff --git a/pigeons/player_settings_pigeon.dart b/pigeons/player_settings_pigeon.dart index 6b78f5b..17f5767 100644 --- a/pigeons/player_settings_pigeon.dart +++ b/pigeons/player_settings_pigeon.dart @@ -14,12 +14,15 @@ import 'package:pigeon/pigeon.dart'; class PlayerSettings { final bool enableTunneling; final Map skipTypes; + //Color in ARGB32 format + final int? themeColor; final int skipForward; final int skipBackward; const PlayerSettings({ required this.enableTunneling, required this.skipTypes, + required this.themeColor, required this.skipForward, required this.skipBackward, });