Support using external fonts as basic and reading fonts (#333)

This commit is contained in:
Ash 2023-02-02 20:36:37 +08:00 committed by GitHub
parent c10354ce34
commit 4b04f96b4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 282 additions and 43 deletions

View File

@ -57,6 +57,8 @@ Nachfolgend sind die bisher erzielten Fortschritte und die Ziele aufgeführt, an
- [ ] Android Widget - [ ] Android Widget
- [ ] ... - [ ] ...
[Was mache ich gerade?](https://github.com/users/Ashinch/projects/2)
## Integration ## Integration
**Read You** ist mit einigen APIs von Drittanbietern kompatibel, um bei der Nutzung Ihrer bestehenden Cloud-Konten als Datenquellen zu unterstützen. **Read You** ist mit einigen APIs von Drittanbietern kompatibel, um bei der Nutzung Ihrer bestehenden Cloud-Konten als Datenquellen zu unterstützen.

View File

@ -55,6 +55,8 @@
- [ ] Android 微件 / 小组件 - [ ] Android 微件 / 小组件
- [ ] ... - [ ] ...
[我目前在做什么?](https://github.com/users/Ashinch/projects/2)
## 集成 ## 集成
**Read You** 也集成了一些第三方服务 API支持您使用已有的云端账户来作为数据源。 **Read You** 也集成了一些第三方服务 API支持您使用已有的云端账户来作为数据源。

View File

@ -57,6 +57,8 @@ The following are the progress made so far and the goals to be worked on in the
- [ ] Android widget - [ ] Android widget
- [ ] ... - [ ] ...
[What am I doing now?](https://github.com/users/Ashinch/projects/2)
## Integration ## Integration
**Read You** integrates with some of third-party service APIs to support you in using your existing cloud accounts as data sources. **Read You** integrates with some of third-party service APIs to support you in using your existing cloud accounts as data sources.

View File

@ -0,0 +1,56 @@
package me.ash.reader.data.model.preference
import android.content.Context
import androidx.compose.material3.Typography
import androidx.compose.ui.text.font.FontFamily
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.R
import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.ExternalFonts
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
import me.ash.reader.ui.theme.SystemTypography
sealed class BasicFontsPreference(val value: Int) : Preference() {
object System : BasicFontsPreference(0)
object External : BasicFontsPreference(5)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
context.dataStore.put(DataStoreKeys.BasicFonts, value)
}
}
fun toDesc(context: Context): String =
when (this) {
System -> context.getString(R.string.system_default)
External -> context.getString(R.string.external_fonts)
}
fun asFontFamily(context: Context): FontFamily =
when (this) {
System -> FontFamily.Default
External -> ExternalFonts.loadBasicTypography(context).displayLarge.fontFamily ?: FontFamily.Default
}
fun asTypography(context: Context): Typography =
when (this) {
System -> SystemTypography
External -> ExternalFonts.loadBasicTypography(context)
}
companion object {
val default = System
val values = listOf(System, External)
fun fromPreferences(preferences: Preferences): BasicFontsPreference =
when (preferences[DataStoreKeys.BasicFonts.key]) {
0 -> System
5 -> External
else -> default
}
}
}

View File

@ -24,6 +24,7 @@ fun Preferences.toSettings(): Settings {
customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this), customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this),
darkTheme = DarkThemePreference.fromPreferences(this), darkTheme = DarkThemePreference.fromPreferences(this),
amoledDarkTheme = AmoledDarkThemePreference.fromPreferences(this), amoledDarkTheme = AmoledDarkThemePreference.fromPreferences(this),
basicFonts = BasicFontsPreference.fromPreferences(this),
// Feeds page // Feeds page
feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this), feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this),

View File

