mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Add ability to style subtitles (#177)
Adds settings for styling text subtitles such as srt/subrip ## Issues Closes #11
This commit is contained in:
parent
762e8f287d
commit
5720603447
14 changed files with 529 additions and 11 deletions
|
|
@ -666,6 +666,12 @@ sealed interface AppPreference<T> {
|
||||||
summaryOn = R.string.one_click_pause_summary_on,
|
summaryOn = R.string.one_click_pause_summary_on,
|
||||||
summaryOff = R.string.disabled,
|
summaryOff = R.string.disabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val SubtitleStyle =
|
||||||
|
AppDestinationPreference(
|
||||||
|
title = R.string.subtitle_style,
|
||||||
|
destination = Destination.Settings(PreferenceScreenOption.SUBTITLES),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -679,6 +685,7 @@ val basicPreferences =
|
||||||
AppPreference.RewatchNextUp,
|
AppPreference.RewatchNextUp,
|
||||||
AppPreference.PlayThemeMusic,
|
AppPreference.PlayThemeMusic,
|
||||||
AppPreference.RememberSelectedTab,
|
AppPreference.RememberSelectedTab,
|
||||||
|
AppPreference.SubtitleStyle,
|
||||||
AppPreference.ThemeColors,
|
AppPreference.ThemeColors,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.github.damontecres.wholphin.preferences
|
package com.github.damontecres.wholphin.preferences
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.datastore.core.CorruptionException
|
import androidx.datastore.core.CorruptionException
|
||||||
import androidx.datastore.core.Serializer
|
import androidx.datastore.core.Serializer
|
||||||
|
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
|
||||||
import com.google.protobuf.InvalidProtocolBufferException
|
import com.google.protobuf.InvalidProtocolBufferException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
@ -74,6 +76,13 @@ class AppPreferencesSerializer
|
||||||
navDrawerSwitchOnFocus =
|
navDrawerSwitchOnFocus =
|
||||||
AppPreference.NavDrawerSwitchOnFocus.defaultValue
|
AppPreference.NavDrawerSwitchOnFocus.defaultValue
|
||||||
showClock = AppPreference.ShowClock.defaultValue
|
showClock = AppPreference.ShowClock.defaultValue
|
||||||
|
|
||||||
|
subtitlesPreferences =
|
||||||
|
SubtitlePreferences
|
||||||
|
.newBuilder()
|
||||||
|
.apply {
|
||||||
|
resetSubtitles()
|
||||||
|
}.build()
|
||||||
}.build()
|
}.build()
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
|
|
@ -112,3 +121,21 @@ inline fun AppPreferences.updateInterfacePreferences(block: InterfacePreferences
|
||||||
update {
|
update {
|
||||||
interfacePreferences = interfacePreferences.toBuilder().apply(block).build()
|
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)),
|
targetValue = 6.dp.times((if (isFocused) 2f else 1f)),
|
||||||
)
|
)
|
||||||
var currentValue by remember(value) { mutableLongStateOf(value) }
|
var currentValue by remember(value) { mutableLongStateOf(value) }
|
||||||
val percent = currentValue.toFloat() / (max - min)
|
val percent = (currentValue - min).toFloat() / (max - min)
|
||||||
|
|
||||||
val handleSeekEventModifier =
|
val handleSeekEventModifier =
|
||||||
Modifier.handleDPadKeyEvents(
|
Modifier.handleDPadKeyEvents(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.github.damontecres.wholphin.ui.playback
|
package com.github.damontecres.wholphin.ui.playback
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.annotation.Dimension
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
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.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
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.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
|
@ -285,8 +287,10 @@ fun PlaybackPage(
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { context ->
|
factory = { context ->
|
||||||
SubtitleView(context).apply {
|
SubtitleView(context).apply {
|
||||||
setUserDefaultStyle()
|
preferences.appPreferences.interfacePreferences.subtitlesPreferences.let {
|
||||||
setUserDefaultTextSize()
|
setStyle(it.toSubtitleStyle())
|
||||||
|
setFixedTextSize(Dimension.SP, it.fontSize.toFloat())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update = {
|
update = {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ enum class PreferenceScreenOption {
|
||||||
BASIC,
|
BASIC,
|
||||||
ADVANCED,
|
ADVANCED,
|
||||||
USER_INTERFACE,
|
USER_INTERFACE,
|
||||||
|
SUBTITLES,
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
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.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.playOnClickSound
|
import com.github.damontecres.wholphin.ui.playOnClickSound
|
||||||
import com.github.damontecres.wholphin.ui.playSoundOnFocus
|
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.setup.UpdateViewModel
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
|
|
@ -94,12 +96,14 @@ fun PreferencesContent(
|
||||||
PreferenceScreenOption.BASIC -> basicPreferences
|
PreferenceScreenOption.BASIC -> basicPreferences
|
||||||
PreferenceScreenOption.ADVANCED -> advancedPreferences
|
PreferenceScreenOption.ADVANCED -> advancedPreferences
|
||||||
PreferenceScreenOption.USER_INTERFACE -> uiPreferences
|
PreferenceScreenOption.USER_INTERFACE -> uiPreferences
|
||||||
|
PreferenceScreenOption.SUBTITLES -> SubtitleSettings.preferences
|
||||||
}
|
}
|
||||||
val screenTitle =
|
val screenTitle =
|
||||||
when (preferenceScreenOption) {
|
when (preferenceScreenOption) {
|
||||||
PreferenceScreenOption.BASIC -> R.string.settings
|
PreferenceScreenOption.BASIC -> R.string.settings
|
||||||
PreferenceScreenOption.ADVANCED -> R.string.advanced_settings
|
PreferenceScreenOption.ADVANCED -> R.string.advanced_settings
|
||||||
PreferenceScreenOption.USER_INTERFACE -> R.string.ui_interface
|
PreferenceScreenOption.USER_INTERFACE -> R.string.ui_interface
|
||||||
|
PreferenceScreenOption.SUBTITLES -> R.string.subtitle_style
|
||||||
}
|
}
|
||||||
|
|
||||||
var visible by remember { mutableStateOf(false) }
|
var visible by remember { mutableStateOf(false) }
|
||||||
|
|
@ -296,6 +300,19 @@ fun PreferencesContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubtitleSettings.Reset -> {
|
||||||
|
ClickPreference(
|
||||||
|
title = stringResource(pref.title),
|
||||||
|
onClick = {
|
||||||
|
viewModel.resetSubtitleSettings()
|
||||||
|
},
|
||||||
|
modifier = Modifier,
|
||||||
|
summary = pref.summary(context, null),
|
||||||
|
onLongClick = {},
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val value = pref.getter.invoke(preferences)
|
val value = pref.getter.invoke(preferences)
|
||||||
ComposablePreference(
|
ComposablePreference(
|
||||||
|
|
@ -351,6 +368,11 @@ fun PreferencesPage(
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.background(MaterialTheme.colorScheme.background),
|
modifier = modifier.background(MaterialTheme.colorScheme.background),
|
||||||
) {
|
) {
|
||||||
|
when (preferenceScreenOption) {
|
||||||
|
PreferenceScreenOption.BASIC,
|
||||||
|
PreferenceScreenOption.ADVANCED,
|
||||||
|
PreferenceScreenOption.USER_INTERFACE,
|
||||||
|
->
|
||||||
PreferencesContent(
|
PreferencesContent(
|
||||||
initialPreferences,
|
initialPreferences,
|
||||||
preferenceScreenOption,
|
preferenceScreenOption,
|
||||||
|
|
@ -359,5 +381,11 @@ fun PreferencesPage(
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.align(Alignment.TopEnd),
|
.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.NavDrawerPinnedItem
|
||||||
import com.github.damontecres.wholphin.data.model.NavPinType
|
import com.github.damontecres.wholphin.data.model.NavPinType
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
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.detail.DebugViewModel.Companion.sendAppLogs
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
||||||
|
|
@ -91,4 +93,20 @@ class PreferencesViewModel
|
||||||
fun sendAppLogs() {
|
fun sendAppLogs() {
|
||||||
sendAppLogs(context, api, clientInfo, deviceInfo)
|
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.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.updateInterfacePreferences
|
import com.github.damontecres.wholphin.preferences.updateInterfacePreferences
|
||||||
import com.github.damontecres.wholphin.preferences.updatePlaybackOverrides
|
import com.github.damontecres.wholphin.preferences.updatePlaybackOverrides
|
||||||
|
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -96,4 +97,7 @@ suspend fun upgradeApp(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (previous.isEqualOrBefore(Version.fromString("0.2.7-1-g0"))) {
|
||||||
|
PreferencesViewModel.resetSubtitleSettings(appPreferences)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,31 @@ enum AppThemeColors {
|
||||||
ORANGE = 3;
|
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 {
|
message InterfacePreferences {
|
||||||
ThemeSongVolume play_theme_songs = 1;
|
ThemeSongVolume play_theme_songs = 1;
|
||||||
bool remember_selected_tab = 2;
|
bool remember_selected_tab = 2;
|
||||||
|
|
@ -90,6 +115,7 @@ message InterfacePreferences {
|
||||||
AppThemeColors app_theme_colors = 4;
|
AppThemeColors app_theme_colors = 4;
|
||||||
bool nav_drawer_switch_on_focus = 5;
|
bool nav_drawer_switch_on_focus = 5;
|
||||||
bool show_clock = 6;
|
bool show_clock = 6;
|
||||||
|
SubtitlePreferences subtitles_preferences = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AppPreferences {
|
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,17 @@
|
||||||
<string name="update_available">Update available</string>
|
<string name="update_available">Update available</string>
|
||||||
<string name="update_url_summary">URL used to check for app updates</string>
|
<string name="update_url_summary">URL used to check for app updates</string>
|
||||||
<string name="update_url">Update URL</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-array name="theme_song_volume">
|
<string-array name="theme_song_volume">
|
||||||
<item>Disabled</item>
|
<item>Disabled</item>
|
||||||
|
|
@ -90,4 +101,29 @@
|
||||||
<item>During end credits/outro</item>
|
<item>During end credits/outro</item>
|
||||||
</string-array>
|
</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>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,9 @@
|
||||||
<string name="add_to_playlist">Add to playlist</string>
|
<string name="add_to_playlist">Add to playlist</string>
|
||||||
<string name="one_click_pause">Pause with one click</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="one_click_pause_summary_on">Press D-Pad center to pause/play</string>
|
||||||
|
<string name="italic_font">Italicize font</string>
|
||||||
|
<string name="font">Font</string>
|
||||||
|
<string name="background">Background</string>
|
||||||
|
|
||||||
<plurals name="downloads">
|
<plurals name="downloads">
|
||||||
<item quantity="zero">%s downloads</item>
|
<item quantity="zero">%s downloads</item>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue