feat(player): Add option to change fonts (#1185)

Co-authored-by: jmir1 <jhmiramon@gmail.com>
This commit is contained in:
Abdallah 2023-11-02 22:00:58 +01:00 committed by GitHub
parent 082d9e3395
commit 5567be64fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 124 additions and 22 deletions

View file

@ -273,6 +273,8 @@ dependencies {
implementation(libs.arthenica.smartexceptions)
// seeker seek bar
implementation(libs.seeker)
// true type parser
implementation(libs.truetypeparser)
}
androidComponents {

View file

@ -13,6 +13,7 @@ import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
@ -573,6 +574,21 @@ class PlayerActivity : BaseActivity() {
MPVLib.setPropertyDouble("sub-delay", subtitlesDelay().get() / 1000.0)
}
MPVLib.setPropertyString(
"sub-fonts-dir",
File(
Environment.getExternalStorageDirectory().absolutePath + File.separator +
getString(R.string.app_name),
"fonts",
).path,
)
if (playerPreferences.subtitleFont().get().trim() != "") {
MPVLib.setPropertyString("sub-font", playerPreferences.subtitleFont().get())
} else {
MPVLib.setPropertyString("sub-font", "Sans Serif")
}
MPVLib.setPropertyString("sub-bold", if (boldSubtitles().get()) "yes" else "no")
MPVLib.setPropertyString("sub-italic", if (italicSubtitles().get()) "yes" else "no")
MPVLib.setPropertyInt("sub-font-size", subtitleFontSize().get())

View file

@ -76,6 +76,8 @@ class PlayerPreferences(
fun overrideSubsASS() = preferenceStore.getBoolean("pref_override_subtitles_ass", false)
fun subtitleFont() = preferenceStore.getString("pref_subtitle_font", "Sans Serif")
fun subtitleFontSize() = preferenceStore.getInt("pref_subtitles_font_size", 55)
fun boldSubtitles() = preferenceStore.getBoolean("pref_bold_subtitles", false)
fun italicSubtitles() = preferenceStore.getBoolean("pref_italic_subtitles", false)

View file

@ -66,6 +66,8 @@ private fun SubtitleColors(
val borderColorPref = screenModel.preferences.borderColorSubtitles()
val backgroundColorPref = screenModel.preferences.backgroundColorSubtitles()
val font by screenModel.preferences.subtitleFont().collectAsState()
Row(horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.fillMaxWidth()) {
SubtitleColorSelector(
label = R.string.player_subtitle_text_color,
@ -89,6 +91,7 @@ private fun SubtitleColors(
Column(horizontalAlignment = Alignment.CenterHorizontally) {
SubtitlePreview(
font = font,
isBold = screenModel.preferences.boldSubtitles().collectAsState().value,
isItalic = screenModel.preferences.italicSubtitles().collectAsState().value,
textColor = Color(textColorPref.collectAsState().value),

View file

@ -1,26 +1,37 @@
package eu.kanade.tachiyomi.ui.player.settings.sheets.subtitle
import android.os.Environment
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material.SnackbarDefaults.backgroundColor
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.outlined.FormatBold
import androidx.compose.material.icons.outlined.FormatItalic
import androidx.compose.material.icons.outlined.FormatSize
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
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.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.yubyf.truetypeparser.TTFFile
import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.OutlinedNumericChooser
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
@ -29,6 +40,7 @@ import eu.kanade.tachiyomi.ui.player.settings.PlayerSettingsScreenModel
import `is`.xyz.mpv.MPVLib
import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.padding
import java.io.File
@Composable
fun SubtitleFontPage(screenModel: PlayerSettingsScreenModel) {
@ -41,6 +53,7 @@ fun SubtitleFontPage(screenModel: PlayerSettingsScreenModel) {
private fun SubtitleFont(
screenModel: PlayerSettingsScreenModel,
) {
val font by screenModel.preferences.subtitleFont().collectAsState()
val boldSubtitles by screenModel.preferences.boldSubtitles().collectAsState()
val italicSubtitles by screenModel.preferences.italicSubtitles().collectAsState()
val subtitleFontSize by screenModel.preferences.subtitleFontSize().collectAsState()
@ -65,6 +78,30 @@ private fun SubtitleFont(
screenModel.preferences.subtitleFontSize().set(it)
}
val updateFont: (String) -> Unit = {
MPVLib.setPropertyString("sub-font", it)
screenModel.preferences.subtitleFont().set(it)
}
val context = LocalContext.current
val fontList by remember {
derivedStateOf {
val customFonts = File(
Environment.getExternalStorageDirectory().absolutePath +
File.separator + context.getString(R.string.app_name) +
File.separator,
"fonts",
).listFiles { file ->
file.extension.equals("ttf", true) ||
file.extension.equals("otf", true)
}?.map {
TTFFile.open(it).families.values.toTypedArray()[0] to it.absolutePath
} ?: emptyList()
listOf("Sans Serif" to "") + customFonts
}
}
var selectingFont by remember { mutableStateOf(false) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny),
@ -74,11 +111,13 @@ private fun SubtitleFont(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxWidth(),
) {
Icon(
imageVector = Icons.Outlined.FormatSize,
contentDescription = null,
modifier = Modifier.size(32.dp),
)
IconButton(onClick = { selectingFont = true }) {
Icon(
imageVector = Icons.Outlined.FormatSize,
contentDescription = null,
modifier = Modifier.size(32.dp),
)
}
OutlinedNumericChooser(
label = stringResource(id = R.string.player_font_size_text_field),
@ -111,7 +150,26 @@ private fun SubtitleFont(
)
}
DropdownMenu(expanded = selectingFont, onDismissRequest = { selectingFont = false }) {
fontList.map {
val fontName = it.first
DropdownMenuItem(
text = { Text(fontName) },
onClick = { updateFont(fontName) },
trailingIcon = {
if (font == fontName) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
)
}
},
)
}
}
SubtitlePreview(
font = font,
isBold = boldSubtitles,
isItalic = italicSubtitles,
textColor = Color(textColor),

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.player.settings.sheets.subtitle
import android.graphics.Rect
import android.graphics.Typeface
import android.os.Build
import android.os.Environment
import androidx.annotation.RequiresApi
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
@ -25,6 +26,7 @@ import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
@ -32,11 +34,13 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.yubyf.truetypeparser.TTFFile
import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.player.settings.PlayerSettingsScreenModel
import tachiyomi.presentation.core.components.material.padding
import java.io.File
@Composable
fun SubtitleSettingsSheet(
@ -71,41 +75,38 @@ fun SubtitleSettingsSheet(
@Composable
fun OutLineText(
text: String,
font: Typeface,
outlineColor: Color = Color.Black,
textColor: Color = Color.White,
backgroundColor: Color = Color.Black,
isBold: Boolean = false,
isItalic: Boolean = false,
backgroundColor: Color = Color.Black,
) {
val textPaintStroke = Paint().asFrameworkPaint().apply {
typeface = Typeface.create(
Typeface.SANS_SERIF,
if (isBold) FontWeight.Bold.weight else FontWeight.Normal.weight,
isItalic,
)
typeface = font
isAntiAlias = true
style = android.graphics.Paint.Style.STROKE
textSize = 16f
textSize = 48f
color = outlineColor.toArgb()
strokeWidth = 2f
strokeMiter = 2f
strokeWidth = 12f
strokeMiter = 8f
strokeJoin = android.graphics.Paint.Join.ROUND
// change the text alignment from left to center (basically shift the anchor point of the text)
// keep in mind that this only affects horizontal alignment
// https://developer.android.com/reference/android/graphics/Paint.Align
textAlign = android.graphics.Paint.Align.CENTER
isFakeBoldText = isBold
textSkewX = if (isItalic) -0.25f else 0f
}
val textPaint = Paint().asFrameworkPaint().apply {
typeface = Typeface.create(
Typeface.SANS_SERIF,
if (isBold) FontWeight.Bold.weight else FontWeight.Normal.weight,
isItalic,
)
typeface = font
isAntiAlias = true
style = android.graphics.Paint.Style.FILL
textSize = 16f
textSize = 48f
color = textColor.toArgb()
textAlign = android.graphics.Paint.Align.CENTER
isFakeBoldText = isBold
textSkewX = if (isItalic) -0.25f else 0f
}
Canvas(modifier = Modifier.fillMaxSize()) {
drawIntoCanvas {
@ -142,15 +143,31 @@ fun OutLineText(
}
}
@RequiresApi(Build.VERSION_CODES.P)
@Composable
fun SubtitlePreview(
font: String,
isBold: Boolean,
isItalic: Boolean,
textColor: Color,
borderColor: Color,
backgroundColor: Color,
) {
val fontMap = File(
Environment.getExternalStorageDirectory().absolutePath +
File.separator + LocalContext.current.getString(R.string.app_name) +
File.separator,
"fonts",
).listFiles { file ->
file.extension.equals("ttf", true) ||
file.extension.equals("otf", true)
}?.associateBy(
{ TTFFile.open(it).families.values.toTypedArray()[0] },
{ it.absolutePath },
) ?: emptyMap()
val fontFile = fontMap.keys.firstOrNull { it.contains(font, true) }
?.let { Typeface.createFromFile(fontMap[it]?.let(::File)) } ?: Typeface.SANS_SERIF
Box(
modifier = Modifier
.padding(vertical = MaterialTheme.padding.medium)
@ -160,6 +177,7 @@ fun SubtitlePreview(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
OutLineText(
text = stringResource(R.string.player_subtitle_settings_example),
font = fontFile,
outlineColor = borderColor,
textColor = textColor,
isBold = isBold,

View file

@ -97,6 +97,8 @@ arthenica-smartexceptions = "com.arthenica:smart-exception-java:0.1.1"
seeker = "io.github.2307vivek:seeker:1.1.1"
truetypeparser = "io.github.yubyf:truetypeparser-light:2.1.4"
[bundles]
reactivex = ["rxandroid", "rxjava", "rxrelay"]
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-dnsoverhttps"]

View file

@ -345,6 +345,7 @@
<string name="player_subtitle_text_color">Text</string>
<string name="player_subtitle_border_color">Border</string>
<string name="player_subtitle_background_color">Background</string>
<string name="player_subtitle_font_text_field">Font Name</string>
<!-- TachiyomiSY -->
<string name="data_saver_exclude">Exclude from data saver</string>