mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +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,
|
summaryOff = R.string.disabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val SubtitleStyle =
|
||||||
|
AppDestinationPreference(
|
||||||
|
title = R.string.subtitle_style,
|
||||||
|
destination = Destination.Settings(PreferenceScreenOption.SUBTITLES),
|
||||||
|
)
|
||||||
|
|
||||||
val PlayerBackendPref =
|
val PlayerBackendPref =
|
||||||
AppChoicePreference<PlayerBackend>(
|
AppChoicePreference<PlayerBackend>(
|
||||||
title = R.string.player_backend,
|
title = R.string.player_backend,
|
||||||
|
|
@ -704,6 +710,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
|
||||||
|
|
@ -83,6 +85,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()
|
||||||
|
|
||||||
|
|
@ -126,3 +135,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(
|
||||||
|
|
|
||||||
|
|
@ -342,37 +342,50 @@ fun PlaybackOverlay(
|
||||||
(playerControls.currentPosition.toFloat() / playerControls.duration)
|
(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) {
|
AnimatedVisibility(
|
||||||
Box(
|
seekProgressPercent >= 0 && seekBarFocused,
|
||||||
modifier =
|
) {
|
||||||
Modifier
|
Box(
|
||||||
.align(Alignment.Center)
|
modifier =
|
||||||
.fillMaxWidth(.95f),
|
Modifier
|
||||||
) {
|
.align(Alignment.Center)
|
||||||
SeekPreviewImage(
|
.fillMaxWidth(.95f),
|
||||||
modifier =
|
) {
|
||||||
Modifier
|
Column(
|
||||||
.align(Alignment.BottomStart)
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
.offsetByPercent(
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
xPercentage = seekProgressPercent.coerceIn(0f, 1f),
|
modifier =
|
||||||
).padding(bottom = controllerHeight - titleHeight - subtitleHeight),
|
Modifier
|
||||||
previewImageUrl = imageUrl,
|
.align(Alignment.BottomStart)
|
||||||
duration = playerControls.duration,
|
.offsetByPercent(
|
||||||
seekProgressMs = seekProgressMs,
|
xPercentage = seekProgressPercent.coerceIn(0f, 1f),
|
||||||
videoWidth = trickplayInfo.width,
|
).padding(bottom = controllerHeight - titleHeight - subtitleHeight),
|
||||||
videoHeight = trickplayInfo.height,
|
) {
|
||||||
trickPlayInfo = trickplayInfo,
|
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,
|
||||||
|
videoWidth = trickplayInfo.width,
|
||||||
|
videoHeight = trickplayInfo.height,
|
||||||
|
trickPlayInfo = trickplayInfo,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Text(
|
||||||
|
text = (seekProgressMs / 1000L).seconds.toString(),
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,9 @@ package com.github.damontecres.wholphin.ui.playback
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
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.height
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
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.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.CachePolicy
|
import coil3.request.CachePolicy
|
||||||
import coil3.request.ImageRequest
|
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.CoilTrickplayTransformation
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import org.jellyfin.sdk.model.api.TrickplayInfo
|
import org.jellyfin.sdk.model.api.TrickplayInfo
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
|
|
||||||
fun Modifier.offsetByPercent(
|
fun Modifier.offsetByPercent(
|
||||||
xPercentage: Float,
|
xPercentage: Float,
|
||||||
|
|
@ -85,55 +80,44 @@ fun SeekPreviewImage(
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
Column(
|
if (previewImageUrl.isNotNullOrBlank() &&
|
||||||
modifier = modifier,
|
videoWidth != null &&
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
videoHeight != null
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
) {
|
) {
|
||||||
if (previewImageUrl.isNotNullOrBlank() &&
|
val height = 160.dp
|
||||||
videoWidth != null &&
|
val width = height * (videoWidth.toFloat() / videoHeight)
|
||||||
videoHeight != null
|
val heightPx = with(LocalDensity.current) { height.toPx().toInt() }
|
||||||
) {
|
val widthPx = with(LocalDensity.current) { width.toPx().toInt() }
|
||||||
val height = 160.dp
|
|
||||||
val width = height * (videoWidth.toFloat() / videoHeight)
|
|
||||||
val heightPx = with(LocalDensity.current) { height.toPx().toInt() }
|
|
||||||
val widthPx = with(LocalDensity.current) { width.toPx().toInt() }
|
|
||||||
|
|
||||||
val index = (seekProgressMs.toDouble() / trickPlayInfo.interval).toInt() // Which tile
|
val index = (seekProgressMs.toDouble() / trickPlayInfo.interval).toInt() // Which tile
|
||||||
val numberOfTitlesPerImage = trickPlayInfo.tileHeight * trickPlayInfo.tileWidth
|
val numberOfTitlesPerImage = trickPlayInfo.tileHeight * trickPlayInfo.tileWidth
|
||||||
val imageIndex = index % numberOfTitlesPerImage
|
val imageIndex = index % numberOfTitlesPerImage
|
||||||
|
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
modifier
|
||||||
.width(width)
|
.width(width)
|
||||||
.height(height)
|
.height(height)
|
||||||
.background(Color.Black)
|
.background(Color.Black)
|
||||||
.border(1.5.dp, color = MaterialTheme.colorScheme.border),
|
.border(1.5.dp, color = MaterialTheme.colorScheme.border),
|
||||||
model =
|
model =
|
||||||
ImageRequest
|
ImageRequest
|
||||||
.Builder(context)
|
.Builder(context)
|
||||||
.data(previewImageUrl)
|
.data(previewImageUrl)
|
||||||
.memoryCachePolicy(CachePolicy.ENABLED)
|
.memoryCachePolicy(CachePolicy.ENABLED)
|
||||||
.transformations(
|
.transformations(
|
||||||
CoilTrickplayTransformation(
|
CoilTrickplayTransformation(
|
||||||
widthPx,
|
widthPx,
|
||||||
heightPx,
|
heightPx,
|
||||||
trickPlayInfo.tileHeight,
|
trickPlayInfo.tileHeight,
|
||||||
trickPlayInfo.tileWidth,
|
trickPlayInfo.tileWidth,
|
||||||
imageIndex,
|
imageIndex,
|
||||||
index,
|
index,
|
||||||
),
|
),
|
||||||
).build(),
|
).build(),
|
||||||
contentScale = ContentScale.None,
|
contentScale = ContentScale.None,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
placeholder = placeHolder,
|
placeholder = placeHolder,
|
||||||
)
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
text = (seekProgressMs / 1000L).seconds.toString(),
|
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
|
||||||
style = MaterialTheme.typography.labelLarge,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,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) }
|
||||||
|
|
@ -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 -> {
|
else -> {
|
||||||
val value = pref.getter.invoke(preferences)
|
val value = pref.getter.invoke(preferences)
|
||||||
ComposablePreference(
|
ComposablePreference(
|
||||||
|
|
@ -357,13 +374,24 @@ fun PreferencesPage(
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.background(MaterialTheme.colorScheme.background),
|
modifier = modifier.background(MaterialTheme.colorScheme.background),
|
||||||
) {
|
) {
|
||||||
PreferencesContent(
|
when (preferenceScreenOption) {
|
||||||
initialPreferences,
|
PreferenceScreenOption.BASIC,
|
||||||
preferenceScreenOption,
|
PreferenceScreenOption.ADVANCED,
|
||||||
Modifier
|
PreferenceScreenOption.USER_INTERFACE,
|
||||||
.fillMaxWidth(.4f)
|
->
|
||||||
.fillMaxHeight()
|
PreferencesContent(
|
||||||
.align(Alignment.TopEnd),
|
initialPreferences,
|
||||||
)
|
preferenceScreenOption,
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth(.4f)
|
||||||
|
.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.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 java.io.File
|
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;
|
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;
|
||||||
|
|
@ -101,6 +126,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,23 @@
|
||||||
<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 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">
|
<string-array name="theme_song_volume">
|
||||||
<item>Disabled</item>
|
<item>Disabled</item>
|
||||||
|
|
@ -90,9 +107,33 @@
|
||||||
<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>
|
||||||
|
|
||||||
<string-array name="player_backend_options">
|
<string-array name="player_backend_options">
|
||||||
<item>ExoPlayer (default)</item>
|
<item>ExoPlayer (default)</item>
|
||||||
<item>MPV (Experimental)</item>
|
<item>MPV (Experimental)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -144,11 +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="player_backend">Playback Backend</string>
|
<string name="italic_font">Italicize font</string>
|
||||||
<string name="mpv_hardware_decoding">MPV: Use hardware decoding</string>
|
<string name="font">Font</string>
|
||||||
<string name="disable_if_crash">Disable if you experience crashes</string>
|
<string name="background">Background</string>
|
||||||
<string name="mpv_options">MPV Options</string>
|
|
||||||
<string name="exoplayer_options">ExoPlayer Options</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