@ -7,6 +7,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.DataStoreKeys
import me.ash.reader.ui.ext.ExternalFonts
import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put import me.ash.reader.ui.ext.put
@ -34,14 +35,14 @@ sealed class ReadingFontsPreference(val value: Int) : Preference() {
External -> context.getString(R.string.external_fonts) External -> context.getString(R.string.external_fonts)
} }
fun asFontFamily(): FontFamily = fun asFontFamily(context: Context): FontFamily =
when (this) { when (this) {
System -> FontFamily.Default System -> FontFamily.Default
Serif -> FontFamily.Serif Serif -> FontFamily.Serif
SansSerif -> FontFamily.SansSerif SansSerif -> FontFamily.SansSerif
Monospace -> FontFamily.Monospace Monospace -> FontFamily.Monospace
Cursive -> FontFamily.Cursive Cursive -> FontFamily.Cursive
External -> FontFamily.Default External -> ExternalFonts.loadReadingTypography(context).displayLarge.fontFamily ?: FontFamily.Default
} }
companion object { companion object {

View File

@ -25,6 +25,7 @@ data class Settings(
val customPrimaryColor: String = CustomPrimaryColorPreference.default, val customPrimaryColor: String = CustomPrimaryColorPreference.default,
val darkTheme: DarkThemePreference = DarkThemePreference.default, val darkTheme: DarkThemePreference = DarkThemePreference.default,
val amoledDarkTheme: AmoledDarkThemePreference = AmoledDarkThemePreference.default, val amoledDarkTheme: AmoledDarkThemePreference = AmoledDarkThemePreference.default,
val basicFonts: BasicFontsPreference = BasicFontsPreference.default,
// Feeds page // Feeds page
val feedsFilterBarStyle: FeedsFilterBarStylePreference = FeedsFilterBarStylePreference.default, val feedsFilterBarStyle: FeedsFilterBarStylePreference = FeedsFilterBarStylePreference.default,
@ -95,6 +96,7 @@ val LocalDarkTheme =
compositionLocalOf<DarkThemePreference> { DarkThemePreference.default } compositionLocalOf<DarkThemePreference> { DarkThemePreference.default }
val LocalAmoledDarkTheme = val LocalAmoledDarkTheme =
compositionLocalOf<AmoledDarkThemePreference> { AmoledDarkThemePreference.default } compositionLocalOf<AmoledDarkThemePreference> { AmoledDarkThemePreference.default }
val LocalBasicFonts = compositionLocalOf<BasicFontsPreference> { BasicFontsPreference.default }
// Feeds page // Feeds page
val LocalFeedsFilterBarStyle = val LocalFeedsFilterBarStyle =
@ -141,8 +143,10 @@ val LocalFlowArticleListTonalElevation =
// Reading page // Reading page
val LocalReadingTheme = compositionLocalOf<ReadingThemePreference> { ReadingThemePreference.default } val LocalReadingTheme = compositionLocalOf<ReadingThemePreference> { ReadingThemePreference.default }
val LocalReadingDarkTheme = compositionLocalOf<ReadingDarkThemePreference> { ReadingDarkThemePreference.default } val LocalReadingDarkTheme = compositionLocalOf<ReadingDarkThemePreference> { ReadingDarkThemePreference.default }
val LocalReadingPageTonalElevation = compositionLocalOf<ReadingPageTonalElevationPreference> { ReadingPageTonalElevationPreference.default } val LocalReadingPageTonalElevation =
val LocalReadingAutoHideToolbar = compositionLocalOf<ReadingAutoHideToolbarPreference> { ReadingAutoHideToolbarPreference.default } compositionLocalOf<ReadingPageTonalElevationPreference> { ReadingPageTonalElevationPreference.default }
val LocalReadingAutoHideToolbar =
compositionLocalOf<ReadingAutoHideToolbarPreference> { ReadingAutoHideToolbarPreference.default }
val LocalReadingTextFontSize = compositionLocalOf { ReadingTextFontSizePreference.default } val LocalReadingTextFontSize = compositionLocalOf { ReadingTextFontSizePreference.default }
val LocalReadingLetterSpacing = compositionLocalOf { ReadingLetterSpacingPreference.default } val LocalReadingLetterSpacing = compositionLocalOf { ReadingLetterSpacingPreference.default }
val LocalReadingTextHorizontalPadding = compositionLocalOf { ReadingTextHorizontalPaddingPreference.default } val LocalReadingTextHorizontalPadding = compositionLocalOf { ReadingTextHorizontalPaddingPreference.default }
@ -161,7 +165,8 @@ val LocalReadingSubheadUpperCase =
compositionLocalOf<ReadingSubheadUpperCasePreference> { ReadingSubheadUpperCasePreference.default } compositionLocalOf<ReadingSubheadUpperCasePreference> { ReadingSubheadUpperCasePreference.default }
val LocalReadingImageHorizontalPadding = compositionLocalOf { ReadingImageHorizontalPaddingPreference.default } val LocalReadingImageHorizontalPadding = compositionLocalOf { ReadingImageHorizontalPaddingPreference.default }
val LocalReadingImageRoundedCorners = compositionLocalOf { ReadingImageRoundedCornersPreference.default } val LocalReadingImageRoundedCorners = compositionLocalOf { ReadingImageRoundedCornersPreference.default }
val LocalReadingImageMaximize = compositionLocalOf<ReadingImageMaximizePreference> { ReadingImageMaximizePreference.default } val LocalReadingImageMaximize =
compositionLocalOf<ReadingImageMaximizePreference> { ReadingImageMaximizePreference.default }
// Interaction // Interaction
val LocalInitialPage = compositionLocalOf<InitialPagePreference> { InitialPagePreference.default } val LocalInitialPage = compositionLocalOf<InitialPagePreference> { InitialPagePreference.default }
@ -192,12 +197,14 @@ fun SettingsProvider(
LocalNewVersionLog provides settings.newVersionLog, LocalNewVersionLog provides settings.newVersionLog,
LocalNewVersionSize provides settings.newVersionSize, LocalNewVersionSize provides settings.newVersionSize,
LocalNewVersionDownloadUrl provides settings.newVersionDownloadUrl, LocalNewVersionDownloadUrl provides settings.newVersionDownloadUrl,
LocalBasicFonts provides settings.basicFonts,
// Theme // Theme
LocalThemeIndex provides settings.themeIndex, LocalThemeIndex provides settings.themeIndex,
LocalCustomPrimaryColor provides settings.customPrimaryColor, LocalCustomPrimaryColor provides settings.customPrimaryColor,
LocalDarkTheme provides settings.darkTheme, LocalDarkTheme provides settings.darkTheme,
LocalAmoledDarkTheme provides settings.amoledDarkTheme, LocalAmoledDarkTheme provides settings.amoledDarkTheme,
LocalBasicFonts provides settings.basicFonts,
// Feeds page // Feeds page
LocalFeedsTopBarTonalElevation provides settings.feedsTopBarTonalElevation, LocalFeedsTopBarTonalElevation provides settings.feedsTopBarTonalElevation,

View File

@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
@ -79,7 +80,7 @@ fun bodyForeground(): Color = onSurfaceVariantColor()
@ReadOnlyComposable @ReadOnlyComposable
fun bodyStyle(): TextStyle = fun bodyStyle(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontWeight = if (LocalReadingTextBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (LocalReadingTextBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
fontSize = LocalReadingTextFontSize.current.sp, fontSize = LocalReadingTextFontSize.current.sp,
letterSpacing = LocalReadingLetterSpacing.current.sp, letterSpacing = LocalReadingLetterSpacing.current.sp,
@ -92,7 +93,7 @@ fun bodyStyle(): TextStyle =
@ReadOnlyComposable @ReadOnlyComposable
fun h1Style(): TextStyle = fun h1Style(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
fontSize = 28.sp, fontSize = 28.sp,
letterSpacing = 0.sp, letterSpacing = 0.sp,
@ -105,7 +106,7 @@ fun h1Style(): TextStyle =
@ReadOnlyComposable @ReadOnlyComposable
fun h2Style(): TextStyle = fun h2Style(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
fontSize = 28.sp, fontSize = 28.sp,
letterSpacing = 0.sp, letterSpacing = 0.sp,
@ -118,7 +119,7 @@ fun h2Style(): TextStyle =
@ReadOnlyComposable @ReadOnlyComposable
fun h3Style(): TextStyle = fun h3Style(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
fontSize = 19.sp, fontSize = 19.sp,
letterSpacing = 0.sp, letterSpacing = 0.sp,
@ -131,7 +132,7 @@ fun h3Style(): TextStyle =
@ReadOnlyComposable @ReadOnlyComposable
fun h4Style(): TextStyle = fun h4Style(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
fontSize = 17.sp, fontSize = 17.sp,
letterSpacing = 0.sp, letterSpacing = 0.sp,
@ -144,7 +145,7 @@ fun h4Style(): TextStyle =
@ReadOnlyComposable @ReadOnlyComposable
fun h5Style(): TextStyle = fun h5Style(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
fontSize = 17.sp, fontSize = 17.sp,
letterSpacing = 0.sp, letterSpacing = 0.sp,
@ -157,7 +158,7 @@ fun h5Style(): TextStyle =
@ReadOnlyComposable @ReadOnlyComposable
fun h6Style(): TextStyle = fun h6Style(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
fontSize = 17.sp, fontSize = 17.sp,
letterSpacing = 0.sp, letterSpacing = 0.sp,
@ -171,7 +172,7 @@ fun h6Style(): TextStyle =
fun captionStyle(): TextStyle = fun captionStyle(): TextStyle =
MaterialTheme.typography.bodySmall.merge( MaterialTheme.typography.bodySmall.merge(
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
color = bodyForeground().copy(alpha = 0.6f), color = bodyForeground().copy(alpha = 0.6f),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
@ -182,7 +183,7 @@ fun captionStyle(): TextStyle =
@ReadOnlyComposable @ReadOnlyComposable
fun linkTextStyle(): TextStyle = fun linkTextStyle(): TextStyle =
TextStyle( TextStyle(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current),
fontSize = LocalReadingTextFontSize.current.sp, fontSize = LocalReadingTextFontSize.current.sp,
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
textDecoration = TextDecoration.Underline, textDecoration = TextDecoration.Underline,

View File

@ -148,6 +148,12 @@ sealed class DataStoreKeys<T> {
get() = booleanPreferencesKey("amoledDarkTheme") get() = booleanPreferencesKey("amoledDarkTheme")
} }
object BasicFonts : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("basicFonts")
}
// Feeds page // Feeds page
object FeedsFilterBarStyle : DataStoreKeys<Int>() { object FeedsFilterBarStyle : DataStoreKeys<Int>() {

View File

@ -0,0 +1,117 @@
package me.ash.reader.ui.ext
import android.content.Context
import android.graphics.Typeface
import android.net.Uri
import androidx.compose.material3.Typography
import androidx.compose.ui.text.font.FontFamily
import me.ash.reader.ui.theme.SystemTypography
import java.io.File
class ExternalFonts(
private val ctx: Context,
private val uri: Uri,
private val type: FontType,
) {
enum class FontType(val value: String) {
BasicFont("basic_font.ttf"),
ReadingFont("reading_font.ttf"),
;
fun toPath(ctx: Context): String = ctx.filesDir.absolutePath + File.separator + value
}
private lateinit var fontByteArray: ByteArray
init {
ctx.contentResolver.openInputStream(uri)?.use { inputStream ->
fontByteArray = inputStream.readBytes()
// File(inputStream.readString()).let {
// if (!it.exists()) throw IllegalArgumentException("Invalid path")
// if (!it.isFile) throw IllegalArgumentException("Invalid path")
// if (it.extension.lowercase() != "ttf") throw IllegalArgumentException("Only *.ttf fonts are supported")
// fontByteArray = it
// }
}
}
fun copyToInternalStorage() {
File(type.toPath(ctx)).let {
if (it.exists()) it.delete()
if (it.createNewFile()) it.writeBytes(fontByteArray)
}
}
companion object {
fun loadBasicTypography(ctx: Context): Typography = loadTypography(ctx, FontType.BasicFont)
fun loadReadingTypography(ctx: Context): Typography = loadTypography(ctx, FontType.ReadingFont)
private var basicTypography: Typography? = null
private var readingTypography: Typography? = null
private fun createFontFamily(ctx: Context, type: FontType): FontFamily =
File(type.toPath(ctx)).takeIf { it.exists() }
?.run { FontFamily(Typeface.createFromFile(this)) } ?: FontFamily.Default
private fun createTypography(fontFamily: FontFamily): Typography =
Typography(
displayLarge = SystemTypography.displayLarge.copy(
fontFamily = fontFamily,
),
displayMedium = SystemTypography.displayMedium.copy(
fontFamily = fontFamily,
),
displaySmall = SystemTypography.displaySmall.copy(
fontFamily = fontFamily,
),
headlineLarge = SystemTypography.headlineLarge.copy(
fontFamily = fontFamily
),
headlineMedium = SystemTypography.headlineMedium.copy(
fontFamily = fontFamily
),
headlineSmall = SystemTypography.headlineSmall.copy(
fontFamily = fontFamily
),
titleLarge = SystemTypography.titleLarge.copy(
fontFamily = fontFamily
),
titleMedium = SystemTypography.titleMedium.copy(
fontFamily = fontFamily
),
titleSmall = SystemTypography.titleSmall.copy(
fontFamily = fontFamily
),
labelLarge = SystemTypography.labelLarge.copy(
fontFamily = fontFamily
),
bodyLarge = SystemTypography.bodyLarge.copy(
fontFamily = fontFamily
),
bodyMedium = SystemTypography.bodyMedium.copy(
fontFamily = fontFamily
),
bodySmall = SystemTypography.bodySmall.copy(
fontFamily = fontFamily
),
labelMedium = SystemTypography.labelMedium.copy(
fontFamily = fontFamily
),
labelSmall = SystemTypography.labelSmall.copy(
fontFamily = fontFamily
),
)
private fun loadTypography(ctx: Context, type: FontType): Typography =
when (type) {
FontType.BasicFont -> basicTypography ?: createTypography(createFontFamily(ctx, type))
.also { basicTypography = it }
FontType.ReadingFont -> readingTypography ?: createTypography(createFontFamily(ctx, type))
.also { readingTypography = it }
}
}
}

View File

@ -47,7 +47,7 @@ fun SubscribeDialog(
val groupsState = subscribeUiState.groups.collectAsState(initial = emptyList()) val groupsState = subscribeUiState.groups.collectAsState(initial = emptyList())
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
it?.let { uri -> it?.let { uri ->
context.contentResolver.openInputStream(uri)?.let { inputStream -> context.contentResolver.openInputStream(uri)?.use { inputStream ->
subscribeViewModel.importFromInputStream(inputStream) subscribeViewModel.importFromInputStream(inputStream)
} }
} }

View File

@ -54,7 +54,7 @@ fun Metadata(
text = dateString, text = dateString,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
style = MaterialTheme.typography.labelMedium.copy( style = MaterialTheme.typography.labelMedium.copy(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(context),
), ),
textAlign = titleAlign.toTextAlign(), textAlign = titleAlign.toTextAlign(),
) )
@ -64,7 +64,7 @@ fun Metadata(
text = if (titleUpperCase.value) titleUpperCaseString else title, text = if (titleUpperCase.value) titleUpperCaseString else title,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.headlineLarge.copy( style = MaterialTheme.typography.headlineLarge.copy(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(context),
fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal,
), ),
textAlign = titleAlign.toTextAlign(), textAlign = titleAlign.toTextAlign(),
@ -79,7 +79,7 @@ fun Metadata(
text = it, text = it,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
style = MaterialTheme.typography.labelMedium.copy( style = MaterialTheme.typography.labelMedium.copy(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(context),
), ),
textAlign = titleAlign.toTextAlign(), textAlign = titleAlign.toTextAlign(),
) )
@ -92,7 +92,7 @@ fun Metadata(
text = feedName, text = feedName,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
style = MaterialTheme.typography.labelMedium.copy( style = MaterialTheme.typography.labelMedium.copy(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(context),
), ),
textAlign = titleAlign.toTextAlign(), textAlign = titleAlign.toTextAlign(),
) )

View File

@ -2,6 +2,8 @@ package me.ash.reader.ui.page.settings.color
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -25,11 +27,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import me.ash.reader.R import me.ash.reader.R
import me.ash.reader.data.model.preference.* import me.ash.reader.data.model.preference.*
import me.ash.reader.ui.component.base.* import me.ash.reader.ui.component.base.*
import me.ash.reader.ui.ext.ExternalFonts
import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.page.settings.SettingItem
import me.ash.reader.ui.svg.PALETTE import me.ash.reader.ui.svg.PALETTE
@ -47,10 +51,19 @@ fun ColorAndStylePage(
val darkThemeNot = !darkTheme val darkThemeNot = !darkTheme
val themeIndex = LocalThemeIndex.current val themeIndex = LocalThemeIndex.current
val customPrimaryColor = LocalCustomPrimaryColor.current val customPrimaryColor = LocalCustomPrimaryColor.current
val fonts = LocalBasicFonts.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper() val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper()
var radioButtonSelected by remember { mutableStateOf(if (themeIndex > 4) 0 else 1) } var radioButtonSelected by remember { mutableStateOf(if (themeIndex > 4) 0 else 1) }
var fontsDialogVisible by remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let {
ExternalFonts(context, it, ExternalFonts.FontType.BasicFont).copyToInternalStorage()
BasicFontsPreference.External.put(context, scope)
}
}
RYScaffold( RYScaffold(
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
@ -152,9 +165,8 @@ fun ColorAndStylePage(
} }
SettingItem( SettingItem(
title = stringResource(R.string.basic_fonts), title = stringResource(R.string.basic_fonts),
desc = stringResource(R.string.system_default), desc = fonts.toDesc(context),
enable = false, onClick = { fontsDialogVisible = true },
onClick = {},
) {} ) {}
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
} }
@ -195,6 +207,26 @@ fun ColorAndStylePage(
} }
} }
) )
RadioDialog(
visible = fontsDialogVisible,
title = stringResource(R.string.basic_fonts),
options = BasicFontsPreference.values.map {
RadioDialogOption(
text = it.toDesc(context),
style = TextStyle(fontFamily = it.asFontFamily(context)),
selected = it == fonts,
) {
if (it.value == BasicFontsPreference.External.value) {
launcher.launch("*/*")
} else {
it.put(context, scope)
}
}
}
) {
fontsDialogVisible = false
}
} }
@Composable @Composable

View File

@ -1,5 +1,7 @@
package me.ash.reader.ui.page.settings.color.reading package me.ash.reader.ui.page.settings.color.reading
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
@ -27,6 +29,7 @@ import me.ash.reader.R
import me.ash.reader.data.model.preference.* import me.ash.reader.data.model.preference.*
import me.ash.reader.ui.component.ReadingThemePrev import me.ash.reader.ui.component.ReadingThemePrev
import me.ash.reader.ui.component.base.* import me.ash.reader.ui.component.base.*
import me.ash.reader.ui.ext.ExternalFonts
import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.page.settings.SettingItem
import me.ash.reader.ui.theme.palette.onLight import me.ash.reader.ui.theme.palette.onLight
@ -48,6 +51,13 @@ fun ReadingStylePage(
var tonalElevationDialogVisible by remember { mutableStateOf(false) } var tonalElevationDialogVisible by remember { mutableStateOf(false) }
var fontsDialogVisible by remember { mutableStateOf(false) } var fontsDialogVisible by remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let {
ExternalFonts(context, it, ExternalFonts.FontType.ReadingFont).copyToInternalStorage()
ReadingFontsPreference.External.put(context, scope)
}
}
RYScaffold( RYScaffold(
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
navigationIcon = { navigationIcon = {
@ -247,10 +257,14 @@ fun ReadingStylePage(
options = ReadingFontsPreference.values.map { options = ReadingFontsPreference.values.map {
RadioDialogOption( RadioDialogOption(
text = it.toDesc(context), text = it.toDesc(context),
style = TextStyle(fontFamily = it.asFontFamily()), style = TextStyle(fontFamily = it.asFontFamily(context)),
selected = it == fonts, selected = it == fonts,
) { ) {
it.put(context, scope) if (it.value == ReadingFontsPreference.External.value) {
launcher.launch("*/*")
} else {
it.put(context, scope)
}
} }
} }
) { ) {

View File

@ -48,7 +48,7 @@ fun TitleAndTextPreview() {
text = if (titleUpperCase.value) titleUpperCaseString else stringResource(id = R.string.title), text = if (titleUpperCase.value) titleUpperCaseString else stringResource(id = R.string.title),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.headlineLarge.copy( style = MaterialTheme.typography.headlineLarge.copy(
fontFamily = LocalReadingFonts.current.asFontFamily(), fontFamily = LocalReadingFonts.current.asFontFamily(context),
fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal,
), ),
textAlign = titleAlign.toTextAlign(), textAlign = titleAlign.toTextAlign(),

View File

@ -2,19 +2,15 @@ package me.ash.reader.ui.theme
import androidx.compose.material3.Typography import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import me.ash.reader.R
val AppTypography = Typography( val SystemTypography = Typography(
displayLarge = TextStyle( displayLarge = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.W400,
fontSize = 57.sp, fontSize = 57.sp,
lineHeight = 64.sp, lineHeight = 64.sp,
letterSpacing = -0.25.sp, letterSpacing = (-0.25).sp,
), ),
displayMedium = TextStyle( displayMedium = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.W400,

View File

@ -3,6 +3,8 @@ package me.ash.reader.ui.theme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import me.ash.reader.data.model.preference.LocalBasicFonts
import me.ash.reader.data.model.preference.LocalThemeIndex import me.ash.reader.data.model.preference.LocalThemeIndex
import me.ash.reader.ui.theme.palette.LocalTonalPalettes import me.ash.reader.ui.theme.palette.LocalTonalPalettes
import me.ash.reader.ui.theme.palette.TonalPalettes import me.ash.reader.ui.theme.palette.TonalPalettes
@ -20,26 +22,26 @@ fun AppTheme(
val themeIndex = LocalThemeIndex.current val themeIndex = LocalThemeIndex.current
val tonalPalettes = wallpaperPalettes[ val tonalPalettes = wallpaperPalettes[
if (themeIndex >= wallpaperPalettes.size) { if (themeIndex >= wallpaperPalettes.size) {
when { when {
wallpaperPalettes.size == 5 -> 0 wallpaperPalettes.size == 5 -> 0
wallpaperPalettes.size > 5 -> 5 wallpaperPalettes.size > 5 -> 5
else -> 0 else -> 0
}
} else {
themeIndex
} }
} else {
themeIndex
}
] ]
ProvideZcamViewingConditions { ProvideZcamViewingConditions {
CompositionLocalProvider( CompositionLocalProvider(
LocalTonalPalettes provides tonalPalettes.apply { Preheating() }, LocalTonalPalettes provides tonalPalettes.apply { Preparing() },
) { ) {
MaterialTheme( MaterialTheme(
colorScheme = colorScheme =
if (useDarkTheme) dynamicDarkColorScheme() if (useDarkTheme) dynamicDarkColorScheme()
else dynamicLightColorScheme(), else dynamicLightColorScheme(),
typography = AppTypography, typography = LocalBasicFonts.current.asTypography(LocalContext.current),
shapes = Shapes, shapes = Shapes,
content = content, content = content,
) )

View File

@ -106,7 +106,7 @@ data class TonalPalettes(
} }
@Composable @Composable
fun Preheating() { fun Preparing() {
tonalTokens.forEach { primary(it) } tonalTokens.forEach { primary(it) }
tonalTokens.forEach { secondary(it) } tonalTokens.forEach { secondary(it) }
tonalTokens.forEach { tertiary(it) } tonalTokens.forEach { tertiary(it) }

View File

@ -390,5 +390,5 @@
<string name="username">Username</string> <string name="username">Username</string>
<string name="password">Password</string> <string name="password">Password</string>
<string name="connection">Connection</string> <string name="connection">Connection</string>
<string name="system_default">System Default</string> <string name="system_default">System</string>
</resources> </resources>