From 4b04f96b4c4739d2e9997cd46ea14ea9c426f3b1 Mon Sep 17 00:00:00 2001 From: Ash Date: Thu, 2 Feb 2023 20:36:37 +0800 Subject: [PATCH] Support using external fonts as basic and reading fonts (#333) --- README-de.md | 2 + README-zh-CN.md | 2 + README.md | 2 + .../model/preference/BasicFontsPreference.kt | 56 +++++++++ .../data/model/preference/Preference.kt | 1 + .../preference/ReadingFontsPreference.kt | 5 +- .../reader/data/model/preference/Settings.kt | 13 +- .../ash/reader/ui/component/reader/Styles.kt | 19 +-- .../java/me/ash/reader/ui/ext/DataStoreExt.kt | 6 + .../me/ash/reader/ui/ext/ExternalFonts.kt | 117 ++++++++++++++++++ .../home/feeds/subscribe/SubscribeDialog.kt | 2 +- .../reader/ui/page/home/reading/Metadata.kt | 8 +- .../page/settings/color/ColorAndStylePage.kt | 38 +++++- .../color/reading/ReadingStylePage.kt | 18 ++- .../color/reading/TitleAndTextPreview.kt | 2 +- .../ui/theme/{Type.kt => SystemTypography.kt} | 8 +- .../main/java/me/ash/reader/ui/theme/Theme.kt | 22 ++-- .../reader/ui/theme/palette/TonalPalettes.kt | 2 +- app/src/main/res/values/strings.xml | 2 +- 19 files changed, 282 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/data/model/preference/BasicFontsPreference.kt create mode 100644 app/src/main/java/me/ash/reader/ui/ext/ExternalFonts.kt rename app/src/main/java/me/ash/reader/ui/theme/{Type.kt => SystemTypography.kt} (91%) diff --git a/README-de.md b/README-de.md index af965cfc..25814d26 100644 --- a/README-de.md +++ b/README-de.md @@ -57,6 +57,8 @@ Nachfolgend sind die bisher erzielten Fortschritte und die Ziele aufgeführt, an - [ ] Android Widget - [ ] ... +[Was mache ich gerade?](https://github.com/users/Ashinch/projects/2) + ## Integration **Read You** ist mit einigen APIs von Drittanbietern kompatibel, um bei der Nutzung Ihrer bestehenden Cloud-Konten als Datenquellen zu unterstützen. diff --git a/README-zh-CN.md b/README-zh-CN.md index ee1b1aab..91704817 100644 --- a/README-zh-CN.md +++ b/README-zh-CN.md @@ -55,6 +55,8 @@ - [ ] Android 微件 / 小组件 - [ ] ... +[我目前在做什么?](https://github.com/users/Ashinch/projects/2) + ## 集成 **Read You** 也集成了一些第三方服务 API,支持您使用已有的云端账户来作为数据源。 diff --git a/README.md b/README.md index 5d5988e4..c65bf77f 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ The following are the progress made so far and the goals to be worked on in the - [ ] Android widget - [ ] ... +[What am I doing now?](https://github.com/users/Ashinch/projects/2) + ## Integration **Read You** integrates with some of third-party service APIs to support you in using your existing cloud accounts as data sources. diff --git a/app/src/main/java/me/ash/reader/data/model/preference/BasicFontsPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/BasicFontsPreference.kt new file mode 100644 index 00000000..64f4b6e7 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/model/preference/BasicFontsPreference.kt @@ -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 + } + } +} diff --git a/app/src/main/java/me/ash/reader/data/model/preference/Preference.kt b/app/src/main/java/me/ash/reader/data/model/preference/Preference.kt index 9eaf6a34..861c90aa 100644 --- a/app/src/main/java/me/ash/reader/data/model/preference/Preference.kt +++ b/app/src/main/java/me/ash/reader/data/model/preference/Preference.kt @@ -24,6 +24,7 @@ fun Preferences.toSettings(): Settings { customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this), darkTheme = DarkThemePreference.fromPreferences(this), amoledDarkTheme = AmoledDarkThemePreference.fromPreferences(this), + basicFonts = BasicFontsPreference.fromPreferences(this), // Feeds page feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this), diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingFontsPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingFontsPreference.kt index b82550d6..b991f1d8 100644 --- a/app/src/main/java/me/ash/reader/data/model/preference/ReadingFontsPreference.kt +++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingFontsPreference.kt @@ -7,6 +7,7 @@ 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 @@ -34,14 +35,14 @@ sealed class ReadingFontsPreference(val value: Int) : Preference() { External -> context.getString(R.string.external_fonts) } - fun asFontFamily(): FontFamily = + fun asFontFamily(context: Context): FontFamily = when (this) { System -> FontFamily.Default Serif -> FontFamily.Serif SansSerif -> FontFamily.SansSerif Monospace -> FontFamily.Monospace Cursive -> FontFamily.Cursive - External -> FontFamily.Default + External -> ExternalFonts.loadReadingTypography(context).displayLarge.fontFamily ?: FontFamily.Default } companion object { diff --git a/app/src/main/java/me/ash/reader/data/model/preference/Settings.kt b/app/src/main/java/me/ash/reader/data/model/preference/Settings.kt index b8305a7b..11d05fda 100644 --- a/app/src/main/java/me/ash/reader/data/model/preference/Settings.kt +++ b/app/src/main/java/me/ash/reader/data/model/preference/Settings.kt @@ -25,6 +25,7 @@ data class Settings( val customPrimaryColor: String = CustomPrimaryColorPreference.default, val darkTheme: DarkThemePreference = DarkThemePreference.default, val amoledDarkTheme: AmoledDarkThemePreference = AmoledDarkThemePreference.default, + val basicFonts: BasicFontsPreference = BasicFontsPreference.default, // Feeds page val feedsFilterBarStyle: FeedsFilterBarStylePreference = FeedsFilterBarStylePreference.default, @@ -95,6 +96,7 @@ val LocalDarkTheme = compositionLocalOf { DarkThemePreference.default } val LocalAmoledDarkTheme = compositionLocalOf { AmoledDarkThemePreference.default } +val LocalBasicFonts = compositionLocalOf { BasicFontsPreference.default } // Feeds page val LocalFeedsFilterBarStyle = @@ -141,8 +143,10 @@ val LocalFlowArticleListTonalElevation = // Reading page val LocalReadingTheme = compositionLocalOf { ReadingThemePreference.default } val LocalReadingDarkTheme = compositionLocalOf { ReadingDarkThemePreference.default } -val LocalReadingPageTonalElevation = compositionLocalOf { ReadingPageTonalElevationPreference.default } -val LocalReadingAutoHideToolbar = compositionLocalOf { ReadingAutoHideToolbarPreference.default } +val LocalReadingPageTonalElevation = + compositionLocalOf { ReadingPageTonalElevationPreference.default } +val LocalReadingAutoHideToolbar = + compositionLocalOf { ReadingAutoHideToolbarPreference.default } val LocalReadingTextFontSize = compositionLocalOf { ReadingTextFontSizePreference.default } val LocalReadingLetterSpacing = compositionLocalOf { ReadingLetterSpacingPreference.default } val LocalReadingTextHorizontalPadding = compositionLocalOf { ReadingTextHorizontalPaddingPreference.default } @@ -161,7 +165,8 @@ val LocalReadingSubheadUpperCase = compositionLocalOf { ReadingSubheadUpperCasePreference.default } val LocalReadingImageHorizontalPadding = compositionLocalOf { ReadingImageHorizontalPaddingPreference.default } val LocalReadingImageRoundedCorners = compositionLocalOf { ReadingImageRoundedCornersPreference.default } -val LocalReadingImageMaximize = compositionLocalOf { ReadingImageMaximizePreference.default } +val LocalReadingImageMaximize = + compositionLocalOf { ReadingImageMaximizePreference.default } // Interaction val LocalInitialPage = compositionLocalOf { InitialPagePreference.default } @@ -192,12 +197,14 @@ fun SettingsProvider( LocalNewVersionLog provides settings.newVersionLog, LocalNewVersionSize provides settings.newVersionSize, LocalNewVersionDownloadUrl provides settings.newVersionDownloadUrl, + LocalBasicFonts provides settings.basicFonts, // Theme LocalThemeIndex provides settings.themeIndex, LocalCustomPrimaryColor provides settings.customPrimaryColor, LocalDarkTheme provides settings.darkTheme, LocalAmoledDarkTheme provides settings.amoledDarkTheme, + LocalBasicFonts provides settings.basicFonts, // Feeds page LocalFeedsTopBarTonalElevation provides settings.feedsTopBarTonalElevation, diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt b/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt index e1a337b1..2d172d78 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.Stable import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily @@ -79,7 +80,7 @@ fun bodyForeground(): Color = onSurfaceVariantColor() @ReadOnlyComposable fun bodyStyle(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontWeight = if (LocalReadingTextBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontSize = LocalReadingTextFontSize.current.sp, letterSpacing = LocalReadingLetterSpacing.current.sp, @@ -92,7 +93,7 @@ fun bodyStyle(): TextStyle = @ReadOnlyComposable fun h1Style(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontSize = 28.sp, letterSpacing = 0.sp, @@ -105,7 +106,7 @@ fun h1Style(): TextStyle = @ReadOnlyComposable fun h2Style(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontSize = 28.sp, letterSpacing = 0.sp, @@ -118,7 +119,7 @@ fun h2Style(): TextStyle = @ReadOnlyComposable fun h3Style(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontSize = 19.sp, letterSpacing = 0.sp, @@ -131,7 +132,7 @@ fun h3Style(): TextStyle = @ReadOnlyComposable fun h4Style(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontSize = 17.sp, letterSpacing = 0.sp, @@ -144,7 +145,7 @@ fun h4Style(): TextStyle = @ReadOnlyComposable fun h5Style(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontSize = 17.sp, letterSpacing = 0.sp, @@ -157,7 +158,7 @@ fun h5Style(): TextStyle = @ReadOnlyComposable fun h6Style(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal, fontSize = 17.sp, letterSpacing = 0.sp, @@ -171,7 +172,7 @@ fun h6Style(): TextStyle = fun captionStyle(): TextStyle = MaterialTheme.typography.bodySmall.merge( TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), color = bodyForeground().copy(alpha = 0.6f), textAlign = TextAlign.Center, ) @@ -182,7 +183,7 @@ fun captionStyle(): TextStyle = @ReadOnlyComposable fun linkTextStyle(): TextStyle = TextStyle( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(LocalContext.current), fontSize = LocalReadingTextFontSize.current.sp, color = MaterialTheme.colorScheme.primary, textDecoration = TextDecoration.Underline, diff --git a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt index c62c0b33..f9708b6d 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt @@ -148,6 +148,12 @@ sealed class DataStoreKeys { get() = booleanPreferencesKey("amoledDarkTheme") } + object BasicFonts : DataStoreKeys() { + + override val key: Preferences.Key + get() = intPreferencesKey("basicFonts") + } + // Feeds page object FeedsFilterBarStyle : DataStoreKeys() { diff --git a/app/src/main/java/me/ash/reader/ui/ext/ExternalFonts.kt b/app/src/main/java/me/ash/reader/ui/ext/ExternalFonts.kt new file mode 100644 index 00000000..91aa4174 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/ext/ExternalFonts.kt @@ -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 } + } + } +} diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt index 619c5e42..23c29897 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt @@ -47,7 +47,7 @@ fun SubscribeDialog( val groupsState = subscribeUiState.groups.collectAsState(initial = emptyList()) val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { it?.let { uri -> - context.contentResolver.openInputStream(uri)?.let { inputStream -> + context.contentResolver.openInputStream(uri)?.use { inputStream -> subscribeViewModel.importFromInputStream(inputStream) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/Metadata.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/Metadata.kt index ec7ea1e8..6040dd61 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/Metadata.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/Metadata.kt @@ -54,7 +54,7 @@ fun Metadata( text = dateString, color = MaterialTheme.colorScheme.outline, style = MaterialTheme.typography.labelMedium.copy( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(context), ), textAlign = titleAlign.toTextAlign(), ) @@ -64,7 +64,7 @@ fun Metadata( text = if (titleUpperCase.value) titleUpperCaseString else title, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.headlineLarge.copy( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(context), fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal, ), textAlign = titleAlign.toTextAlign(), @@ -79,7 +79,7 @@ fun Metadata( text = it, color = MaterialTheme.colorScheme.outline, style = MaterialTheme.typography.labelMedium.copy( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(context), ), textAlign = titleAlign.toTextAlign(), ) @@ -92,7 +92,7 @@ fun Metadata( text = feedName, color = MaterialTheme.colorScheme.outline, style = MaterialTheme.typography.labelMedium.copy( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(context), ), textAlign = titleAlign.toTextAlign(), ) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt index b8b5d6e2..c05abe47 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt @@ -2,6 +2,8 @@ package me.ash.reader.ui.page.settings.color import android.content.Context import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.* import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -25,11 +27,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.data.model.preference.* 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.settings.SettingItem import me.ash.reader.ui.svg.PALETTE @@ -47,10 +51,19 @@ fun ColorAndStylePage( val darkThemeNot = !darkTheme val themeIndex = LocalThemeIndex.current val customPrimaryColor = LocalCustomPrimaryColor.current + val fonts = LocalBasicFonts.current val scope = rememberCoroutineScope() val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper() 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( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, @@ -152,9 +165,8 @@ fun ColorAndStylePage( } SettingItem( title = stringResource(R.string.basic_fonts), - desc = stringResource(R.string.system_default), - enable = false, - onClick = {}, + desc = fonts.toDesc(context), + onClick = { fontsDialogVisible = true }, ) {} 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 diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt index e3fb5f73..73c606d2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt @@ -1,5 +1,7 @@ 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.clickable 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.ui.component.ReadingThemePrev 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.settings.SettingItem import me.ash.reader.ui.theme.palette.onLight @@ -48,6 +51,13 @@ fun ReadingStylePage( var tonalElevationDialogVisible 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( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { @@ -247,10 +257,14 @@ fun ReadingStylePage( options = ReadingFontsPreference.values.map { RadioDialogOption( text = it.toDesc(context), - style = TextStyle(fontFamily = it.asFontFamily()), + style = TextStyle(fontFamily = it.asFontFamily(context)), selected = it == fonts, ) { - it.put(context, scope) + if (it.value == ReadingFontsPreference.External.value) { + launcher.launch("*/*") + } else { + it.put(context, scope) + } } } ) { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/TitleAndTextPreview.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/TitleAndTextPreview.kt index 698f88f1..b2d8795f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/TitleAndTextPreview.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/TitleAndTextPreview.kt @@ -48,7 +48,7 @@ fun TitleAndTextPreview() { text = if (titleUpperCase.value) titleUpperCaseString else stringResource(id = R.string.title), color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.headlineLarge.copy( - fontFamily = LocalReadingFonts.current.asFontFamily(), + fontFamily = LocalReadingFonts.current.asFontFamily(context), fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal, ), textAlign = titleAlign.toTextAlign(), diff --git a/app/src/main/java/me/ash/reader/ui/theme/Type.kt b/app/src/main/java/me/ash/reader/ui/theme/SystemTypography.kt similarity index 91% rename from app/src/main/java/me/ash/reader/ui/theme/Type.kt rename to app/src/main/java/me/ash/reader/ui/theme/SystemTypography.kt index 24e424f1..fc8efe4e 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/Type.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/SystemTypography.kt @@ -2,19 +2,15 @@ package me.ash.reader.ui.theme import androidx.compose.material3.Typography 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.unit.sp -import me.ash.reader.R -val AppTypography = Typography( +val SystemTypography = Typography( displayLarge = TextStyle( fontWeight = FontWeight.W400, fontSize = 57.sp, lineHeight = 64.sp, - letterSpacing = -0.25.sp, + letterSpacing = (-0.25).sp, ), displayMedium = TextStyle( fontWeight = FontWeight.W400, diff --git a/app/src/main/java/me/ash/reader/ui/theme/Theme.kt b/app/src/main/java/me/ash/reader/ui/theme/Theme.kt index da6e5481..d08f3831 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/Theme.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/Theme.kt @@ -3,6 +3,8 @@ package me.ash.reader.ui.theme import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable 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.ui.theme.palette.LocalTonalPalettes import me.ash.reader.ui.theme.palette.TonalPalettes @@ -20,26 +22,26 @@ fun AppTheme( val themeIndex = LocalThemeIndex.current val tonalPalettes = wallpaperPalettes[ - if (themeIndex >= wallpaperPalettes.size) { - when { - wallpaperPalettes.size == 5 -> 0 - wallpaperPalettes.size > 5 -> 5 - else -> 0 - } - } else { - themeIndex + if (themeIndex >= wallpaperPalettes.size) { + when { + wallpaperPalettes.size == 5 -> 0 + wallpaperPalettes.size > 5 -> 5 + else -> 0 } + } else { + themeIndex + } ] ProvideZcamViewingConditions { CompositionLocalProvider( - LocalTonalPalettes provides tonalPalettes.apply { Preheating() }, + LocalTonalPalettes provides tonalPalettes.apply { Preparing() }, ) { MaterialTheme( colorScheme = if (useDarkTheme) dynamicDarkColorScheme() else dynamicLightColorScheme(), - typography = AppTypography, + typography = LocalBasicFonts.current.asTypography(LocalContext.current), shapes = Shapes, content = content, ) diff --git a/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt b/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt index 8c4854f5..01e8dc74 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt @@ -106,7 +106,7 @@ data class TonalPalettes( } @Composable - fun Preheating() { + fun Preparing() { tonalTokens.forEach { primary(it) } tonalTokens.forEach { secondary(it) } tonalTokens.forEach { tertiary(it) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fb6c52ae..c6719dac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -390,5 +390,5 @@ Username Password Connection - System Default + System