mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Merge branch 'main' into develop/mpv
This commit is contained in:
commit
6d585a2a99
16 changed files with 612 additions and 97 deletions
|
|
@ -668,6 +668,12 @@ sealed interface AppPreference<T> {
|
|||
summaryOff = R.string.disabled,
|
||||
)
|
||||
|
||||
val SubtitleStyle =
|
||||
AppDestinationPreference(
|
||||
title = R.string.subtitle_style,
|
||||
destination = Destination.Settings(PreferenceScreenOption.SUBTITLES),
|
||||
)
|
||||
|
||||
val PlayerBackendPref =
|
||||
AppChoicePreference<PlayerBackend>(
|
||||
title = R.string.player_backend,
|
||||
|
|
@ -704,6 +710,7 @@ val basicPreferences =
|
|||
AppPreference.RewatchNextUp,
|
||||
AppPreference.PlayThemeMusic,
|
||||
AppPreference.RememberSelectedTab,
|
||||
AppPreference.SubtitleStyle,
|
||||
AppPreference.ThemeColors,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.github.damontecres.wholphin.preferences
|
||||
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
|
@ -83,6 +85,13 @@ class AppPreferencesSerializer
|
|||
navDrawerSwitchOnFocus =
|
||||
AppPreference.NavDrawerSwitchOnFocus.defaultValue
|
||||
showClock = AppPreference.ShowClock.defaultValue
|
||||
|
||||
subtitlesPreferences =
|
||||
SubtitlePreferences
|
||||
.newBuilder()
|
||||
.apply {
|
||||
resetSubtitles()
|
||||
}.build()
|
||||
}.build()
|
||||
}.build()
|
||||
|
||||
|
|
@ -126,3 +135,21 @@ inline fun AppPreferences.updateInterfacePreferences(block: InterfacePreferences
|
|||
update {
|
||||
interfacePreferences = interfacePreferences.toBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
inline fun AppPreferences.updateSubtitlePreferences(block: SubtitlePreferences.Builder.() -> Unit): AppPreferences =
|
||||
updateInterfacePreferences {
|
||||
subtitlesPreferences = subtitlesPreferences.toBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
fun SubtitlePreferences.Builder.resetSubtitles() {
|
||||
fontSize = SubtitleSettings.FontSize.defaultValue.toInt()
|
||||
fontColor = SubtitleSettings.FontColor.defaultValue.toArgb()
|
||||
fontBold = SubtitleSettings.FontBold.defaultValue
|
||||
fontItalic = SubtitleSettings.FontItalic.defaultValue
|
||||
fontOpacity = SubtitleSettings.FontOpacity.defaultValue.toInt()
|
||||
edgeColor = SubtitleSettings.EdgeColor.defaultValue.toArgb()
|
||||
edgeStyle = SubtitleSettings.EdgeStylePref.defaultValue
|
||||
backgroundColor = SubtitleSettings.BackgroundColor.defaultValue.toArgb()
|
||||
backgroundOpacity = SubtitleSettings.BackgroundOpacity.defaultValue.toInt()
|
||||
backgroundStyle = SubtitleSettings.BackgroundStylePref.defaultValue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ fun SliderBar(
|
|||
targetValue = 6.dp.times((if (isFocused) 2f else 1f)),
|
||||
)
|
||||
var currentValue by remember(value) { mutableLongStateOf(value) }
|
||||
val percent = currentValue.toFloat() / (max - min)
|
||||
val percent = (currentValue - min).toFloat() / (max - min)
|
||||
|
||||
val handleSeekEventModifier =
|
||||
Modifier.handleDPadKeyEvents(
|
||||
|
|
|
|||
|
|
@ -342,29 +342,36 @@ fun PlaybackOverlay(
|
|||
(playerControls.currentPosition.toFloat() / playerControls.duration)
|
||||
}
|
||||
}
|
||||
if (trickplayInfo != null) {
|
||||
|
||||
AnimatedVisibility(
|
||||
seekProgressPercent >= 0 && seekBarFocused,
|
||||
) {
|
||||
val tilesPerImage = trickplayInfo.tileWidth * trickplayInfo.tileHeight
|
||||
val index =
|
||||
(seekProgressMs / trickplayInfo.interval).toInt() / tilesPerImage
|
||||
val imageUrl = trickplayUrlFor(index)
|
||||
|
||||
if (imageUrl != null) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.fillMaxWidth(.95f),
|
||||
) {
|
||||
SeekPreviewImage(
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.offsetByPercent(
|
||||
xPercentage = seekProgressPercent.coerceIn(0f, 1f),
|
||||
).padding(bottom = controllerHeight - titleHeight - subtitleHeight),
|
||||
) {
|
||||
if (trickplayInfo != null) {
|
||||
val tilesPerImage = trickplayInfo.tileWidth * trickplayInfo.tileHeight
|
||||
val index =
|
||||
(seekProgressMs / trickplayInfo.interval).toInt() / tilesPerImage
|
||||
val imageUrl = trickplayUrlFor(index)
|
||||
|
||||
if (imageUrl != null) {
|
||||
SeekPreviewImage(
|
||||
modifier =
|
||||
Modifier,
|
||||
previewImageUrl = imageUrl,
|
||||
duration = playerControls.duration,
|
||||
seekProgressMs = seekProgressMs,
|
||||
|
|
@ -374,6 +381,12 @@ fun PlaybackOverlay(
|
|||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = (seekProgressMs / 1000L).seconds.toString(),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.damontecres.wholphin.ui.playback
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.annotation.Dimension
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
|
|
@ -70,6 +71,7 @@ import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
|||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.toSubtitleStyle
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
|
|
@ -285,8 +287,10 @@ fun PlaybackPage(
|
|||
AndroidView(
|
||||
factory = { context ->
|
||||
SubtitleView(context).apply {
|
||||
setUserDefaultStyle()
|
||||
setUserDefaultTextSize()
|
||||
preferences.appPreferences.interfacePreferences.subtitlesPreferences.let {
|
||||
setStyle(it.toSubtitleStyle())
|
||||
setFixedTextSize(Dimension.SP, it.fontSize.toFloat())
|
||||
}
|
||||
}
|
||||
},
|
||||
update = {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,9 @@ package com.github.damontecres.wholphin.ui.playback
|
|||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
|
|
@ -17,7 +14,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.request.CachePolicy
|
||||
import coil3.request.ImageRequest
|
||||
|
|
@ -25,7 +21,6 @@ import coil3.request.transformations
|
|||
import com.github.damontecres.wholphin.ui.CoilTrickplayTransformation
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import org.jellyfin.sdk.model.api.TrickplayInfo
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
fun Modifier.offsetByPercent(
|
||||
xPercentage: Float,
|
||||
|
|
@ -85,11 +80,6 @@ fun SeekPreviewImage(
|
|||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
if (previewImageUrl.isNotNullOrBlank() &&
|
||||
videoWidth != null &&
|
||||
videoHeight != null
|
||||
|
|
@ -105,7 +95,7 @@ fun SeekPreviewImage(
|
|||
|
||||
AsyncImage(
|
||||
modifier =
|
||||
Modifier
|
||||
modifier
|
||||
.width(width)
|
||||
.height(height)
|
||||
.background(Color.Black)
|
||||
|
|
@ -130,10 +120,4 @@ fun SeekPreviewImage(
|
|||
placeholder = placeHolder,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = (seekProgressMs / 1000L).seconds.toString(),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ enum class PreferenceScreenOption {
|
|||
BASIC,
|
||||
ADVANCED,
|
||||
USER_INTERFACE,
|
||||
SUBTITLES,
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ import com.github.damontecres.wholphin.ui.ifElse
|
|||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.playOnClickSound
|
||||
import com.github.damontecres.wholphin.ui.playSoundOnFocus
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleStylePage
|
||||
import com.github.damontecres.wholphin.ui.setup.UpdateViewModel
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
|
|
@ -94,12 +96,14 @@ fun PreferencesContent(
|
|||
PreferenceScreenOption.BASIC -> basicPreferences
|
||||
PreferenceScreenOption.ADVANCED -> advancedPreferences
|
||||
PreferenceScreenOption.USER_INTERFACE -> uiPreferences
|
||||
PreferenceScreenOption.SUBTITLES -> SubtitleSettings.preferences
|
||||
}
|
||||
val screenTitle =
|
||||
when (preferenceScreenOption) {
|
||||
PreferenceScreenOption.BASIC -> R.string.settings
|
||||
PreferenceScreenOption.ADVANCED -> R.string.advanced_settings
|
||||
PreferenceScreenOption.USER_INTERFACE -> R.string.ui_interface
|
||||
PreferenceScreenOption.SUBTITLES -> R.string.subtitle_style
|
||||
}
|
||||
|
||||
var visible by remember { mutableStateOf(false) }
|
||||
|
|
@ -302,6 +306,19 @@ fun PreferencesContent(
|
|||
)
|
||||
}
|
||||
|
||||
SubtitleSettings.Reset -> {
|
||||
ClickPreference(
|
||||
title = stringResource(pref.title),
|
||||
onClick = {
|
||||
viewModel.resetSubtitleSettings()
|
||||
},
|
||||
modifier = Modifier,
|
||||
summary = pref.summary(context, null),
|
||||
onLongClick = {},
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val value = pref.getter.invoke(preferences)
|
||||
ComposablePreference(
|
||||
|
|
@ -357,6 +374,11 @@ fun PreferencesPage(
|
|||
Box(
|
||||
modifier = modifier.background(MaterialTheme.colorScheme.background),
|
||||
) {
|
||||
when (preferenceScreenOption) {
|
||||
PreferenceScreenOption.BASIC,
|
||||
PreferenceScreenOption.ADVANCED,
|
||||
PreferenceScreenOption.USER_INTERFACE,
|
||||
->
|
||||
PreferencesContent(
|
||||
initialPreferences,
|
||||
preferenceScreenOption,
|
||||
|
|
@ -365,5 +387,11 @@ fun PreferencesPage(
|
|||
.fillMaxHeight()
|
||||
.align(Alignment.TopEnd),
|
||||
)
|
||||
|
||||
PreferenceScreenOption.SUBTITLES ->
|
||||
SubtitleStylePage(
|
||||
initialPreferences,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import com.github.damontecres.wholphin.data.isPinned
|
|||
import com.github.damontecres.wholphin.data.model.NavDrawerPinnedItem
|
||||
import com.github.damontecres.wholphin.data.model.NavPinType
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.resetSubtitles
|
||||
import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences
|
||||
import com.github.damontecres.wholphin.ui.detail.DebugViewModel.Companion.sendAppLogs
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
||||
|
|
@ -91,4 +93,20 @@ class PreferencesViewModel
|
|||
fun sendAppLogs() {
|
||||
sendAppLogs(context, api, clientInfo, deviceInfo)
|
||||
}
|
||||
|
||||
fun resetSubtitleSettings() {
|
||||
viewModelScope.launchIO {
|
||||
resetSubtitleSettings(preferenceDataStore)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
suspend fun resetSubtitleSettings(appPreferences: DataStore<AppPreferences>) {
|
||||
appPreferences.updateData {
|
||||
it.updateSubtitlePreferences {
|
||||
resetSubtitles()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,248 @@
|
|||
package com.github.damontecres.wholphin.ui.preferences.subtitle
|
||||
|
||||
import android.graphics.Typeface
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.ui.CaptionStyleCompat
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.preferences.AppChoicePreference
|
||||
import com.github.damontecres.wholphin.preferences.AppClickablePreference
|
||||
import com.github.damontecres.wholphin.preferences.AppSliderPreference
|
||||
import com.github.damontecres.wholphin.preferences.AppSwitchPreference
|
||||
import com.github.damontecres.wholphin.preferences.BackgroundStyle
|
||||
import com.github.damontecres.wholphin.preferences.EdgeStyle
|
||||
import com.github.damontecres.wholphin.preferences.SubtitlePreferences
|
||||
import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences
|
||||
import com.github.damontecres.wholphin.ui.indexOfFirstOrNull
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceGroup
|
||||
|
||||
object SubtitleSettings {
|
||||
val FontSize =
|
||||
AppSliderPreference(
|
||||
title = R.string.font_size,
|
||||
defaultValue = 24,
|
||||
min = 8,
|
||||
max = 70,
|
||||
interval = 2,
|
||||
getter = {
|
||||
it.interfacePreferences.subtitlesPreferences.fontSize
|
||||
.toLong()
|
||||
},
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { fontSize = value.toInt() }
|
||||
},
|
||||
summarizer = { value -> value?.toString() },
|
||||
)
|
||||
|
||||
private val colorList =
|
||||
listOf(
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.LightGray,
|
||||
Color.DarkGray,
|
||||
Color.Red,
|
||||
Color.Yellow,
|
||||
Color.Green,
|
||||
Color.Cyan,
|
||||
Color.Blue,
|
||||
Color.Magenta,
|
||||
)
|
||||
|
||||
val FontColor =
|
||||
AppChoicePreference<Color>(
|
||||
title = R.string.font_color,
|
||||
defaultValue = Color.White,
|
||||
getter = { Color(it.interfacePreferences.subtitlesPreferences.fontColor) },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { fontColor = value.toArgb().and(0x00FFFFFF) }
|
||||
},
|
||||
displayValues = R.array.font_colors,
|
||||
indexToValue = { colorList.getOrNull(it) ?: Color.White },
|
||||
valueToIndex = { value ->
|
||||
val color = value.toArgb().and(0x00FFFFFF)
|
||||
colorList.indexOfFirstOrNull { color == it.toArgb().and(0x00FFFFFF) } ?: 0
|
||||
},
|
||||
)
|
||||
|
||||
val FontBold =
|
||||
AppSwitchPreference(
|
||||
title = R.string.bold_font,
|
||||
defaultValue = false,
|
||||
getter = { it.interfacePreferences.subtitlesPreferences.fontBold },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { fontBold = value }
|
||||
},
|
||||
)
|
||||
val FontItalic =
|
||||
AppSwitchPreference(
|
||||
title = R.string.italic_font,
|
||||
defaultValue = false,
|
||||
getter = { it.interfacePreferences.subtitlesPreferences.fontItalic },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { fontItalic = value }
|
||||
},
|
||||
)
|
||||
|
||||
val FontOpacity =
|
||||
AppSliderPreference(
|
||||
title = R.string.font_opacity,
|
||||
defaultValue = 100,
|
||||
min = 10,
|
||||
max = 100,
|
||||
interval = 10,
|
||||
getter = {
|
||||
it.interfacePreferences.subtitlesPreferences.fontOpacity
|
||||
.toLong()
|
||||
},
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { fontOpacity = value.toInt() }
|
||||
},
|
||||
summarizer = { value -> value?.let { "$it%" } },
|
||||
)
|
||||
|
||||
val EdgeStylePref =
|
||||
AppChoicePreference<EdgeStyle>(
|
||||
title =
|
||||
R.string.edge_style,
|
||||
defaultValue = EdgeStyle.EDGE_SOLID,
|
||||
getter = { it.interfacePreferences.subtitlesPreferences.edgeStyle },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { edgeStyle = value }
|
||||
},
|
||||
displayValues = R.array.subtitle_edge,
|
||||
indexToValue = { EdgeStyle.forNumber(it) },
|
||||
valueToIndex = { it.number },
|
||||
)
|
||||
|
||||
val EdgeColor =
|
||||
AppChoicePreference<Color>(
|
||||
title = R.string.edge_color,
|
||||
defaultValue = Color.Black,
|
||||
getter = { Color(it.interfacePreferences.subtitlesPreferences.edgeColor) },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { edgeColor = value.toArgb().and(0x00FFFFFF) }
|
||||
},
|
||||
displayValues = R.array.font_colors,
|
||||
indexToValue = { colorList.getOrNull(it) ?: Color.White },
|
||||
valueToIndex = { value ->
|
||||
val color = value.toArgb().and(0x00FFFFFF)
|
||||
colorList.indexOfFirstOrNull { color == it.toArgb().and(0x00FFFFFF) } ?: 0
|
||||
},
|
||||
)
|
||||
|
||||
val BackgroundColor =
|
||||
AppChoicePreference<Color>(
|
||||
title = R.string.background_color,
|
||||
defaultValue = Color.Transparent,
|
||||
getter = { Color(it.interfacePreferences.subtitlesPreferences.backgroundColor) },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { backgroundColor = value.toArgb().and(0x00FFFFFF) }
|
||||
},
|
||||
displayValues = R.array.font_colors,
|
||||
indexToValue = { colorList.getOrNull(it) ?: Color.White },
|
||||
valueToIndex = { value ->
|
||||
val color = value.toArgb().and(0x00FFFFFF)
|
||||
colorList.indexOfFirstOrNull { color == it.toArgb().and(0x00FFFFFF) } ?: 0
|
||||
},
|
||||
)
|
||||
|
||||
val BackgroundOpacity =
|
||||
AppSliderPreference(
|
||||
title = R.string.background_opacity,
|
||||
defaultValue = 50,
|
||||
min = 10,
|
||||
max = 100,
|
||||
interval = 10,
|
||||
getter = {
|
||||
it.interfacePreferences.subtitlesPreferences.backgroundOpacity
|
||||
.toLong()
|
||||
},
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { backgroundOpacity = value.toInt() }
|
||||
},
|
||||
summarizer = { value -> value?.let { "$it%" } },
|
||||
)
|
||||
|
||||
val BackgroundStylePref =
|
||||
AppChoicePreference<BackgroundStyle>(
|
||||
title =
|
||||
R.string.background_style,
|
||||
defaultValue = BackgroundStyle.BG_NONE,
|
||||
getter = { it.interfacePreferences.subtitlesPreferences.backgroundStyle },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateSubtitlePreferences { backgroundStyle = value }
|
||||
},
|
||||
displayValues = R.array.background_style,
|
||||
indexToValue = { BackgroundStyle.forNumber(it) },
|
||||
valueToIndex = { it.number },
|
||||
)
|
||||
|
||||
val Reset =
|
||||
AppClickablePreference(
|
||||
title = R.string.reset,
|
||||
getter = { },
|
||||
setter = { prefs, _ -> prefs },
|
||||
)
|
||||
|
||||
val preferences =
|
||||
listOf(
|
||||
PreferenceGroup(
|
||||
title = R.string.font,
|
||||
preferences =
|
||||
listOf(
|
||||
FontSize,
|
||||
FontColor,
|
||||
FontBold,
|
||||
FontItalic,
|
||||
FontOpacity,
|
||||
),
|
||||
),
|
||||
PreferenceGroup(
|
||||
title = R.string.edge_style,
|
||||
preferences =
|
||||
listOf(
|
||||
EdgeStylePref,
|
||||
EdgeColor,
|
||||
),
|
||||
),
|
||||
PreferenceGroup(
|
||||
title = R.string.background,
|
||||
preferences =
|
||||
listOf(
|
||||
BackgroundStylePref,
|
||||
BackgroundColor,
|
||||
BackgroundOpacity,
|
||||
),
|
||||
),
|
||||
PreferenceGroup(
|
||||
title = R.string.more,
|
||||
preferences =
|
||||
listOf(Reset),
|
||||
),
|
||||
)
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
fun SubtitlePreferences.toSubtitleStyle(): CaptionStyleCompat {
|
||||
val fo = (fontOpacity / 100.0 * 255).toInt().shl(24)
|
||||
val bg = (backgroundOpacity / 100.0 * 255).toInt().shl(24).or(backgroundColor)
|
||||
return CaptionStyleCompat(
|
||||
fo.or(fontColor),
|
||||
if (backgroundStyle == BackgroundStyle.BG_WRAP)bg else 0,
|
||||
if (backgroundStyle == BackgroundStyle.BG_BOXED) bg else 0,
|
||||
when (edgeStyle) {
|
||||
EdgeStyle.EDGE_NONE, EdgeStyle.UNRECOGNIZED -> CaptionStyleCompat.EDGE_TYPE_NONE
|
||||
EdgeStyle.EDGE_SOLID -> CaptionStyleCompat.EDGE_TYPE_OUTLINE
|
||||
EdgeStyle.EDGE_SHADOW -> CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW
|
||||
},
|
||||
fo.or(edgeColor),
|
||||
when {
|
||||
fontBold && fontItalic -> Typeface.defaultFromStyle(Typeface.BOLD_ITALIC)
|
||||
fontBold -> Typeface.defaultFromStyle(Typeface.BOLD)
|
||||
fontItalic -> Typeface.defaultFromStyle(Typeface.ITALIC)
|
||||
else -> Typeface.DEFAULT
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
package com.github.damontecres.wholphin.ui.preferences.subtitle
|
||||
|
||||
import androidx.annotation.Dimension
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.media3.common.text.Cue
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.ui.SubtitleView
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceScreenOption
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesContent
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.toSubtitleStyle
|
||||
import timber.log.Timber
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
@Composable
|
||||
fun SubtitleStylePage(
|
||||
initialPreferences: AppPreferences,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: PreferencesViewModel = hiltViewModel(),
|
||||
) {
|
||||
var preferences by remember { mutableStateOf(initialPreferences) }
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.preferenceDataStore.data.collect {
|
||||
preferences = it
|
||||
}
|
||||
}
|
||||
Timber.v("SubtitleStylePage")
|
||||
val prefs = preferences.interfacePreferences.subtitlesPreferences
|
||||
Row(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.weight(1f),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.eclipse),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize(),
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
val examples =
|
||||
mapOf(
|
||||
"Subtitles will look like this" to 48.dp,
|
||||
"This is another example" to 24.dp,
|
||||
"Longer multi line subtitles will\nlook like this" to 0.dp,
|
||||
)
|
||||
examples.forEach { (text, padding) ->
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
SubtitleView(context)
|
||||
},
|
||||
update = {
|
||||
it.setStyle(prefs.toSubtitleStyle())
|
||||
it.setFixedTextSize(Dimension.SP, prefs.fontSize.toFloat())
|
||||
it.setCues(
|
||||
listOf(
|
||||
Cue.Builder().setText(text).build(),
|
||||
),
|
||||
)
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = padding),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
PreferencesContent(
|
||||
initialPreferences = preferences,
|
||||
preferenceScreenOption = PreferenceScreenOption.SUBTITLES,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.fillMaxWidth(.25f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.preferences.AppPreference
|
|||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.updateInterfacePreferences
|
||||
import com.github.damontecres.wholphin.preferences.updatePlaybackOverrides
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
|
@ -115,4 +116,7 @@ suspend fun upgradeApp(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (previous.isEqualOrBefore(Version.fromString("0.2.7-1-g0"))) {
|
||||
PreferencesViewModel.resetSubtitleSettings(appPreferences)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,31 @@ enum AppThemeColors {
|
|||
ORANGE = 3;
|
||||
}
|
||||
|
||||
enum EdgeStyle{
|
||||
EDGE_NONE = 0;
|
||||
EDGE_SOLID = 1;
|
||||
EDGE_SHADOW = 2;
|
||||
}
|
||||
|
||||
enum BackgroundStyle{
|
||||
BG_NONE = 0;
|
||||
BG_WRAP = 1;
|
||||
BG_BOXED = 2;
|
||||
}
|
||||
|
||||
message SubtitlePreferences{
|
||||
int32 font_size = 1;
|
||||
int32 font_color = 2;
|
||||
int32 font_opacity = 3;
|
||||
int32 edge_color = 4;
|
||||
EdgeStyle edge_style = 5;
|
||||
int32 background_color = 6;
|
||||
int32 background_opacity = 7;
|
||||
BackgroundStyle background_style = 8;
|
||||
bool font_bold = 9;
|
||||
bool font_italic = 10;
|
||||
}
|
||||
|
||||
message InterfacePreferences {
|
||||
ThemeSongVolume play_theme_songs = 1;
|
||||
bool remember_selected_tab = 2;
|
||||
|
|
@ -101,6 +126,7 @@ message InterfacePreferences {
|
|||
AppThemeColors app_theme_colors = 4;
|
||||
bool nav_drawer_switch_on_focus = 5;
|
||||
bool show_clock = 6;
|
||||
SubtitlePreferences subtitles_preferences = 7;
|
||||
}
|
||||
|
||||
message AppPreferences {
|
||||
|
|
|
|||
BIN
app/src/main/res/mipmap-hdpi/eclipse.jpeg
Normal file
BIN
app/src/main/res/mipmap-hdpi/eclipse.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 377 KiB |
|
|
@ -47,6 +47,23 @@
|
|||
<string name="update_available">Update available</string>
|
||||
<string name="update_url_summary">URL used to check for app updates</string>
|
||||
<string name="update_url">Update URL</string>
|
||||
<string name="font_size">Font size</string>
|
||||
<string name="font_color">Font color</string>
|
||||
<string name="font_opacity">Font opacity</string>
|
||||
<string name="edge_style">Edge style</string>
|
||||
<string name="edge_color">Edge color</string>
|
||||
<string name="background_opacity">Background opacity</string>
|
||||
<string name="background_style">Background style</string>
|
||||
<string name="subtitle_style">Subtitle style</string>
|
||||
<string name="background_color">Background color</string>
|
||||
<string name="reset">Reset</string>
|
||||
<string name="bold_font">Bold font</string>
|
||||
|
||||
<string name="player_backend">Playback Backend</string>
|
||||
<string name="mpv_hardware_decoding">MPV: Use hardware decoding</string>
|
||||
<string name="disable_if_crash">Disable if you experience crashes</string>
|
||||
<string name="mpv_options">MPV Options</string>
|
||||
<string name="exoplayer_options">ExoPlayer Options</string>
|
||||
|
||||
<string-array name="theme_song_volume">
|
||||
<item>Disabled</item>
|
||||
|
|
@ -90,9 +107,33 @@
|
|||
<item>During end credits/outro</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="font_colors">
|
||||
<item>White</item>
|
||||
<item>Black</item>
|
||||
<item>Light Gray</item>
|
||||
<item>Dark Gray</item>
|
||||
<item>Red</item>
|
||||
<item>Yellow</item>
|
||||
<item>Green</item>
|
||||
<item>Cyan</item>
|
||||
<item>Blue</item>
|
||||
<item>Magenta</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="subtitle_edge">
|
||||
<item>None</item>
|
||||
<item>Outline</item>
|
||||
<item>Shadow</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="background_style">
|
||||
<item>None</item>
|
||||
<item>Wrap</item>
|
||||
<item>Boxed</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="player_backend_options">
|
||||
<item>ExoPlayer (default)</item>
|
||||
<item>MPV (Experimental)</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -144,11 +144,9 @@
|
|||
<string name="add_to_playlist">Add to playlist</string>
|
||||
<string name="one_click_pause">Pause with one click</string>
|
||||
<string name="one_click_pause_summary_on">Press D-Pad center to pause/play</string>
|
||||
<string name="player_backend">Playback Backend</string>
|
||||
<string name="mpv_hardware_decoding">MPV: Use hardware decoding</string>
|
||||
<string name="disable_if_crash">Disable if you experience crashes</string>
|
||||
<string name="mpv_options">MPV Options</string>
|
||||
<string name="exoplayer_options">ExoPlayer Options</string>
|
||||
<string name="italic_font">Italicize font</string>
|
||||
<string name="font">Font</string>
|
||||
<string name="background">Background</string>
|
||||
|
||||
<plurals name="downloads">
|
||||
<item quantity="zero">%s downloads</item>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue