diff --git a/README-de.md b/README-de.md
index a60df5b..cb34047 100644
--- a/README-de.md
+++ b/README-de.md
@@ -66,6 +66,14 @@ Nachfolgend sind die bisher erzielten Fortschritte und die Ziele aufgeführt, an
> Bei den oben genannten Funktionen handelt es sich nur um vorläufige Implementierungen, und es können noch unbekannte Probleme auftreten.
+## Herunterladen
+
+[](https://f-droid.org/packages/me.ash.reader/)
+
+oder hole die APK aus dem [GitHub Releasebereich](https://github.com/Ashinch/ReadYou/releases).
+
## Build
> Wenn Sie eine Vorschau der Read You App wollen, können Sie die **Vorschau-Version** der APK-Datei in [Telegram] (https://t.me/ReadYouApp) herunterladen.
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/LanguagesPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/LanguagesPreference.kt
index 422caf9..eb757e5 100644
--- a/app/src/main/java/me/ash/reader/data/model/preference/LanguagesPreference.kt
+++ b/app/src/main/java/me/ash/reader/data/model/preference/LanguagesPreference.kt
@@ -20,6 +20,8 @@ sealed class LanguagesPreference(val value: Int) : Preference() {
object Czech : LanguagesPreference(5)
object Italian : LanguagesPreference(6)
+ object Hindi : LanguagesPreference(7)
+
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
context.dataStore.put(
@@ -39,6 +41,7 @@ sealed class LanguagesPreference(val value: Int) : Preference() {
French -> context.getString(R.string.french)
Czech -> context.getString(R.string.czech)
Italian -> context.getString(R.string.italian)
+ Hindi -> context.getString(R.string.hindi)
}
fun getLocale(): Locale =
@@ -50,6 +53,7 @@ sealed class LanguagesPreference(val value: Int) : Preference() {
French -> Locale("fr", "FR")
Czech -> Locale("cs", "CZ")
Italian -> Locale("it", "IT")
+ Hindi -> Locale("hi", "IN")
}
fun setLocale(context: Context) {
@@ -74,7 +78,7 @@ sealed class LanguagesPreference(val value: Int) : Preference() {
companion object {
val default = UseDeviceLanguages
- val values = listOf(UseDeviceLanguages, English, ChineseSimplified, German, French, Czech, Italian)
+ val values = listOf(UseDeviceLanguages, English, ChineseSimplified, German, French, Czech, Italian, Hindi)
fun fromPreferences(preferences: Preferences): LanguagesPreference =
when (preferences[DataStoreKeys.Languages.key]) {
@@ -85,6 +89,7 @@ sealed class LanguagesPreference(val value: Int) : Preference() {
4 -> French
5 -> Czech
6 -> Italian
+ 7 -> Hindi
else -> default
}
@@ -97,6 +102,7 @@ sealed class LanguagesPreference(val value: Int) : Preference() {
4 -> French
5 -> Czech
6 -> Italian
+ 7 -> Hindi
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 9acac2a..9eaf6a3 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
@@ -11,6 +11,7 @@ sealed class Preference {
fun Preferences.toSettings(): Settings {
return Settings(
+ // Version
newVersionNumber = NewVersionNumberPreference.fromPreferences(this),
skipVersionNumber = SkipVersionNumberPreference.fromPreferences(this),
newVersionPublishDate = NewVersionPublishDatePreference.fromPreferences(this),
@@ -18,11 +19,13 @@ fun Preferences.toSettings(): Settings {
newVersionSize = NewVersionSizePreference.fromPreferences(this),
newVersionDownloadUrl = NewVersionDownloadUrlPreference.fromPreferences(this),
+ // Theme
themeIndex = ThemeIndexPreference.fromPreferences(this),
customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this),
darkTheme = DarkThemePreference.fromPreferences(this),
amoledDarkTheme = AmoledDarkThemePreference.fromPreferences(this),
+ // Feeds page
feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this),
feedsFilterBarFilled = FeedsFilterBarFilledPreference.fromPreferences(this),
feedsFilterBarPadding = FeedsFilterBarPaddingPreference.fromPreferences(this),
@@ -31,6 +34,7 @@ fun Preferences.toSettings(): Settings {
feedsGroupListExpand = FeedsGroupListExpandPreference.fromPreferences(this),
feedsGroupListTonalElevation = FeedsGroupListTonalElevationPreference.fromPreferences(this),
+ // Flow page
flowFilterBarStyle = FlowFilterBarStylePreference.fromPreferences(this),
flowFilterBarFilled = FlowFilterBarFilledPreference.fromPreferences(this),
flowFilterBarPadding = FlowFilterBarPaddingPreference.fromPreferences(this),
@@ -46,9 +50,32 @@ fun Preferences.toSettings(): Settings {
),
flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this),
+ // Reading page
+ readingTheme = ReadingThemePreference.fromPreferences(this),
+ readingDarkTheme = ReadingDarkThemePreference.fromPreferences(this),
+ readingPageTonalElevation = ReadingPageTonalElevationPreference.fromPreferences(this),
+ readingAutoHideToolbar = ReadingAutoHideToolbarPreference.fromPreferences(this),
+ readingTextFontSize = ReadingTextFontSizePreference.fromPreferences(this),
+ readingLetterSpacing = ReadingLetterSpacingPreference.fromPreferences(this),
+ readingTextHorizontalPadding = ReadingTextHorizontalPaddingPreference.fromPreferences(this),
+ readingTextAlign = ReadingTextAlignPreference.fromPreferences(this),
+ readingTextBold = ReadingTextBoldPreference.fromPreferences(this),
+ readingTitleAlign = ReadingTitleAlignPreference.fromPreferences(this),
+ readingSubheadAlign = ReadingSubheadAlignPreference.fromPreferences(this),
+ readingFonts = ReadingFontsPreference.fromPreferences(this),
+ readingTitleBold = ReadingTitleBoldPreference.fromPreferences(this),
+ readingSubheadBold = ReadingSubheadBoldPreference.fromPreferences(this),
+ readingTitleUpperCase = ReadingTitleUpperCasePreference.fromPreferences(this),
+ readingSubheadUpperCase = ReadingSubheadUpperCasePreference.fromPreferences(this),
+ readingImageHorizontalPadding = ReadingImageHorizontalPaddingPreference.fromPreferences(this),
+ readingImageRoundedCorners = ReadingImageRoundedCornersPreference.fromPreferences(this),
+ readingImageMaximize = ReadingImageMaximizePreference.fromPreferences(this),
+
+ // Interaction
initialPage = InitialPagePreference.fromPreferences(this),
initialFilter = InitialFilterPreference.fromPreferences(this),
+ // Languages
languages = LanguagesPreference.fromPreferences(this),
)
}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingAutoHideToolbarPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingAutoHideToolbarPreference.kt
new file mode 100644
index 0000000..d918632
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingAutoHideToolbarPreference.kt
@@ -0,0 +1,39 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingAutoHideToolbarPreference(val value: Boolean) : Preference() {
+ object ON : ReadingAutoHideToolbarPreference(true)
+ object OFF : ReadingAutoHideToolbarPreference(false)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingAutoHideToolbar, value)
+ }
+ }
+
+ companion object {
+
+ val default = ON
+ val values = listOf(ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingAutoHideToolbar.key]) {
+ true -> ON
+ false -> OFF
+ else -> default
+ }
+ }
+}
+
+operator fun ReadingAutoHideToolbarPreference.not(): ReadingAutoHideToolbarPreference =
+ when (value) {
+ true -> ReadingAutoHideToolbarPreference.OFF
+ false -> ReadingAutoHideToolbarPreference.ON
+ }
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingDarkThemePreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingDarkThemePreference.kt
new file mode 100644
index 0000000..71dee4d
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingDarkThemePreference.kt
@@ -0,0 +1,72 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.Stable
+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.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingDarkThemePreference(val value: Int) : Preference() {
+ object UseAppTheme : ReadingDarkThemePreference(0)
+ object ON : ReadingDarkThemePreference(1)
+ object OFF : ReadingDarkThemePreference(2)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingDarkTheme,
+ value
+ )
+ }
+ }
+
+ fun toDesc(context: Context): String =
+ when (this) {
+ UseAppTheme -> context.getString(R.string.use_app_theme)
+ ON -> context.getString(R.string.on)
+ OFF -> context.getString(R.string.off)
+ }
+
+ @Composable
+ @ReadOnlyComposable
+ fun isDarkTheme(): Boolean = when (this) {
+ UseAppTheme -> LocalDarkTheme.current.isDarkTheme()
+ ON -> true
+ OFF -> false
+ }
+
+ companion object {
+
+ val default = UseAppTheme
+ val values = listOf(UseAppTheme, ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingDarkTheme.key]) {
+ 0 -> UseAppTheme
+ 1 -> ON
+ 2 -> OFF
+ else -> default
+ }
+ }
+}
+
+@Stable
+@Composable
+@ReadOnlyComposable
+operator fun ReadingDarkThemePreference.not(): ReadingDarkThemePreference =
+ when (this) {
+ ReadingDarkThemePreference.UseAppTheme -> if (LocalDarkTheme.current.isDarkTheme()) {
+ ReadingDarkThemePreference.OFF
+ } else {
+ ReadingDarkThemePreference.ON
+ }
+
+ ReadingDarkThemePreference.ON -> ReadingDarkThemePreference.OFF
+ ReadingDarkThemePreference.OFF -> ReadingDarkThemePreference.ON
+ }
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
new file mode 100644
index 0000000..b6b2dd1
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingFontsPreference.kt
@@ -0,0 +1,65 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+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.dataStore
+import me.ash.reader.ui.ext.put
+import me.ash.reader.ui.theme.googleSansDisplay
+import me.ash.reader.ui.theme.googleSansText
+
+sealed class ReadingFontsPreference(val value: Int) : Preference() {
+ object GoogleSans : ReadingFontsPreference(0)
+ object Serif : ReadingFontsPreference(1)
+ object SansSerif : ReadingFontsPreference(2)
+ object Monospace : ReadingFontsPreference(3)
+ object Cursive : ReadingFontsPreference(4)
+ object External : ReadingFontsPreference(5)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingFonts, value)
+ }
+ }
+
+ fun toDesc(context: Context): String =
+ when (this) {
+ GoogleSans -> "Google Sans"
+ Serif -> "Serif"
+ SansSerif -> "Sans-Serif"
+ Monospace -> "Monospace"
+ Cursive -> "Cursive"
+ External -> context.getString(R.string.external_fonts)
+ }
+
+ fun asFontFamily(isDisplay: Boolean = false): FontFamily =
+ when (this) {
+ GoogleSans -> if (isDisplay) googleSansDisplay else googleSansText
+ Serif -> FontFamily.Serif
+ SansSerif -> FontFamily.SansSerif
+ Monospace -> FontFamily.Monospace
+ Cursive -> FontFamily.Cursive
+ External -> FontFamily.Default
+ }
+
+ companion object {
+
+ val default = GoogleSans
+ val values = listOf(GoogleSans, Serif, SansSerif, Monospace, Cursive, External)
+
+ fun fromPreferences(preferences: Preferences): ReadingFontsPreference =
+ when (preferences[DataStoreKeys.ReadingFonts.key]) {
+ 0 -> GoogleSans
+ 1 -> Serif
+ 2 -> SansSerif
+ 3 -> Monospace
+ 4 -> Cursive
+ 5 -> External
+ else -> default
+ }
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageHorizontalPaddingPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageHorizontalPaddingPreference.kt
new file mode 100644
index 0000000..40eeaef
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageHorizontalPaddingPreference.kt
@@ -0,0 +1,23 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+object ReadingImageHorizontalPaddingPreference {
+
+ const val default = 24
+
+ fun put(context: Context, scope: CoroutineScope, value: Int) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingImageHorizontalPadding, value)
+ }
+ }
+
+ fun fromPreferences(preferences: Preferences) =
+ preferences[DataStoreKeys.ReadingImageHorizontalPadding.key] ?: default
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageMaximizePreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageMaximizePreference.kt
new file mode 100644
index 0000000..2a37938
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageMaximizePreference.kt
@@ -0,0 +1,39 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingImageMaximizePreference(val value: Boolean) : Preference() {
+ object ON : ReadingImageMaximizePreference(true)
+ object OFF : ReadingImageMaximizePreference(false)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingImageMaximize, value)
+ }
+ }
+
+ companion object {
+
+ val default = ON
+ val values = listOf(ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingImageMaximize.key]) {
+ true -> ON
+ false -> OFF
+ else -> default
+ }
+ }
+}
+
+operator fun ReadingImageMaximizePreference.not(): ReadingImageMaximizePreference =
+ when (value) {
+ true -> ReadingImageMaximizePreference.OFF
+ false -> ReadingImageMaximizePreference.ON
+ }
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageRoundedCornersPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageRoundedCornersPreference.kt
new file mode 100644
index 0000000..0b2de79
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingImageRoundedCornersPreference.kt
@@ -0,0 +1,23 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+object ReadingImageRoundedCornersPreference {
+
+ const val default = 32
+
+ fun put(context: Context, scope: CoroutineScope, value: Int) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingImageRoundedCorners, value)
+ }
+ }
+
+ fun fromPreferences(preferences: Preferences) =
+ preferences[DataStoreKeys.ReadingImageRoundedCorners.key] ?: default
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingLetterSpacingPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingLetterSpacingPreference.kt
new file mode 100644
index 0000000..a81bb3e
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingLetterSpacingPreference.kt
@@ -0,0 +1,23 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+object ReadingLetterSpacingPreference {
+
+ const val default = 0.5
+
+ fun put(context: Context, scope: CoroutineScope, value: Double) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingLetterSpacing, value)
+ }
+ }
+
+ fun fromPreferences(preferences: Preferences) =
+ preferences[DataStoreKeys.ReadingLetterSpacing.key] ?: default
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingPageTonalElevationPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingPageTonalElevationPreference.kt
new file mode 100644
index 0000000..b288d4c
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingPageTonalElevationPreference.kt
@@ -0,0 +1,53 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.data.constant.ElevationTokens
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingPageTonalElevationPreference(val value: Int) : Preference() {
+ object Level0 : ReadingPageTonalElevationPreference(ElevationTokens.Level0)
+ object Level1 : ReadingPageTonalElevationPreference(ElevationTokens.Level1)
+ object Level2 : ReadingPageTonalElevationPreference(ElevationTokens.Level2)
+ object Level3 : ReadingPageTonalElevationPreference(ElevationTokens.Level3)
+ object Level4 : ReadingPageTonalElevationPreference(ElevationTokens.Level4)
+ object Level5 : ReadingPageTonalElevationPreference(ElevationTokens.Level5)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingPageTonalElevation, value)
+ }
+ }
+
+ fun toDesc(context: Context): String =
+ when (this) {
+ Level0 -> "Level 0 (${ElevationTokens.Level0}dp)"
+ Level1 -> "Level 1 (${ElevationTokens.Level1}dp)"
+ Level2 -> "Level 2 (${ElevationTokens.Level2}dp)"
+ Level3 -> "Level 3 (${ElevationTokens.Level3}dp)"
+ Level4 -> "Level 4 (${ElevationTokens.Level4}dp)"
+ Level5 -> "Level 5 (${ElevationTokens.Level5}dp)"
+ }
+
+ companion object {
+
+ val default = Level0
+ val values = listOf(Level0, Level1, Level2, Level3, Level4, Level5)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingPageTonalElevation.key]) {
+ ElevationTokens.Level0 -> Level0
+ ElevationTokens.Level1 -> Level1
+ ElevationTokens.Level2 -> Level2
+ ElevationTokens.Level3 -> Level3
+ ElevationTokens.Level4 -> Level4
+ ElevationTokens.Level5 -> Level5
+ else -> default
+ }
+ }
+}
+
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadAlignPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadAlignPreference.kt
new file mode 100644
index 0000000..35aefe6
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadAlignPreference.kt
@@ -0,0 +1,58 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.compose.ui.text.style.TextAlign
+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.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingSubheadAlignPreference(val value: Int) : Preference() {
+ object Left : ReadingSubheadAlignPreference(0)
+ object Right : ReadingSubheadAlignPreference(1)
+ object Center : ReadingSubheadAlignPreference(2)
+ object Justify : ReadingSubheadAlignPreference(3)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingSubheadAlign,
+ value
+ )
+ }
+ }
+
+ fun toDesc(context: Context): String =
+ when (this) {
+ Left -> context.getString(R.string.align_left)
+ Right -> context.getString(R.string.align_right)
+ Center -> context.getString(R.string.center_text)
+ Justify -> context.getString(R.string.justify)
+ }
+
+ fun toTextAlign(): TextAlign =
+ when (this) {
+ Left -> TextAlign.Start
+ Right -> TextAlign.End
+ Center -> TextAlign.Center
+ Justify -> TextAlign.Justify
+ }
+
+ companion object {
+
+ val default = Left
+ val values = listOf(Left, Right, Center, Justify)
+
+ fun fromPreferences(preferences: Preferences): ReadingSubheadAlignPreference =
+ when (preferences[DataStoreKeys.ReadingSubheadAlign.key]) {
+ 0 -> Left
+ 1 -> Right
+ 2 -> Center
+ 3 -> Justify
+ else -> default
+ }
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadBoldPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadBoldPreference.kt
new file mode 100644
index 0000000..1d79acd
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadBoldPreference.kt
@@ -0,0 +1,42 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingSubheadBoldPreference(val value: Boolean) : Preference() {
+ object ON : ReadingSubheadBoldPreference(true)
+ object OFF : ReadingSubheadBoldPreference(false)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingSubheadBold,
+ value
+ )
+ }
+ }
+
+ companion object {
+
+ val default = OFF
+ val values = listOf(ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingSubheadBold.key]) {
+ true -> ON
+ false -> OFF
+ else -> default
+ }
+ }
+}
+
+operator fun ReadingSubheadBoldPreference.not(): ReadingSubheadBoldPreference =
+ when (value) {
+ true -> ReadingSubheadBoldPreference.OFF
+ false -> ReadingSubheadBoldPreference.ON
+ }
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadUpperCasePreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadUpperCasePreference.kt
new file mode 100644
index 0000000..a574dd4
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingSubheadUpperCasePreference.kt
@@ -0,0 +1,42 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingSubheadUpperCasePreference(val value: Boolean) : Preference() {
+ object ON : ReadingSubheadUpperCasePreference(true)
+ object OFF : ReadingSubheadUpperCasePreference(false)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingSubheadUpperCase,
+ value
+ )
+ }
+ }
+
+ companion object {
+
+ val default = OFF
+ val values = listOf(ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingSubheadUpperCase.key]) {
+ true -> ON
+ false -> OFF
+ else -> default
+ }
+ }
+}
+
+operator fun ReadingSubheadUpperCasePreference.not(): ReadingSubheadUpperCasePreference =
+ when (value) {
+ true -> ReadingSubheadUpperCasePreference.OFF
+ false -> ReadingSubheadUpperCasePreference.ON
+ }
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextAlignPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextAlignPreference.kt
new file mode 100644
index 0000000..b541b8e
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextAlignPreference.kt
@@ -0,0 +1,66 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.text.style.TextAlign
+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.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingTextAlignPreference(val value: Int) : Preference() {
+ object Left : ReadingTextAlignPreference(0)
+ object Right : ReadingTextAlignPreference(1)
+ object Center : ReadingTextAlignPreference(2)
+ object Justify : ReadingTextAlignPreference(3)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingTextAlign,
+ value
+ )
+ }
+ }
+
+ fun toDesc(context: Context): String =
+ when (this) {
+ Left -> context.getString(R.string.align_left)
+ Right -> context.getString(R.string.align_right)
+ Center -> context.getString(R.string.center_text)
+ Justify -> context.getString(R.string.justify)
+ }
+
+ fun toTextAlign(): TextAlign =
+ when (this) {
+ Left -> TextAlign.Start
+ Right -> TextAlign.End
+ Center -> TextAlign.Center
+ Justify -> TextAlign.Justify
+ }
+ fun toAlignment(): Alignment.Horizontal =
+ when (this) {
+ Left -> Alignment.Start
+ Right -> Alignment.End
+ Center -> Alignment.CenterHorizontally
+ Justify -> Alignment.Start
+ }
+
+ companion object {
+
+ val default = Left
+ val values = listOf(Left, Right, Center, Justify)
+
+ fun fromPreferences(preferences: Preferences): ReadingTextAlignPreference =
+ when (preferences[DataStoreKeys.ReadingTextAlign.key]) {
+ 0 -> Left
+ 1 -> Right
+ 2 -> Center
+ 3 -> Justify
+ else -> default
+ }
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextBoldPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextBoldPreference.kt
new file mode 100644
index 0000000..b8d1103
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextBoldPreference.kt
@@ -0,0 +1,42 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingTextBoldPreference(val value: Boolean) : Preference() {
+ object ON : ReadingTextBoldPreference(true)
+ object OFF : ReadingTextBoldPreference(false)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingTextBold,
+ value
+ )
+ }
+ }
+
+ companion object {
+
+ val default = OFF
+ val values = listOf(ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingTextBold.key]) {
+ true -> ON
+ false -> OFF
+ else -> default
+ }
+ }
+}
+
+operator fun ReadingTextBoldPreference.not(): ReadingTextBoldPreference =
+ when (value) {
+ true -> ReadingTextBoldPreference.OFF
+ false -> ReadingTextBoldPreference.ON
+ }
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextFontSizePreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextFontSizePreference.kt
new file mode 100644
index 0000000..85ff1c9
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextFontSizePreference.kt
@@ -0,0 +1,23 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+object ReadingTextFontSizePreference {
+
+ const val default = 17
+
+ fun put(context: Context, scope: CoroutineScope, value: Int) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingTextFontSize, value)
+ }
+ }
+
+ fun fromPreferences(preferences: Preferences) =
+ preferences[DataStoreKeys.ReadingTextFontSize.key] ?: default
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextHorizontalPaddingPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextHorizontalPaddingPreference.kt
new file mode 100644
index 0000000..2c5524f
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTextHorizontalPaddingPreference.kt
@@ -0,0 +1,23 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+object ReadingTextHorizontalPaddingPreference {
+
+ const val default = 24
+
+ fun put(context: Context, scope: CoroutineScope, value: Int) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingTextHorizontalPadding, value)
+ }
+ }
+
+ fun fromPreferences(preferences: Preferences) =
+ preferences[DataStoreKeys.ReadingTextHorizontalPadding.key] ?: default
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingThemePreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingThemePreference.kt
new file mode 100644
index 0000000..19be8c6
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingThemePreference.kt
@@ -0,0 +1,127 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.compose.runtime.Immutable
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+@Immutable
+sealed class ReadingThemePreference(val value: Int) : Preference() {
+
+ object MaterialYou : ReadingThemePreference(0)
+ object Reeder : ReadingThemePreference(1)
+ object Paper : ReadingThemePreference(2)
+ object Custom : ReadingThemePreference(3)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(DataStoreKeys.ReadingTheme, value)
+ }
+ }
+
+ fun toDesc(context: Context): String =
+ when (this) {
+ MaterialYou -> "Material You"
+ Reeder -> "Reeder"
+ Paper -> "Paper"
+ Custom -> "Custom"
+ }
+
+ fun applyTheme(context: Context, scope: CoroutineScope) {
+ when (this) {
+ MaterialYou -> {
+ ReadingTitleBoldPreference.default.put(context, scope)
+ ReadingTitleUpperCasePreference.default.put(context, scope)
+ ReadingTitleAlignPreference.default.put(context, scope)
+ ReadingSubheadBoldPreference.default.put(context, scope)
+ ReadingSubheadUpperCasePreference.default.put(context, scope)
+ ReadingSubheadAlignPreference.default.put(context, scope)
+ ReadingTextBoldPreference.default.put(context, scope)
+ ReadingTextHorizontalPaddingPreference.put(context, scope,
+ ReadingTextHorizontalPaddingPreference.default)
+ ReadingTextAlignPreference.default.put(context, scope)
+ ReadingLetterSpacingPreference.put(context, scope, ReadingLetterSpacingPreference.default)
+ ReadingTextFontSizePreference.put(context, scope, ReadingTextFontSizePreference.default)
+ ReadingImageRoundedCornersPreference.put(context, scope, ReadingImageRoundedCornersPreference.default)
+ ReadingImageHorizontalPaddingPreference.put(context, scope,
+ ReadingImageHorizontalPaddingPreference.default)
+ ReadingImageMaximizePreference.default.put(context, scope)
+ }
+
+ Reeder -> {
+ ReadingTitleBoldPreference.ON.put(context, scope)
+ ReadingTitleUpperCasePreference.default.put(context, scope)
+ ReadingTitleAlignPreference.default.put(context, scope)
+ ReadingSubheadBoldPreference.ON.put(context, scope)
+ ReadingSubheadUpperCasePreference.default.put(context, scope)
+ ReadingSubheadAlignPreference.default.put(context, scope)
+ ReadingTextBoldPreference.default.put(context, scope)
+ ReadingTextHorizontalPaddingPreference.put(context, scope,
+ ReadingTextHorizontalPaddingPreference.default)
+ ReadingTextAlignPreference.default.put(context, scope)
+ ReadingLetterSpacingPreference.put(context, scope, ReadingLetterSpacingPreference.default)
+ ReadingTextFontSizePreference.put(context, scope, 18)
+ ReadingImageRoundedCornersPreference.put(context, scope, 0)
+ ReadingImageHorizontalPaddingPreference.put(context, scope, 0)
+ ReadingImageMaximizePreference.default.put(context, scope)
+ }
+
+ Paper -> {
+ ReadingTitleBoldPreference.ON.put(context, scope)
+ ReadingTitleUpperCasePreference.ON.put(context, scope)
+ ReadingTitleAlignPreference.Center.put(context, scope)
+ ReadingSubheadBoldPreference.ON.put(context, scope)
+ ReadingSubheadUpperCasePreference.ON.put(context, scope)
+ ReadingSubheadAlignPreference.Center.put(context, scope)
+ ReadingTextBoldPreference.default.put(context, scope)
+ ReadingTextHorizontalPaddingPreference.put(context, scope,
+ ReadingTextHorizontalPaddingPreference.default)
+ ReadingTextAlignPreference.Center.put(context, scope)
+ ReadingLetterSpacingPreference.put(context, scope, ReadingLetterSpacingPreference.default)
+ ReadingTextFontSizePreference.put(context, scope, 20)
+ ReadingImageRoundedCornersPreference.put(context, scope, 0)
+ ReadingImageHorizontalPaddingPreference.put(context, scope,
+ ReadingImageHorizontalPaddingPreference.default)
+ ReadingImageMaximizePreference.default.put(context, scope)
+ }
+
+ Custom -> {
+ ReadingTitleBoldPreference.default.put(context, scope)
+ ReadingTitleUpperCasePreference.default.put(context, scope)
+ ReadingTitleAlignPreference.default.put(context, scope)
+ ReadingSubheadBoldPreference.default.put(context, scope)
+ ReadingSubheadUpperCasePreference.default.put(context, scope)
+ ReadingSubheadAlignPreference.default.put(context, scope)
+ ReadingTextBoldPreference.default.put(context, scope)
+ ReadingTextHorizontalPaddingPreference.put(context, scope,
+ ReadingTextHorizontalPaddingPreference.default)
+ ReadingTextAlignPreference.default.put(context, scope)
+ ReadingLetterSpacingPreference.put(context, scope, ReadingLetterSpacingPreference.default)
+ ReadingTextFontSizePreference.put(context, scope, ReadingTextFontSizePreference.default)
+ ReadingImageRoundedCornersPreference.put(context, scope, ReadingImageRoundedCornersPreference.default)
+ ReadingImageHorizontalPaddingPreference.put(context, scope,
+ ReadingImageHorizontalPaddingPreference.default)
+ ReadingImageMaximizePreference.default.put(context, scope)
+ }
+ }
+ }
+
+ companion object {
+
+ val default = MaterialYou
+ val values = listOf(MaterialYou, Reeder, Paper, Custom)
+
+ fun fromPreferences(preferences: Preferences): ReadingThemePreference =
+ when (preferences[DataStoreKeys.ReadingTheme.key]) {
+ 0 -> MaterialYou
+ 1 -> Reeder
+ 2 -> Paper
+ 3 -> Custom
+ else -> default
+ }
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleAlignPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleAlignPreference.kt
new file mode 100644
index 0000000..a38f2dc
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleAlignPreference.kt
@@ -0,0 +1,61 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.text.style.TextAlign
+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.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingTitleAlignPreference(val value: Int) : Preference() {
+ object Left : ReadingTitleAlignPreference(0)
+ object Right : ReadingTitleAlignPreference(1)
+ object Center : ReadingTitleAlignPreference(2)
+ object Justify : ReadingTitleAlignPreference(3)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingTitleAlign,
+ value
+ )
+ }
+ }
+
+ @Stable
+ fun toDesc(context: Context): String =
+ when (this) {
+ Left -> context.getString(R.string.align_left)
+ Right -> context.getString(R.string.align_right)
+ Center -> context.getString(R.string.center_text)
+ Justify -> context.getString(R.string.justify)
+ }
+
+ @Stable
+ fun toTextAlign(): TextAlign =
+ when (this) {
+ Left -> TextAlign.Start
+ Right -> TextAlign.End
+ Center -> TextAlign.Center
+ Justify -> TextAlign.Justify
+ }
+
+ companion object {
+
+ val default = Left
+ val values = listOf(Left, Right, Center, Justify)
+
+ fun fromPreferences(preferences: Preferences): ReadingTitleAlignPreference =
+ when (preferences[DataStoreKeys.ReadingTitleAlign.key]) {
+ 0 -> Left
+ 1 -> Right
+ 2 -> Center
+ 3 -> Justify
+ else -> default
+ }
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleBoldPreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleBoldPreference.kt
new file mode 100644
index 0000000..599daa8
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleBoldPreference.kt
@@ -0,0 +1,42 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingTitleBoldPreference(val value: Boolean) : Preference() {
+ object ON : ReadingTitleBoldPreference(true)
+ object OFF : ReadingTitleBoldPreference(false)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingTitleBold,
+ value
+ )
+ }
+ }
+
+ companion object {
+
+ val default = OFF
+ val values = listOf(ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingTitleBold.key]) {
+ true -> ON
+ false -> OFF
+ else -> default
+ }
+ }
+}
+
+operator fun ReadingTitleBoldPreference.not(): ReadingTitleBoldPreference =
+ when (value) {
+ true -> ReadingTitleBoldPreference.OFF
+ false -> ReadingTitleBoldPreference.ON
+ }
diff --git a/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleUpperCasePreference.kt b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleUpperCasePreference.kt
new file mode 100644
index 0000000..e2f66c7
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/data/model/preference/ReadingTitleUpperCasePreference.kt
@@ -0,0 +1,42 @@
+package me.ash.reader.data.model.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import me.ash.reader.ui.ext.DataStoreKeys
+import me.ash.reader.ui.ext.dataStore
+import me.ash.reader.ui.ext.put
+
+sealed class ReadingTitleUpperCasePreference(val value: Boolean) : Preference() {
+ object ON : ReadingTitleUpperCasePreference(true)
+ object OFF : ReadingTitleUpperCasePreference(false)
+
+ override fun put(context: Context, scope: CoroutineScope) {
+ scope.launch {
+ context.dataStore.put(
+ DataStoreKeys.ReadingTitleUpperCase,
+ value
+ )
+ }
+ }
+
+ companion object {
+
+ val default = OFF
+ val values = listOf(ON, OFF)
+
+ fun fromPreferences(preferences: Preferences) =
+ when (preferences[DataStoreKeys.ReadingTitleUpperCase.key]) {
+ true -> ON
+ false -> OFF
+ else -> default
+ }
+ }
+}
+
+operator fun ReadingTitleUpperCasePreference.not(): ReadingTitleUpperCasePreference =
+ when (value) {
+ true -> ReadingTitleUpperCasePreference.OFF
+ false -> ReadingTitleUpperCasePreference.ON
+ }
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 7dd6cec..b8305a7 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
@@ -12,6 +12,7 @@ import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.dataStore
data class Settings(
+ // Version
val newVersionNumber: Version = NewVersionNumberPreference.default,
val skipVersionNumber: Version = SkipVersionNumberPreference.default,
val newVersionPublishDate: String = NewVersionPublishDatePreference.default,
@@ -19,11 +20,13 @@ data class Settings(
val newVersionSize: String = NewVersionSizePreference.default,
val newVersionDownloadUrl: String = NewVersionDownloadUrlPreference.default,
+ // Theme
val themeIndex: Int = ThemeIndexPreference.default,
val customPrimaryColor: String = CustomPrimaryColorPreference.default,
val darkTheme: DarkThemePreference = DarkThemePreference.default,
val amoledDarkTheme: AmoledDarkThemePreference = AmoledDarkThemePreference.default,
+ // Feeds page
val feedsFilterBarStyle: FeedsFilterBarStylePreference = FeedsFilterBarStylePreference.default,
val feedsFilterBarFilled: FeedsFilterBarFilledPreference = FeedsFilterBarFilledPreference.default,
val feedsFilterBarPadding: Int = FeedsFilterBarPaddingPreference.default,
@@ -32,6 +35,7 @@ data class Settings(
val feedsGroupListExpand: FeedsGroupListExpandPreference = FeedsGroupListExpandPreference.default,
val feedsGroupListTonalElevation: FeedsGroupListTonalElevationPreference = FeedsGroupListTonalElevationPreference.default,
+ // Flow page
val flowFilterBarStyle: FlowFilterBarStylePreference = FlowFilterBarStylePreference.default,
val flowFilterBarFilled: FlowFilterBarFilledPreference = FlowFilterBarFilledPreference.default,
val flowFilterBarPadding: Int = FlowFilterBarPaddingPreference.default,
@@ -45,67 +49,36 @@ data class Settings(
val flowArticleListDateStickyHeader: FlowArticleListDateStickyHeaderPreference = FlowArticleListDateStickyHeaderPreference.default,
val flowArticleListTonalElevation: FlowArticleListTonalElevationPreference = FlowArticleListTonalElevationPreference.default,
+ // Reading page
+ val readingTheme: ReadingThemePreference = ReadingThemePreference.default,
+ val readingDarkTheme: ReadingDarkThemePreference = ReadingDarkThemePreference.default,
+ val readingPageTonalElevation: ReadingPageTonalElevationPreference = ReadingPageTonalElevationPreference.default,
+ val readingAutoHideToolbar: ReadingAutoHideToolbarPreference = ReadingAutoHideToolbarPreference.default,
+ val readingTextFontSize: Int = ReadingTextFontSizePreference.default,
+ val readingLetterSpacing: Double = ReadingLetterSpacingPreference.default,
+ val readingTextHorizontalPadding: Int = ReadingTextHorizontalPaddingPreference.default,
+ val readingTextAlign: ReadingTextAlignPreference = ReadingTextAlignPreference.default,
+ val readingTextBold: ReadingTextBoldPreference = ReadingTextBoldPreference.default,
+ val readingTitleAlign: ReadingTitleAlignPreference = ReadingTitleAlignPreference.default,
+ val readingSubheadAlign: ReadingSubheadAlignPreference = ReadingSubheadAlignPreference.default,
+ val readingFonts: ReadingFontsPreference = ReadingFontsPreference.default,
+ val readingTitleBold: ReadingTitleBoldPreference = ReadingTitleBoldPreference.default,
+ val readingSubheadBold: ReadingSubheadBoldPreference = ReadingSubheadBoldPreference.default,
+ val readingTitleUpperCase: ReadingTitleUpperCasePreference = ReadingTitleUpperCasePreference.default,
+ val readingSubheadUpperCase: ReadingSubheadUpperCasePreference = ReadingSubheadUpperCasePreference.default,
+ val readingImageHorizontalPadding: Int = ReadingImageHorizontalPaddingPreference.default,
+ val readingImageRoundedCorners: Int = ReadingImageRoundedCornersPreference.default,
+ val readingImageMaximize: ReadingImageMaximizePreference = ReadingImageMaximizePreference.default,
+
+ // Interaction
val initialPage: InitialPagePreference = InitialPagePreference.default,
val initialFilter: InitialFilterPreference = InitialFilterPreference.default,
+ // Languages
val languages: LanguagesPreference = LanguagesPreference.default,
)
-@Composable
-fun SettingsProvider(
- content: @Composable () -> Unit,
-) {
- val context = LocalContext.current
- val settings = remember {
- context.dataStore.data.map {
- Log.i("RLog", "AppTheme: ${it}")
- it.toSettings()
- }
- }.collectAsStateValue(initial = Settings())
-
- CompositionLocalProvider(
- LocalNewVersionNumber provides settings.newVersionNumber,
- LocalSkipVersionNumber provides settings.skipVersionNumber,
- LocalNewVersionPublishDate provides settings.newVersionPublishDate,
- LocalNewVersionLog provides settings.newVersionLog,
- LocalNewVersionSize provides settings.newVersionSize,
- LocalNewVersionDownloadUrl provides settings.newVersionDownloadUrl,
-
- LocalThemeIndex provides settings.themeIndex,
- LocalCustomPrimaryColor provides settings.customPrimaryColor,
- LocalDarkTheme provides settings.darkTheme,
- LocalAmoledDarkTheme provides settings.amoledDarkTheme,
-
- LocalFeedsTopBarTonalElevation provides settings.feedsTopBarTonalElevation,
- LocalFeedsGroupListExpand provides settings.feedsGroupListExpand,
- LocalFeedsGroupListTonalElevation provides settings.feedsGroupListTonalElevation,
- LocalFeedsFilterBarStyle provides settings.feedsFilterBarStyle,
- LocalFeedsFilterBarFilled provides settings.feedsFilterBarFilled,
- LocalFeedsFilterBarPadding provides settings.feedsFilterBarPadding,
- LocalFeedsFilterBarTonalElevation provides settings.feedsFilterBarTonalElevation,
-
- LocalFlowTopBarTonalElevation provides settings.flowTopBarTonalElevation,
- LocalFlowArticleListFeedIcon provides settings.flowArticleListFeedIcon,
- LocalFlowArticleListFeedName provides settings.flowArticleListFeedName,
- LocalFlowArticleListImage provides settings.flowArticleListImage,
- LocalFlowArticleListDesc provides settings.flowArticleListDesc,
- LocalFlowArticleListTime provides settings.flowArticleListTime,
- LocalFlowArticleListDateStickyHeader provides settings.flowArticleListDateStickyHeader,
- LocalFlowArticleListTonalElevation provides settings.flowArticleListTonalElevation,
- LocalFlowFilterBarStyle provides settings.flowFilterBarStyle,
- LocalFlowFilterBarFilled provides settings.flowFilterBarFilled,
- LocalFlowFilterBarPadding provides settings.flowFilterBarPadding,
- LocalFlowFilterBarTonalElevation provides settings.flowFilterBarTonalElevation,
-
- LocalInitialPage provides settings.initialPage,
- LocalInitialFilter provides settings.initialFilter,
-
- LocalLanguages provides settings.languages,
- ) {
- content()
- }
-}
-
+// Version
val LocalNewVersionNumber = compositionLocalOf { NewVersionNumberPreference.default }
val LocalSkipVersionNumber = compositionLocalOf { SkipVersionNumberPreference.default }
val LocalNewVersionPublishDate = compositionLocalOf { NewVersionPublishDatePreference.default }
@@ -113,6 +86,7 @@ val LocalNewVersionLog = compositionLocalOf { NewVersionLogPreference.default }
val LocalNewVersionSize = compositionLocalOf { NewVersionSizePreference.default }
val LocalNewVersionDownloadUrl = compositionLocalOf { NewVersionDownloadUrlPreference.default }
+// Theme
val LocalThemeIndex =
compositionLocalOf { ThemeIndexPreference.default }
val LocalCustomPrimaryColor =
@@ -122,6 +96,7 @@ val LocalDarkTheme =
val LocalAmoledDarkTheme =
compositionLocalOf { AmoledDarkThemePreference.default }
+// Feeds page
val LocalFeedsFilterBarStyle =
compositionLocalOf { FeedsFilterBarStylePreference.default }
val LocalFeedsFilterBarFilled =
@@ -137,6 +112,7 @@ val LocalFeedsGroupListExpand =
val LocalFeedsGroupListTonalElevation =
compositionLocalOf { FeedsGroupListTonalElevationPreference.default }
+// Flow page
val LocalFlowFilterBarStyle =
compositionLocalOf { FlowFilterBarStylePreference.default }
val LocalFlowFilterBarFilled =
@@ -162,9 +138,119 @@ val LocalFlowArticleListDateStickyHeader =
val LocalFlowArticleListTonalElevation =
compositionLocalOf { FlowArticleListTonalElevationPreference.default }
+// Reading page
+val LocalReadingTheme = compositionLocalOf { ReadingThemePreference.default }
+val LocalReadingDarkTheme = compositionLocalOf { ReadingDarkThemePreference.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 }
+val LocalReadingTextAlign = compositionLocalOf { ReadingTextAlignPreference.default }
+val LocalReadingTextBold = compositionLocalOf { ReadingTextBoldPreference.default }
+val LocalReadingTitleAlign = compositionLocalOf { ReadingTitleAlignPreference.default }
+val LocalReadingSubheadAlign =
+ compositionLocalOf { ReadingSubheadAlignPreference.default }
+val LocalReadingFonts = compositionLocalOf { ReadingFontsPreference.default }
+val LocalReadingTitleBold = compositionLocalOf { ReadingTitleBoldPreference.default }
+val LocalReadingSubheadBold =
+ compositionLocalOf { ReadingSubheadBoldPreference.default }
+val LocalReadingTitleUpperCase =
+ compositionLocalOf { ReadingTitleUpperCasePreference.default }
+val LocalReadingSubheadUpperCase =
+ compositionLocalOf { ReadingSubheadUpperCasePreference.default }
+val LocalReadingImageHorizontalPadding = compositionLocalOf { ReadingImageHorizontalPaddingPreference.default }
+val LocalReadingImageRoundedCorners = compositionLocalOf { ReadingImageRoundedCornersPreference.default }
+val LocalReadingImageMaximize = compositionLocalOf { ReadingImageMaximizePreference.default }
+
+// Interaction
val LocalInitialPage = compositionLocalOf { InitialPagePreference.default }
val LocalInitialFilter =
compositionLocalOf { InitialFilterPreference.default }
+// Languages
val LocalLanguages =
compositionLocalOf { LanguagesPreference.default }
+
+@Composable
+fun SettingsProvider(
+ content: @Composable () -> Unit,
+) {
+ val context = LocalContext.current
+ val settings = remember {
+ context.dataStore.data.map {
+ Log.i("RLog", "AppTheme: ${it}")
+ it.toSettings()
+ }
+ }.collectAsStateValue(initial = Settings())
+
+ CompositionLocalProvider(
+ // Version
+ LocalNewVersionNumber provides settings.newVersionNumber,
+ LocalSkipVersionNumber provides settings.skipVersionNumber,
+ LocalNewVersionPublishDate provides settings.newVersionPublishDate,
+ LocalNewVersionLog provides settings.newVersionLog,
+ LocalNewVersionSize provides settings.newVersionSize,
+ LocalNewVersionDownloadUrl provides settings.newVersionDownloadUrl,
+
+ // Theme
+ LocalThemeIndex provides settings.themeIndex,
+ LocalCustomPrimaryColor provides settings.customPrimaryColor,
+ LocalDarkTheme provides settings.darkTheme,
+ LocalAmoledDarkTheme provides settings.amoledDarkTheme,
+
+ // Feeds page
+ LocalFeedsTopBarTonalElevation provides settings.feedsTopBarTonalElevation,
+ LocalFeedsGroupListExpand provides settings.feedsGroupListExpand,
+ LocalFeedsGroupListTonalElevation provides settings.feedsGroupListTonalElevation,
+ LocalFeedsFilterBarStyle provides settings.feedsFilterBarStyle,
+ LocalFeedsFilterBarFilled provides settings.feedsFilterBarFilled,
+ LocalFeedsFilterBarPadding provides settings.feedsFilterBarPadding,
+ LocalFeedsFilterBarTonalElevation provides settings.feedsFilterBarTonalElevation,
+
+ // Flow page
+ LocalFlowTopBarTonalElevation provides settings.flowTopBarTonalElevation,
+ LocalFlowArticleListFeedIcon provides settings.flowArticleListFeedIcon,
+ LocalFlowArticleListFeedName provides settings.flowArticleListFeedName,
+ LocalFlowArticleListImage provides settings.flowArticleListImage,
+ LocalFlowArticleListDesc provides settings.flowArticleListDesc,
+ LocalFlowArticleListTime provides settings.flowArticleListTime,
+ LocalFlowArticleListDateStickyHeader provides settings.flowArticleListDateStickyHeader,
+ LocalFlowArticleListTonalElevation provides settings.flowArticleListTonalElevation,
+ LocalFlowFilterBarStyle provides settings.flowFilterBarStyle,
+ LocalFlowFilterBarFilled provides settings.flowFilterBarFilled,
+ LocalFlowFilterBarPadding provides settings.flowFilterBarPadding,
+ LocalFlowFilterBarTonalElevation provides settings.flowFilterBarTonalElevation,
+
+ // Reading page
+ LocalReadingTheme provides settings.readingTheme,
+ LocalReadingDarkTheme provides settings.readingDarkTheme,
+ LocalReadingPageTonalElevation provides settings.readingPageTonalElevation,
+ LocalReadingAutoHideToolbar provides settings.readingAutoHideToolbar,
+ LocalReadingTextFontSize provides settings.readingTextFontSize,
+ LocalReadingLetterSpacing provides settings.readingLetterSpacing,
+ LocalReadingTextHorizontalPadding provides settings.readingTextHorizontalPadding,
+ LocalReadingTextAlign provides settings.readingTextAlign,
+ LocalReadingTextBold provides settings.readingTextBold,
+ LocalReadingTitleAlign provides settings.readingTitleAlign,
+ LocalReadingSubheadAlign provides settings.readingSubheadAlign,
+ LocalReadingFonts provides settings.readingFonts,
+ LocalReadingTitleBold provides settings.readingTitleBold,
+ LocalReadingSubheadBold provides settings.readingSubheadBold,
+ LocalReadingTitleUpperCase provides settings.readingTitleUpperCase,
+ LocalReadingSubheadUpperCase provides settings.readingSubheadUpperCase,
+ LocalReadingImageHorizontalPadding provides settings.readingImageHorizontalPadding,
+ LocalReadingImageRoundedCorners provides settings.readingImageRoundedCorners,
+ LocalReadingImageMaximize provides settings.readingImageMaximize,
+
+ // Interaction
+ LocalInitialPage provides settings.initialPage,
+ LocalInitialFilter provides settings.initialFilter,
+
+ // Languages
+ LocalLanguages provides settings.languages,
+ ) {
+ content()
+ }
+}
+
diff --git a/app/src/main/java/me/ash/reader/ui/component/ReadingThemePrev.kt b/app/src/main/java/me/ash/reader/ui/component/ReadingThemePrev.kt
new file mode 100644
index 0000000..9df4db4
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/component/ReadingThemePrev.kt
@@ -0,0 +1,168 @@
+package me.ash.reader.ui.component
+
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import me.ash.reader.data.model.preference.LocalReadingImageHorizontalPadding
+import me.ash.reader.data.model.preference.LocalReadingImageRoundedCorners
+import me.ash.reader.data.model.preference.LocalReadingTextAlign
+import me.ash.reader.data.model.preference.ReadingThemePreference
+import me.ash.reader.ui.theme.Shape24
+import me.ash.reader.ui.theme.palette.onDark
+import me.ash.reader.ui.theme.palette.onLight
+
+@Composable
+fun ReadingThemePrev(
+ selected: ReadingThemePreference = ReadingThemePreference.MaterialYou,
+ theme: ReadingThemePreference = ReadingThemePreference.MaterialYou,
+ onClick: () -> Unit = {},
+) {
+ val context = LocalContext.current
+ val imageRoundedCorners = LocalReadingImageRoundedCorners.current
+ val roundedCorners by remember { mutableStateOf(RoundedCornerShape((imageRoundedCorners / 2).dp)) }
+
+ Column(
+ modifier = Modifier
+ .width(150.dp)
+ .clip(Shape24)
+ .background(MaterialTheme.colorScheme.inverseOnSurface
+ onLight MaterialTheme.colorScheme.surface
+ )
+ .border(
+ width = animateDpAsState(if (selected == theme) 4.dp else (-1).dp).value,
+ color = MaterialTheme.colorScheme.primary,
+ shape = Shape24
+ )
+ .clickable(onClick = onClick),
+ horizontalAlignment = when (theme) {
+ ReadingThemePreference.MaterialYou -> Alignment.Start
+ ReadingThemePreference.Reeder -> Alignment.Start
+ ReadingThemePreference.Paper -> Alignment.CenterHorizontally
+ ReadingThemePreference.Custom -> LocalReadingTextAlign.current.toAlignment()
+ }
+ ) {
+ Spacer(modifier = Modifier.height(22.dp))
+ // Header
+ Text(
+ modifier = Modifier.padding(horizontal = 12.dp),
+ text = theme.toDesc(context),
+ style = MaterialTheme.typography.titleSmall,
+ )
+ Spacer(modifier = Modifier.height(2.dp))
+ // Metadata
+ Box(modifier = Modifier
+ .padding(horizontal = 12.dp)
+ .size(width = 32.dp, height = 4.dp)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.tertiaryContainer)
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ // Paragraph
+ Box(modifier = Modifier
+ .padding(horizontal = 12.dp)
+ .fillMaxWidth()
+ .height(12.dp)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surfaceVariant)
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Row(modifier = Modifier
+ .padding(horizontal = 12.dp)
+ .width(114.dp)
+ .height(12.dp)
+ ) {
+ Box(modifier = Modifier
+ .weight(1f)
+ .fillMaxSize()
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surfaceVariant)
+ )
+ Box(modifier = Modifier
+ .padding(start = 4.dp)
+ .weight(2f)
+ .fillMaxSize()
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surfaceVariant)
+ )
+ }
+ Spacer(modifier = Modifier.height(8.dp))
+ // Image
+ Box(modifier = Modifier
+ .padding(horizontal = when (theme) {
+ ReadingThemePreference.MaterialYou -> 12.dp
+ ReadingThemePreference.Reeder -> 0.dp
+ ReadingThemePreference.Paper -> 12.dp
+ ReadingThemePreference.Custom -> (LocalReadingImageHorizontalPadding.current / 2).dp
+ })
+ .fillMaxWidth()
+ .height(46.dp)
+ .clip(when (theme) {
+ ReadingThemePreference.MaterialYou -> MaterialTheme.shapes.medium
+ ReadingThemePreference.Reeder -> RectangleShape
+ ReadingThemePreference.Paper -> RectangleShape
+ ReadingThemePreference.Custom -> roundedCorners
+ })
+ .background(MaterialTheme.colorScheme.primaryContainer onDark MaterialTheme.colorScheme.secondaryContainer)
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ // Footer
+ Box(modifier = Modifier
+ .padding(horizontal = 12.dp)
+ .width(100.dp)
+ .height(12.dp)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surfaceVariant)
+ )
+ Spacer(modifier = Modifier.height(14.dp))
+ }
+}
+
+@Preview
+@Composable
+private fun ReadYouPreview() {
+ ReadingThemePrev(
+ selected = ReadingThemePreference.MaterialYou,
+ theme = ReadingThemePreference.MaterialYou,
+ )
+}
+
+@Preview
+@Composable
+private fun ReederPreview() {
+ ReadingThemePrev(
+ theme = ReadingThemePreference.Reeder,
+ )
+}
+
+@Preview
+@Composable
+private fun PaperPreview() {
+ ReadingThemePrev(
+ theme = ReadingThemePreference.Paper,
+ )
+}
+
+@Preview
+@Composable
+private fun CustomPreview() {
+ ReadingThemePrev(
+ theme = ReadingThemePreference.Custom,
+ )
+}
diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt b/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt
index 8363208..535d7e0 100644
--- a/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt
+++ b/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt
@@ -16,6 +16,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.BaselineShift
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -63,7 +64,7 @@ fun RadioDialog(
text = option.text,
style = MaterialTheme.typography.bodyLarge.copy(
baselineShift = BaselineShift.None
- ),
+ ).merge(other = option.style),
color = MaterialTheme.colorScheme.onSurface,
)
}
@@ -78,6 +79,7 @@ fun RadioDialog(
@Immutable
data class RadioDialogOption(
val text: String = "",
+ val style: TextStyle? = null,
val selected: Boolean = false,
val onClick: () -> Unit = {},
-)
\ No newline at end of file
+)
diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt
index e9c172b..a5552fc 100644
--- a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt
+++ b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt
@@ -33,6 +33,7 @@ import androidx.compose.foundation.text.selection.DisableSelection
import androidx.compose.material.Text
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.RectangleShape
@@ -51,6 +52,7 @@ import coil.size.Precision
import coil.size.Size
import coil.size.pxOrElse
import me.ash.reader.R
+import me.ash.reader.data.model.preference.LocalReadingImageMaximize
import me.ash.reader.ui.component.base.RYAsyncImage
import org.jsoup.Jsoup
import org.jsoup.helper.StringUtil
@@ -63,6 +65,7 @@ import kotlin.math.roundToInt
fun LazyListScope.htmlFormattedText(
inputStream: InputStream,
+ subheadUpperCase: Boolean = false,
baseUrl: String,
@DrawableRes imagePlaceholder: Int,
onLinkClick: (String) -> Unit,
@@ -72,6 +75,7 @@ fun LazyListScope.htmlFormattedText(
?.let { body ->
formatBody(
element = body,
+ subheadUpperCase = subheadUpperCase,
imagePlaceholder = imagePlaceholder,
onLinkClick = onLinkClick,
baseUrl = baseUrl,
@@ -81,6 +85,7 @@ fun LazyListScope.htmlFormattedText(
private fun LazyListScope.formatBody(
element: Element,
+ subheadUpperCase: Boolean = false,
@DrawableRes imagePlaceholder: Int,
onLinkClick: (String) -> Unit,
baseUrl: String,
@@ -98,7 +103,7 @@ private fun LazyListScope.formatBody(
text = paragraph,
style = bodyStyle(),
modifier = Modifier
- .padding(horizontal = PADDING_HORIZONTAL.dp)
+ .padding(horizontal = textHorizontalPadding().dp)
.width(MAX_CONTENT_WIDTH.dp)
) { offset ->
paragraph.getStringAnnotations("URL", offset, offset)
@@ -112,7 +117,7 @@ private fun LazyListScope.formatBody(
text = paragraph,
style = bodyStyle(),
modifier = Modifier
- .padding(horizontal = PADDING_HORIZONTAL.dp)
+ .padding(horizontal = textHorizontalPadding().dp)
.width(MAX_CONTENT_WIDTH.dp)
)
}
@@ -121,6 +126,7 @@ private fun LazyListScope.formatBody(
composer.appendTextChildren(
element.childNodes(),
+ subheadUpperCase = subheadUpperCase,
lazyListScope = this,
imagePlaceholder = imagePlaceholder,
onLinkClick = onLinkClick,
@@ -144,7 +150,7 @@ private fun LazyListScope.formatCodeBlock(
color = codeBlockBackground(),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
- .padding(horizontal = PADDING_HORIZONTAL.dp),
+ .padding(horizontal = textHorizontalPadding().dp),
) {
Box(
modifier = Modifier
@@ -179,6 +185,7 @@ private fun LazyListScope.formatCodeBlock(
private fun TextComposer.appendTextChildren(
nodes: List,
preFormatted: Boolean = false,
+ subheadUpperCase: Boolean = false,
lazyListScope: LazyListScope,
@DrawableRes imagePlaceholder: Int,
onLinkClick: (String) -> Unit,
@@ -238,9 +245,9 @@ private fun TextComposer.appendTextChildren(
"h1" -> {
withParagraph {
withComposableStyle(
- style = { h5Style().toSpanStyle() }
+ style = { h1Style().toSpanStyle() }
) {
- append("\n${element.text()}")
+ append("\n${if (subheadUpperCase) element.text().uppercase() else element.text()}")
}
}
}
@@ -248,9 +255,9 @@ private fun TextComposer.appendTextChildren(
"h2" -> {
withParagraph {
withComposableStyle(
- style = { h5Style().toSpanStyle() }
+ style = { h2Style().toSpanStyle() }
) {
- append("\n${element.text()}")
+ append("\n${if (subheadUpperCase) element.text().uppercase() else element.text()}")
}
}
}
@@ -258,9 +265,9 @@ private fun TextComposer.appendTextChildren(
"h3" -> {
withParagraph {
withComposableStyle(
- style = { h5Style().toSpanStyle() }
+ style = { h3Style().toSpanStyle() }
) {
- append("\n${element.text()}")
+ append("\n${if (subheadUpperCase) element.text().uppercase() else element.text()}")
}
}
}
@@ -268,9 +275,9 @@ private fun TextComposer.appendTextChildren(
"h4" -> {
withParagraph {
withComposableStyle(
- style = { h5Style().toSpanStyle() }
+ style = { h4Style().toSpanStyle() }
) {
- append("\n${element.text()}")
+ append("\n${if (subheadUpperCase) element.text().uppercase() else element.text()}")
}
}
}
@@ -280,7 +287,7 @@ private fun TextComposer.appendTextChildren(
withComposableStyle(
style = { h5Style().toSpanStyle() }
) {
- append("\n${element.text()}")
+ append("\n${if (subheadUpperCase) element.text().uppercase() else element.text()}")
}
}
}
@@ -288,15 +295,17 @@ private fun TextComposer.appendTextChildren(
"h6" -> {
withParagraph {
withComposableStyle(
- style = { h5Style().toSpanStyle() }
+ style = { h6Style().toSpanStyle() }
) {
- append("\n${element.text()}")
+ append("\n${if (subheadUpperCase) element.text().uppercase() else element.text()}")
}
}
}
"strong", "b" -> {
- withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
+ withComposableStyle(
+ style = { boldStyle().toSpanStyle() }
+ ) {
appendTextChildren(
element.childNodes(),
lazyListScope = lazyListScope,
@@ -419,8 +428,11 @@ private fun TextComposer.appendTextChildren(
"blockquote" -> {
withParagraph {
- withComposableStyle(
- style = { blockQuoteStyle() }
+ withStyle(
+ SpanStyle(
+ fontStyle = FontStyle.Italic,
+ fontWeight = FontWeight.Light,
+ )
) {
appendTextChildren(
element.childNodes(),
@@ -458,10 +470,10 @@ private fun TextComposer.appendTextChildren(
// val scale = remember { mutableStateOf(1f) }
Column(
modifier = Modifier
-// .padding(horizontal = PADDING_HORIZONTAL.dp)
+// .padding(horizontal = horizontalPadding().dp)
.width(MAX_CONTENT_WIDTH.dp)
) {
- Spacer(modifier = Modifier.height(PADDING_HORIZONTAL.dp))
+ Spacer(modifier = Modifier.height(textHorizontalPadding().dp))
DisableSelection {
BoxWithConstraints(
modifier = Modifier
@@ -487,9 +499,10 @@ private fun TextComposer.appendTextChildren(
val imageSize = maxImageSize()
RYAsyncImage(
modifier = Modifier
+ .align(Alignment.Center)
.fillMaxWidth()
- .padding(horizontal = PADDING_HORIZONTAL.dp)
- .clip(IMAGE_SHAPE)
+ .padding(horizontal = imageHorizontalPadding().dp)
+ .clip(imageShape())
.clickable { },
data = imageCandidates.getBestImageForMaxSize(
pixelDensity = pixelDensity(),
@@ -498,24 +511,24 @@ private fun TextComposer.appendTextChildren(
contentDescription = alt,
size = imageSize,
precision = Precision.INEXACT,
- contentScale = ContentScale.FillWidth,
+ contentScale = if (LocalReadingImageMaximize.current.value) ContentScale.FillWidth else ContentScale.Inside,
)
}
}
if (alt.isNotBlank()) {
- Spacer(modifier = Modifier.height(PADDING_HORIZONTAL.dp / 2))
+ Spacer(modifier = Modifier.height(textHorizontalPadding().dp / 2))
Text(
modifier = Modifier
.fillMaxWidth()
- .padding(horizontal = PADDING_HORIZONTAL.dp),
+ .padding(horizontal = textHorizontalPadding().dp),
text = alt,
style = captionStyle(),
)
}
- Spacer(modifier = Modifier.height(PADDING_HORIZONTAL.dp))
+ Spacer(modifier = Modifier.height(textHorizontalPadding().dp))
}
}
}
@@ -528,7 +541,7 @@ private fun TextComposer.appendTextChildren(
.forEach { listItem ->
withParagraph {
// no break space
- append("• ")
+ append(" • ")
appendTextChildren(
listItem.childNodes(),
lazyListScope = lazyListScope,
@@ -612,7 +625,7 @@ private fun TextComposer.appendTextChildren(
lazyListScope.item {
Column(
modifier = Modifier
- .padding(horizontal = PADDING_HORIZONTAL.dp)
+ .padding(horizontal = textHorizontalPadding().dp)
.width(MAX_CONTENT_WIDTH.dp)
) {
DisableSelection {
@@ -622,8 +635,8 @@ private fun TextComposer.appendTextChildren(
RYAsyncImage(
modifier = Modifier
.fillMaxWidth()
- .padding(horizontal = PADDING_HORIZONTAL.dp)
- .clip(IMAGE_SHAPE)
+ .padding(horizontal = imageHorizontalPadding().dp)
+ .clip(imageShape())
.clickable {
onLinkClick(video.link)
},
@@ -636,17 +649,17 @@ private fun TextComposer.appendTextChildren(
}
}
- Spacer(modifier = Modifier.height(PADDING_HORIZONTAL.dp / 2))
+ Spacer(modifier = Modifier.height(textHorizontalPadding().dp / 2))
Text(
modifier = Modifier
.fillMaxWidth()
- .padding(horizontal = PADDING_HORIZONTAL.dp),
+ .padding(horizontal = textHorizontalPadding().dp),
text = stringResource(R.string.touch_to_play_video),
style = captionStyle(),
)
- Spacer(modifier = Modifier.height(PADDING_HORIZONTAL.dp))
+ Spacer(modifier = Modifier.height(textHorizontalPadding().dp))
}
}
}
@@ -661,6 +674,7 @@ private fun TextComposer.appendTextChildren(
appendTextChildren(
nodes = element.childNodes(),
preFormatted = preFormatted,
+ subheadUpperCase = subheadUpperCase,
lazyListScope = lazyListScope,
imagePlaceholder = imagePlaceholder,
onLinkClick = onLinkClick,
diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt b/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt
index 464369b..19155ed 100644
--- a/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt
+++ b/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt
@@ -30,12 +30,14 @@ import me.ash.reader.R
@Suppress("FunctionName")
fun LazyListScope.Reader(
context: Context,
+ subheadUpperCase: Boolean = false,
link: String,
content: String,
) {
Log.i("RLog", "Reader: ")
htmlFormattedText(
inputStream = content.byteInputStream(),
+ subheadUpperCase = subheadUpperCase,
baseUrl = link,
imagePlaceholder = R.drawable.ic_launcher_foreground,
onLinkClick = {
@@ -47,4 +49,4 @@ fun LazyListScope.Reader(
)
}
)
-}
\ No newline at end of file
+}
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 84f3832..6699b0d 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
@@ -23,6 +23,8 @@ package me.ash.reader.ui.component.reader
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
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.text.SpanStyle
import androidx.compose.ui.text.TextStyle
@@ -31,79 +33,162 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import me.ash.reader.data.model.preference.*
import me.ash.reader.ui.ext.alphaLN
-const val PADDING_HORIZONTAL = 24.0
const val MAX_CONTENT_WIDTH = 840.0
-val IMAGE_SHAPE = RoundedCornerShape(32.dp)
+@Stable
@Composable
-fun bodyForeground(): Color =
+@ReadOnlyComposable
+fun imageHorizontalPadding(): Int =
+ LocalReadingImageHorizontalPadding.current
+
+@Stable
+@Composable
+@ReadOnlyComposable
+fun imageShape(): RoundedCornerShape =
+ RoundedCornerShape(LocalReadingImageRoundedCorners.current.dp)
+
+@Stable
+@Composable
+@ReadOnlyComposable
+fun onSurfaceColor(): Color =
+ MaterialTheme.colorScheme.onSurface
+
+@Stable
+@Composable
+@ReadOnlyComposable
+fun onSurfaceVariantColor(): Color =
MaterialTheme.colorScheme.onSurfaceVariant
+@Stable
@Composable
+@ReadOnlyComposable
+fun textHorizontalPadding(): Int =
+ LocalReadingTextHorizontalPadding.current
+
+@Stable
+@Composable
+@ReadOnlyComposable
+fun bodyForeground(): Color = onSurfaceVariantColor()
+
+@Stable
+@Composable
+@ReadOnlyComposable
fun bodyStyle(): TextStyle =
- MaterialTheme.typography.bodyLarge.copy(
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(),
+ fontWeight = if (LocalReadingTextBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
+ fontSize = LocalReadingTextFontSize.current.sp,
+ letterSpacing = LocalReadingLetterSpacing.current.sp,
color = bodyForeground(),
- textAlign = TextAlign.Start,
+ textAlign = LocalReadingTextAlign.current.toTextAlign(),
)
+@Stable
@Composable
+@ReadOnlyComposable
fun h1Style(): TextStyle =
- MaterialTheme.typography.displayMedium.copy(
- color = bodyForeground(),
- textAlign = TextAlign.Start,
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
+ fontSize = 28.sp,
+ letterSpacing = 0.sp,
+ color = onSurfaceColor(),
+ textAlign = LocalReadingSubheadAlign.current.toTextAlign(),
)
+@Stable
@Composable
+@ReadOnlyComposable
fun h2Style(): TextStyle =
- MaterialTheme.typography.displaySmall.copy(
- color = bodyForeground(),
- textAlign = TextAlign.Start,
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
+ fontSize = 28.sp,
+ letterSpacing = 0.sp,
+ color = onSurfaceColor(),
+ textAlign = LocalReadingSubheadAlign.current.toTextAlign(),
)
+@Stable
@Composable
+@ReadOnlyComposable
fun h3Style(): TextStyle =
- MaterialTheme.typography.headlineLarge.copy(
- color = bodyForeground(),
- textAlign = TextAlign.Start,
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
+ fontSize = 19.sp,
+ letterSpacing = 0.sp,
+ color = onSurfaceColor(),
+ textAlign = LocalReadingSubheadAlign.current.toTextAlign(),
)
+@Stable
@Composable
+@ReadOnlyComposable
fun h4Style(): TextStyle =
- MaterialTheme.typography.headlineMedium.copy(
- color = bodyForeground(),
- textAlign = TextAlign.Start,
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
+ fontSize = 17.sp,
+ letterSpacing = 0.sp,
+ color = onSurfaceColor(),
+ textAlign = LocalReadingSubheadAlign.current.toTextAlign(),
)
+@Stable
@Composable
+@ReadOnlyComposable
fun h5Style(): TextStyle =
- MaterialTheme.typography.headlineSmall.copy(
- color = bodyForeground(),
- textAlign = TextAlign.Start,
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
+ fontSize = 17.sp,
+ letterSpacing = 0.sp,
+ color = onSurfaceColor(),
+ textAlign = LocalReadingSubheadAlign.current.toTextAlign(),
)
+@Stable
@Composable
+@ReadOnlyComposable
fun h6Style(): TextStyle =
- MaterialTheme.typography.titleLarge.copy(
- color = bodyForeground(),
- textAlign = TextAlign.Start,
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (LocalReadingSubheadBold.current.value) FontWeight.SemiBold else FontWeight.Normal,
+ fontSize = 17.sp,
+ letterSpacing = 0.sp,
+ color = onSurfaceColor(),
+ textAlign = LocalReadingSubheadAlign.current.toTextAlign(),
)
+@Stable
@Composable
+@ReadOnlyComposable
fun captionStyle(): TextStyle =
- MaterialTheme.typography.bodySmall.copy(
- color = bodyForeground().copy(alpha = 0.6f),
- textAlign = TextAlign.Center,
+ MaterialTheme.typography.bodySmall.merge(
+ TextStyle(
+ fontFamily = LocalReadingFonts.current.asFontFamily(),
+ color = bodyForeground().copy(alpha = 0.6f),
+ textAlign = TextAlign.Center,
+ )
)
+@Stable
@Composable
+@ReadOnlyComposable
fun linkTextStyle(): TextStyle =
TextStyle(
- color = MaterialTheme.colorScheme.secondary,
- textDecoration = TextDecoration.Underline
+ fontFamily = LocalReadingFonts.current.asFontFamily(),
+ fontSize = LocalReadingTextFontSize.current.sp,
+ color = MaterialTheme.colorScheme.primary,
+ textDecoration = TextDecoration.Underline,
)
+@Stable
@Composable
fun codeBlockStyle(): TextStyle =
MaterialTheme.typography.titleSmall.merge(
@@ -113,21 +198,27 @@ fun codeBlockStyle(): TextStyle =
)
)
+@Stable
@Composable
fun codeBlockBackground(): Color =
MaterialTheme.colorScheme.secondary.copy(alpha = (0.dp).alphaLN(weight = 3.2f))
+@Stable
@Composable
-fun blockQuoteStyle(): SpanStyle =
- MaterialTheme.typography.titleSmall.toSpanStyle().merge(
+fun boldStyle(): TextStyle =
+ bodyStyle().merge(
SpanStyle(
- fontWeight = FontWeight.Light
+ fontWeight = FontWeight.SemiBold,
+ color = onSurfaceColor(),
)
)
+@Stable
@Composable
fun codeInlineStyle(): SpanStyle =
- MaterialTheme.typography.titleSmall.toSpanStyle().copy(
- color = bodyForeground(),
- fontFamily = FontFamily.Monospace,
+ MaterialTheme.typography.titleSmall.toSpanStyle().merge(
+ SpanStyle(
+ color = bodyForeground(),
+ fontFamily = FontFamily.Monospace,
+ )
)
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 55cd3e1..c62c0b3 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
@@ -69,6 +69,7 @@ sealed class DataStoreKeys {
abstract val key: Preferences.Key
+ // Version
object IsFirstLaunch : DataStoreKeys() {
override val key: Preferences.Key
@@ -147,6 +148,7 @@ sealed class DataStoreKeys {
get() = booleanPreferencesKey("amoledDarkTheme")
}
+ // Feeds page
object FeedsFilterBarStyle : DataStoreKeys() {
override val key: Preferences.Key
@@ -189,6 +191,7 @@ sealed class DataStoreKeys {
get() = intPreferencesKey("feedsGroupListTonalElevation")
}
+ // Flow page
object FlowFilterBarStyle : DataStoreKeys() {
override val key: Preferences.Key
@@ -261,6 +264,122 @@ sealed class DataStoreKeys {
get() = intPreferencesKey("flowArticleListTonalElevation")
}
+ // Reading page
+ object ReadingDarkTheme : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingDarkTheme")
+ }
+
+ object ReadingPageTonalElevation : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("ReadingPageTonalElevation")
+ }
+
+ object ReadingTextFontSize : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingTextFontSize")
+ }
+
+ object ReadingLetterSpacing : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = doublePreferencesKey("readingLetterSpacing")
+ }
+
+ object ReadingTextHorizontalPadding : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingTextHorizontalPadding")
+ }
+
+ object ReadingTextBold : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = booleanPreferencesKey("readingTextBold")
+ }
+
+ object ReadingTextAlign : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingTextAlign")
+ }
+
+ object ReadingTitleAlign : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingTitleAlign")
+ }
+
+ object ReadingSubheadAlign : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingSubheadAlign")
+ }
+
+ object ReadingTheme : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingTheme")
+ }
+
+ object ReadingFonts : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingFonts")
+ }
+
+ object ReadingAutoHideToolbar : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = booleanPreferencesKey("readingAutoHideToolbar")
+ }
+
+ object ReadingTitleBold : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = booleanPreferencesKey("readingTitleBold")
+ }
+
+ object ReadingSubheadBold : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = booleanPreferencesKey("ReadingSubheadBold")
+ }
+
+ object ReadingTitleUpperCase : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = booleanPreferencesKey("readingTitleUpperCase")
+ }
+
+ object ReadingSubheadUpperCase : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = booleanPreferencesKey("ReadingSubheadUpperCase")
+ }
+
+ object ReadingImageMaximize : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = booleanPreferencesKey("readingImageMaximize")
+ }
+
+ object ReadingImageHorizontalPadding : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingImageHorizontalPadding")
+ }
+
+ object ReadingImageRoundedCorners : DataStoreKeys() {
+
+ override val key: Preferences.Key
+ get() = intPreferencesKey("readingImageRoundedCorners")
+ }
+
+ // Interaction
object InitialPage : DataStoreKeys() {
override val key: Preferences.Key
@@ -273,6 +392,7 @@ sealed class DataStoreKeys {
get() = intPreferencesKey("initialFilter")
}
+ // Languages
object Languages : DataStoreKeys() {
override val key: Preferences.Key
diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
index 4f2da16..3808ad4 100644
--- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
@@ -1,5 +1,6 @@
package me.ash.reader.ui.page.common
+import android.util.Log
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.background
import androidx.compose.material3.MaterialTheme
@@ -12,8 +13,11 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.collectLatest
import me.ash.reader.data.model.general.Filter
import me.ash.reader.data.model.preference.LocalDarkTheme
+import me.ash.reader.data.model.preference.LocalReadingDarkTheme
import me.ash.reader.ui.ext.*
import me.ash.reader.ui.page.home.HomeViewModel
import me.ash.reader.ui.page.home.feeds.FeedsPage
@@ -24,6 +28,7 @@ import me.ash.reader.ui.page.settings.color.ColorAndStylePage
import me.ash.reader.ui.page.settings.color.DarkThemePage
import me.ash.reader.ui.page.settings.color.feeds.FeedsPageStylePage
import me.ash.reader.ui.page.settings.color.flow.FlowPageStylePage
+import me.ash.reader.ui.page.settings.color.reading.*
import me.ash.reader.ui.page.settings.interaction.InteractionPage
import me.ash.reader.ui.page.settings.languages.LanguagesPage
import me.ash.reader.ui.page.settings.tips.TipsAndSupportPage
@@ -36,6 +41,7 @@ fun HomeEntry(
homeViewModel: HomeViewModel = hiltViewModel(),
) {
val context = LocalContext.current
+ var isReadingPage by rememberSaveable { mutableStateOf(false) }
val filterUiState = homeViewModel.filterUiState.collectAsStateValue()
val navController = rememberAnimatedNavController()
@@ -47,6 +53,11 @@ fun HomeEntry(
}
LaunchedEffect(Unit) {
+ navController.currentBackStackEntryFlow.collectLatest {
+ Log.i("RLog", "isReadingPage: ${navController.currentDestination?.route}")
+ delay(310L)
+ isReadingPage = navController.currentDestination?.route == "${RouteName.READING}/{articleId}"
+ }
when (context.initialPage) {
1 -> {
navController.navigate(RouteName.FLOW) {
@@ -80,9 +91,16 @@ fun HomeEntry(
}
}
- val useDarkTheme = LocalDarkTheme.current.isDarkTheme()
+ val useDarkTheme = if (isReadingPage) {
+ LocalReadingDarkTheme.current.isDarkTheme()
+ } else {
+ LocalDarkTheme.current.isDarkTheme()
+ }
- AppTheme(useDarkTheme = useDarkTheme) {
+ AppTheme(
+ useDarkTheme = if (isReadingPage) LocalReadingDarkTheme.current.isDarkTheme()
+ else LocalDarkTheme.current.isDarkTheme()
+ ) {
rememberSystemUiController().run {
setStatusBarColor(Color.Transparent, !useDarkTheme)
@@ -132,6 +150,24 @@ fun HomeEntry(
animatedComposable(route = RouteName.FLOW_PAGE_STYLE) {
FlowPageStylePage(navController)
}
+ animatedComposable(route = RouteName.READING_PAGE_STYLE) {
+ ReadingStylePage(navController)
+ }
+ animatedComposable(route = RouteName.READING_DARK_THEME) {
+ ReadingDarkThemePage(navController)
+ }
+ animatedComposable(route = RouteName.READING_PAGE_TITLE) {
+ ReadingTitlePage(navController)
+ }
+ animatedComposable(route = RouteName.READING_PAGE_TEXT) {
+ ReadingTextPage(navController)
+ }
+ animatedComposable(route = RouteName.READING_PAGE_IMAGE) {
+ ReadingImagePage(navController)
+ }
+ animatedComposable(route = RouteName.READING_PAGE_VIDEO) {
+ ReadingVideoPage(navController)
+ }
// Interaction
animatedComposable(route = RouteName.INTERACTION) {
diff --git a/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt b/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt
index 1d7be0c..d735c23 100644
--- a/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt
@@ -18,6 +18,12 @@ object RouteName {
const val DARK_THEME = "dark_theme"
const val FEEDS_PAGE_STYLE = "feeds_page_style"
const val FLOW_PAGE_STYLE = "flow_page_style"
+ const val READING_PAGE_STYLE = "reading_page_style"
+ const val READING_DARK_THEME = "reading_dark_theme"
+ const val READING_PAGE_TITLE = "reading_page_title"
+ const val READING_PAGE_TEXT = "reading_page_text"
+ const val READING_PAGE_IMAGE = "reading_page_image"
+ const val READING_PAGE_VIDEO = "reading_page_video"
// Interaction
const val INTERACTION = "interaction"
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt
index 0dbead5..e7534e7 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt
@@ -22,6 +22,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import me.ash.reader.R
+import me.ash.reader.data.model.preference.LocalReadingPageTonalElevation
import me.ash.reader.ui.component.base.CanBeDisabledIconButton
@Composable
@@ -34,6 +35,8 @@ fun BottomBar(
onStarred: (isStarred: Boolean) -> Unit = {},
onFullContent: (isFullContent: Boolean) -> Unit = {},
) {
+ val tonalElevation = LocalReadingPageTonalElevation.current
+
Box(
modifier = Modifier
.fillMaxSize()
@@ -43,7 +46,10 @@ fun BottomBar(
RYExtensibleVisibility(visible = isShow) {
val view = LocalView.current
- Surface(modifier = Modifier.navigationBarsPadding()) {
+ Surface(
+ modifier = Modifier.navigationBarsPadding(),
+ tonalElevation = tonalElevation.value.dp,
+ ) {
// TODO: Component styles await refactoring
Row(
modifier = Modifier
@@ -128,4 +134,4 @@ fun BottomBar(
}
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt
index b1883ad..c27f509 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt
@@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
+import me.ash.reader.data.model.preference.LocalReadingSubheadUpperCase
import me.ash.reader.ui.component.reader.Reader
import me.ash.reader.ui.ext.drawVerticalScrollbar
import java.util.*
@@ -30,6 +31,7 @@ fun Content(
isShowToolBar: Boolean,
) {
val context = LocalContext.current
+ val subheadUpperCase = LocalReadingSubheadUpperCase.current
SelectionContainer {
LazyColumn(
@@ -56,7 +58,7 @@ fun Content(
.padding(horizontal = 12.dp)
) {
DisableSelection {
- Header(
+ Metadata(
feedName = feedName,
title = title,
author = author,
@@ -88,6 +90,7 @@ fun Content(
if (!isLoading) {
Reader(
context = context,
+ subheadUpperCase = subheadUpperCase.value,
link = link ?: "",
content = content
)
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt
deleted file mode 100644
index 281ecf1..0000000
--- a/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package me.ash.reader.ui.page.home.reading
-
-import androidx.compose.foundation.layout.*
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import me.ash.reader.ui.ext.formatAsString
-import me.ash.reader.ui.ext.openURL
-import me.ash.reader.ui.ext.roundClick
-import java.util.*
-
-@Composable
-fun Header(
- feedName: String,
- title: String,
- author: String? = null,
- link: String? = null,
- publishedDate: Date,
-) {
- val context = LocalContext.current
- val dateString = remember(publishedDate) {
- publishedDate.formatAsString(context, atHourMinute = true)
- }
-
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .roundClick {
- context.openURL(link)
- }
- .padding(12.dp)
- ) {
- Text(
- modifier = Modifier.alpha(0.7f),
- text = dateString,
- color = MaterialTheme.colorScheme.outline,
- style = MaterialTheme.typography.labelMedium,
- textAlign = TextAlign.Start,
- )
- Spacer(modifier = Modifier.height(4.dp))
- Text(
- text = title,
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.headlineLarge,
- textAlign = TextAlign.Start,
- )
- Spacer(modifier = Modifier.height(4.dp))
- author?.let {
- if (it.isNotEmpty()) {
- Text(
- modifier = Modifier.alpha(0.7f),
- text = it,
- color = MaterialTheme.colorScheme.outline,
- style = MaterialTheme.typography.labelMedium,
- textAlign = TextAlign.Start,
- )
- }
- }
- Text(
- modifier = Modifier.alpha(0.7f),
- text = feedName,
- color = MaterialTheme.colorScheme.outline,
- style = MaterialTheme.typography.labelMedium,
- textAlign = TextAlign.Start,
- )
- }
-}
\ No newline at end of file
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
new file mode 100644
index 0000000..8c5a7ec
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/Metadata.kt
@@ -0,0 +1,100 @@
+package me.ash.reader.ui.page.home.reading
+
+import androidx.compose.foundation.layout.*
+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.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import me.ash.reader.data.model.preference.LocalReadingFonts
+import me.ash.reader.data.model.preference.LocalReadingTitleAlign
+import me.ash.reader.data.model.preference.LocalReadingTitleBold
+import me.ash.reader.data.model.preference.LocalReadingTitleUpperCase
+import me.ash.reader.ui.ext.formatAsString
+import me.ash.reader.ui.ext.openURL
+import me.ash.reader.ui.ext.roundClick
+import java.util.*
+
+@Composable
+fun Metadata(
+ feedName: String,
+ title: String,
+ author: String? = null,
+ link: String? = null,
+ publishedDate: Date,
+) {
+ val context = LocalContext.current
+ val titleBold = LocalReadingTitleBold.current
+ val titleUpperCase = LocalReadingTitleUpperCase.current
+ val titleAlign = LocalReadingTitleAlign.current
+ val dateString = remember(publishedDate) {
+ publishedDate.formatAsString(context, atHourMinute = true)
+ }
+
+ val titleUpperCaseString by remember { derivedStateOf { title.uppercase() } }
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .roundClick {
+ context.openURL(link)
+ }
+ .padding(12.dp)
+ ) {
+ Text(
+ modifier = Modifier
+ .alpha(0.7f)
+ .fillMaxWidth(),
+ text = dateString,
+ color = MaterialTheme.colorScheme.outline,
+ style = MaterialTheme.typography.labelMedium.copy(
+ fontFamily = LocalReadingFonts.current.asFontFamily(),
+ ),
+ textAlign = titleAlign.toTextAlign(),
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = if (titleUpperCase.value) titleUpperCaseString else title,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.headlineLarge.copy(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal,
+ ),
+ textAlign = titleAlign.toTextAlign(),
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ author?.let {
+ if (it.isNotEmpty()) {
+ Text(
+ modifier = Modifier
+ .alpha(0.7f)
+ .fillMaxWidth(),
+ text = it,
+ color = MaterialTheme.colorScheme.outline,
+ style = MaterialTheme.typography.labelMedium.copy(
+ fontFamily = LocalReadingFonts.current.asFontFamily(),
+ ),
+ textAlign = titleAlign.toTextAlign(),
+ )
+ }
+ }
+ Text(
+ modifier = Modifier
+ .alpha(0.7f)
+ .fillMaxWidth(),
+ text = feedName,
+ color = MaterialTheme.colorScheme.outline,
+ style = MaterialTheme.typography.labelMedium.copy(
+ fontFamily = LocalReadingFonts.current.asFontFamily(),
+ ),
+ textAlign = titleAlign.toTextAlign(),
+ )
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt
index d198734..d6e9c3a 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt
@@ -6,8 +6,11 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
+import me.ash.reader.data.model.preference.LocalReadingAutoHideToolbar
+import me.ash.reader.data.model.preference.LocalReadingPageTonalElevation
import me.ash.reader.ui.component.base.RYScaffold
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.isScrollDown
@@ -17,9 +20,13 @@ fun ReadingPage(
navController: NavHostController,
readingViewModel: ReadingViewModel = hiltViewModel(),
) {
+ val tonalElevation = LocalReadingPageTonalElevation.current
val readingUiState = readingViewModel.readingUiState.collectAsStateValue()
- val isShowToolBar =
+ val isShowToolBar = if (LocalReadingAutoHideToolbar.current.value) {
readingUiState.articleWithFeed != null && !readingUiState.listState.isScrollDown()
+ } else {
+ true
+ }
LaunchedEffect(Unit) {
navController.currentBackStackEntryFlow.collect {
@@ -39,12 +46,15 @@ fun ReadingPage(
}
RYScaffold(
+ topBarTonalElevation = tonalElevation.value.dp,
+ containerTonalElevation = tonalElevation.value.dp,
content = {
Log.i("RLog", "TopBar: recomposition")
Box(modifier = Modifier.fillMaxSize()) {
// Top Bar
TopBar(
+ navController = navController,
isShow = isShowToolBar,
title = readingUiState.articleWithFeed?.article?.title,
link = readingUiState.articleWithFeed?.article?.link,
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt
index 4d88925..a87dbf0 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Palette
import androidx.compose.material.icons.outlined.Share
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material3.MaterialTheme
@@ -18,18 +19,24 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
+import androidx.navigation.NavHostController
import me.ash.reader.R
+import me.ash.reader.data.model.preference.LocalReadingPageTonalElevation
import me.ash.reader.ui.component.base.FeedbackIconButton
import me.ash.reader.ui.ext.share
+import me.ash.reader.ui.ext.surfaceColorAtElevation
+import me.ash.reader.ui.page.common.RouteName
@Composable
fun TopBar(
+ navController: NavHostController,
isShow: Boolean,
title: String? = "",
link: String? = "",
onClose: () -> Unit = {},
) {
val context = LocalContext.current
+ val tonalElevation = LocalReadingPageTonalElevation.current
Box(
modifier = Modifier
@@ -41,7 +48,9 @@ fun TopBar(
SmallTopAppBar(
modifier = Modifier.statusBarsPadding(),
colors = TopAppBarDefaults.smallTopAppBarColors(
- containerColor = MaterialTheme.colorScheme.surface,
+ containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
+ elevation = tonalElevation.value.dp
+ ),
),
title = {},
navigationIcon = {
@@ -54,6 +63,16 @@ fun TopBar(
}
},
actions = {
+ FeedbackIconButton(
+ modifier = Modifier.size(22.dp),
+ imageVector = Icons.Outlined.Palette,
+ contentDescription = stringResource(R.string.style),
+ tint = MaterialTheme.colorScheme.onSurface
+ ) {
+ navController.navigate(RouteName.READING_PAGE_STYLE) {
+ launchSingleTop = true
+ }
+ }
FeedbackIconButton(
modifier = Modifier.size(20.dp),
imageVector = Icons.Outlined.Share,
@@ -69,4 +88,4 @@ fun TopBar(
)
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/drawer/FeedOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/drawer/FeedOptionDrawer.kt
new file mode 100644
index 0000000..a4b3470
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/drawer/FeedOptionDrawer.kt
@@ -0,0 +1,71 @@
+package me.ash.reader.ui.page.home.reading.drawer
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material3.Tab
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.hilt.navigation.compose.hiltViewModel
+import kotlinx.coroutines.launch
+import me.ash.reader.R
+import me.ash.reader.ui.component.base.BottomDrawer
+import me.ash.reader.ui.ext.collectAsStateValue
+import me.ash.reader.ui.page.home.feeds.drawer.feed.FeedOptionViewModel
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+fun StyleOptionDrawer(
+ feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
+ content: @Composable () -> Unit = {},
+) {
+ val context = LocalContext.current
+ val view = LocalView.current
+ val scope = rememberCoroutineScope()
+ val feedOptionUiState = feedOptionViewModel.feedOptionUiState.collectAsStateValue()
+ val feed = feedOptionUiState.feed
+ val toastString = stringResource(R.string.rename_toast, feedOptionUiState.newName)
+
+ BackHandler(feedOptionUiState.drawerState.isVisible) {
+ scope.launch {
+ feedOptionUiState.drawerState.hide()
+ }
+ }
+
+ BottomDrawer(
+ drawerState = feedOptionUiState.drawerState,
+ sheetContent = {
+ Info()
+ }
+ ) {
+ content()
+ }
+}
+
+@Composable
+fun Info() {
+ Column(modifier = Modifier.navigationBarsPadding()) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Tab(selected = true, onClick = { /*TODO*/ })
+ }
+ }
+}
+
+@Preview
+@Composable
+fun Prev() {
+ Tab(selected = true, onClick = { /*TODO*/ })
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt
index 5f5ab3d..461d83f 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt
@@ -140,6 +140,7 @@ fun SettingsPage(
}
}
item {
+ Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
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 bede78c..c498ac4 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
@@ -181,11 +181,15 @@ fun ColorAndStylePage(
) {}
SettingItem(
title = stringResource(R.string.reading_page),
- enable = false,
- onClick = {},
+ onClick = {
+ navController.navigate(RouteName.READING_PAGE_STYLE) {
+ launchSingleTop = true
+ }
+ },
) {}
}
item {
+ Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt
index c88c07e..e558edf 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt
@@ -78,6 +78,7 @@ fun DarkThemePage(
}
}
item {
+ Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt
index b0ec6cb..73e24cf 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt
@@ -158,7 +158,7 @@ fun FeedsPageStylePage(
}
}
SettingItem(
- title = stringResource(R.string.padding_on_both_ends),
+ title = stringResource(R.string.horizontal_padding),
desc = "${filterBarPadding}dp",
onClick = {
filterBarPaddingValue = filterBarPadding
@@ -197,7 +197,7 @@ fun FeedsPageStylePage(
TextFieldDialog(
visible = filterBarPaddingDialogVisible,
- title = stringResource(R.string.padding_on_both_ends),
+ title = stringResource(R.string.horizontal_padding),
value = (filterBarPaddingValue ?: "").toString(),
placeholder = stringResource(R.string.value),
onValueChange = {
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt
index 434f921..c942a1f 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt
@@ -218,7 +218,7 @@ fun FlowPageStylePage(
}
}
SettingItem(
- title = stringResource(R.string.padding_on_both_ends),
+ title = stringResource(R.string.horizontal_padding),
desc = "${filterBarPadding}dp",
onClick = {
filterBarPaddingValue = filterBarPadding
@@ -234,6 +234,7 @@ fun FlowPageStylePage(
) {}
}
item {
+ Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
@@ -257,7 +258,7 @@ fun FlowPageStylePage(
TextFieldDialog(
visible = filterBarPaddingDialogVisible,
- title = stringResource(R.string.padding_on_both_ends),
+ title = stringResource(R.string.horizontal_padding),
value = (filterBarPaddingValue ?: "").toString(),
placeholder = stringResource(R.string.value),
onValueChange = {
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingDarkThemePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingDarkThemePage.kt
new file mode 100644
index 0000000..dffbbc5
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingDarkThemePage.kt
@@ -0,0 +1,72 @@
+package me.ash.reader.ui.page.settings.color.reading
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.ArrowBack
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.RadioButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import me.ash.reader.R
+import me.ash.reader.data.model.preference.LocalReadingDarkTheme
+import me.ash.reader.data.model.preference.ReadingDarkThemePreference
+import me.ash.reader.ui.component.base.DisplayText
+import me.ash.reader.ui.component.base.FeedbackIconButton
+import me.ash.reader.ui.component.base.RYScaffold
+import me.ash.reader.ui.page.settings.SettingItem
+import me.ash.reader.ui.theme.palette.onLight
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ReadingDarkThemePage(
+ navController: NavHostController,
+) {
+ val context = LocalContext.current
+ val darkTheme = LocalReadingDarkTheme.current
+ val scope = rememberCoroutineScope()
+
+ RYScaffold(
+ containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
+ navigationIcon = {
+ FeedbackIconButton(
+ imageVector = Icons.Rounded.ArrowBack,
+ contentDescription = stringResource(R.string.back),
+ tint = MaterialTheme.colorScheme.onSurface
+ ) {
+ navController.popBackStack()
+ }
+ },
+ content = {
+ LazyColumn {
+ item {
+ DisplayText(text = stringResource(R.string.dark_theme), desc = "")
+ }
+ item {
+ ReadingDarkThemePreference.values.map {
+ SettingItem(
+ title = it.toDesc(context),
+ onClick = {
+ it.put(context, scope)
+ },
+ ) {
+ RadioButton(selected = it == darkTheme, onClick = {
+ it.put(context, scope)
+ })
+ }
+ }
+ }
+ item {
+ Spacer(modifier = Modifier.height(24.dp))
+ Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
+ }
+ }
+ }
+ )
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingImagePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingImagePage.kt
new file mode 100644
index 0000000..a259019
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingImagePage.kt
@@ -0,0 +1,158 @@
+package me.ash.reader.ui.page.settings.color.reading
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.ArrowBack
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+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.page.settings.SettingItem
+import me.ash.reader.ui.theme.palette.onLight
+
+@Composable
+fun ReadingImagePage(
+ navController: NavHostController,
+) {
+ val context = LocalContext.current
+ val scope = rememberCoroutineScope()
+
+ val readingTheme = LocalReadingTheme.current
+ val roundedCorners = LocalReadingImageRoundedCorners.current
+ val horizontalPadding = LocalReadingImageHorizontalPadding.current
+ val maximize = LocalReadingImageMaximize.current
+
+ var roundedCornersDialogVisible by remember { mutableStateOf(false) }
+ var horizontalPaddingDialogVisible by remember { mutableStateOf(false) }
+
+ var roundedCornersValue: Int? by remember { mutableStateOf(roundedCorners) }
+ var horizontalPaddingValue: Int? by remember { mutableStateOf(horizontalPadding) }
+
+ RYScaffold(
+ containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
+ navigationIcon = {
+ FeedbackIconButton(
+ imageVector = Icons.Rounded.ArrowBack,
+ contentDescription = stringResource(R.string.back),
+ tint = MaterialTheme.colorScheme.onSurface
+ ) {
+ navController.popBackStack()
+ }
+ },
+ content = {
+ LazyColumn {
+ item {
+ DisplayText(text = stringResource(R.string.images), desc = "")
+ }
+
+ // Preview
+ item {
+// Row(
+// modifier = Modifier
+// .fillMaxWidth()
+// .padding(horizontal = 24.dp)
+// .clip(RoundedCornerShape(24.dp))
+// .background(
+// MaterialTheme.colorScheme.inverseOnSurface
+// onLight MaterialTheme.colorScheme.surface.copy(0.7f)
+// )
+// .clickable { },
+// horizontalArrangement = Arrangement.Center,
+// verticalAlignment = Alignment.CenterVertically
+// ) {
+// RYAsyncImage(
+// modifier = Modifier
+// .fillMaxSize()
+// .padding(24.dp)
+// .padding(imageHorizontalPadding().dp)
+// .clip(imageShape()),
+// data = "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60",
+// contentDescription = stringResource(R.string.images),
+// contentScale = ContentScale.Inside,
+// )
+// }
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+
+
+ // Images
+ item {
+ Subtitle(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = stringResource(R.string.images)
+ )
+ SettingItem(
+ title = stringResource(R.string.rounded_corners),
+ desc = "${roundedCorners}dp",
+ onClick = { roundedCornersDialogVisible = true },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.horizontal_padding),
+ desc = "${horizontalPadding}dp",
+ onClick = { horizontalPaddingDialogVisible = true },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.maximize),
+ onClick = {
+ (!maximize).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ },
+ ) {
+ RYSwitch(activated = maximize.value) {
+ (!maximize).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ }
+
+ item {
+ Spacer(modifier = Modifier.height(24.dp))
+ Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
+ }
+ }
+ }
+ )
+
+ TextFieldDialog(
+ visible = roundedCornersDialogVisible,
+ title = stringResource(R.string.rounded_corners),
+ value = (roundedCornersValue ?: "").toString(),
+ placeholder = stringResource(R.string.value),
+ onValueChange = {
+ roundedCornersValue = it.filter { it.isDigit() }.toIntOrNull()
+ },
+ onDismissRequest = {
+ roundedCornersDialogVisible = false
+ },
+ onConfirm = {
+ ReadingImageRoundedCornersPreference.put(context, scope, roundedCornersValue ?: 0)
+ ReadingThemePreference.Custom.put(context, scope)
+ roundedCornersDialogVisible = false
+ }
+ )
+
+ TextFieldDialog(
+ visible = horizontalPaddingDialogVisible,
+ title = stringResource(R.string.horizontal_padding),
+ value = (horizontalPaddingValue ?: "").toString(),
+ placeholder = stringResource(R.string.value),
+ onValueChange = {
+ horizontalPaddingValue = it.filter { it.isDigit() }.toIntOrNull()
+ },
+ onDismissRequest = {
+ horizontalPaddingDialogVisible = false
+ },
+ onConfirm = {
+ ReadingImageHorizontalPaddingPreference.put(context, scope, horizontalPaddingValue ?: 0)
+ ReadingThemePreference.Custom.put(context, scope)
+ horizontalPaddingDialogVisible = false
+ }
+ )
+}
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
new file mode 100644
index 0000000..e3fb5f7
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt
@@ -0,0 +1,259 @@
+package me.ash.reader.ui.page.settings.color.reading
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Image
+import androidx.compose.material.icons.outlined.Movie
+import androidx.compose.material.icons.rounded.ArrowBack
+import androidx.compose.material.icons.rounded.Segment
+import androidx.compose.material.icons.rounded.Title
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+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.ReadingThemePrev
+import me.ash.reader.ui.component.base.*
+import me.ash.reader.ui.page.common.RouteName
+import me.ash.reader.ui.page.settings.SettingItem
+import me.ash.reader.ui.theme.palette.onLight
+
+@Composable
+fun ReadingStylePage(
+ navController: NavHostController,
+) {
+ val context = LocalContext.current
+ val scope = rememberCoroutineScope()
+
+ val readingTheme = LocalReadingTheme.current
+ val darkTheme = LocalReadingDarkTheme.current
+ val darkThemeNot = !darkTheme
+ val tonalElevation = LocalReadingPageTonalElevation.current
+ val fonts = LocalReadingFonts.current
+ val autoHideToolbar = LocalReadingAutoHideToolbar.current
+
+ var tonalElevationDialogVisible by remember { mutableStateOf(false) }
+ var fontsDialogVisible by remember { mutableStateOf(false) }
+
+ RYScaffold(
+ containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
+ navigationIcon = {
+ FeedbackIconButton(
+ imageVector = Icons.Rounded.ArrowBack,
+ contentDescription = stringResource(R.string.back),
+ tint = MaterialTheme.colorScheme.onSurface
+ ) {
+ navController.popBackStack()
+ }
+ },
+ content = {
+ LazyColumn {
+ item {
+ DisplayText(text = stringResource(R.string.reading_page), desc = "")
+ }
+
+ // Preview
+ item {
+ Row(modifier = Modifier.horizontalScroll(rememberScrollState())
+ ) {
+ Spacer(modifier = Modifier.width(24.dp))
+ ReadingThemePreference.values.map {
+ if (readingTheme == ReadingThemePreference.Custom || it != ReadingThemePreference.Custom) {
+ ReadingThemePrev(selected = readingTheme, theme = it) {
+ it.put(context, scope)
+ it.applyTheme(context, scope)
+ }
+ } else {
+ Spacer(modifier = Modifier.width(150.dp))
+ }
+ Spacer(modifier = Modifier.width(8.dp))
+ }
+ Spacer(modifier = Modifier.width((24 - 8).dp))
+ }
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp)
+ .clip(RoundedCornerShape(24.dp))
+ .background(
+ MaterialTheme.colorScheme.inverseOnSurface
+ onLight MaterialTheme.colorScheme.surface.copy(0.7f)
+ )
+ .clickable { },
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+
+ }
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+
+ // General
+ item {
+ Subtitle(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = stringResource(R.string.general)
+ )
+ SettingItem(
+ title = stringResource(R.string.reading_fonts),
+ desc = fonts.toDesc(context),
+ onClick = { fontsDialogVisible = true },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.dark_reading_theme),
+ desc = darkTheme.toDesc(context),
+ separatedActions = true,
+ onClick = {
+ navController.navigate(RouteName.READING_DARK_THEME) {
+ launchSingleTop = true
+ }
+ },
+ ) {
+ RYSwitch(
+ activated = darkTheme.isDarkTheme()
+ ) {
+ darkThemeNot.put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.bionic_reading),
+ separatedActions = true,
+ enable = false,
+ onClick = {
+// (!articleListDesc).put(context, scope)
+ },
+ ) {
+ RYSwitch(
+ activated = false,
+ enable = false,
+ ) {
+// (!articleListDesc).put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.auto_hide_toolbars),
+ onClick = {
+ (!autoHideToolbar).put(context, scope)
+ },
+ ) {
+ RYSwitch(activated = autoHideToolbar.value) {
+ (!autoHideToolbar).put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.rearrange_buttons),
+ enable = false,
+ onClick = {},
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.tonal_elevation),
+ desc = "${tonalElevation.value}dp",
+ onClick = {
+ tonalElevationDialogVisible = true
+ },
+ ) {}
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+
+ // Advanced
+ item {
+ Subtitle(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = stringResource(R.string.advanced)
+ )
+ SettingItem(
+ title = stringResource(R.string.title),
+ desc = stringResource(R.string.title_desc),
+ icon = Icons.Rounded.Title,
+ onClick = {
+ navController.navigate(RouteName.READING_PAGE_TITLE) {
+ launchSingleTop = true
+ }
+ },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.text),
+ desc = stringResource(R.string.text_desc),
+ icon = Icons.Rounded.Segment,
+ onClick = {
+ navController.navigate(RouteName.READING_PAGE_TEXT) {
+ launchSingleTop = true
+ }
+ },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.images),
+ desc = stringResource(R.string.images_desc),
+ icon = Icons.Outlined.Image,
+ onClick = {
+ navController.navigate(RouteName.READING_PAGE_IMAGE) {
+ launchSingleTop = true
+ }
+ },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.videos),
+ desc = stringResource(R.string.videos_desc),
+ icon = Icons.Outlined.Movie,
+ enable = false,
+ onClick = {
+// navController.navigate(RouteName.READING_PAGE_VIDEO) {
+// launchSingleTop = true
+// }
+ },
+ ) {}
+ }
+
+ item {
+ Spacer(modifier = Modifier.height(24.dp))
+ Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
+ }
+ }
+ }
+ )
+
+ RadioDialog(
+ visible = tonalElevationDialogVisible,
+ title = stringResource(R.string.tonal_elevation),
+ options = ReadingPageTonalElevationPreference.values.map {
+ RadioDialogOption(
+ text = it.toDesc(context),
+ selected = it == tonalElevation,
+ ) {
+ it.put(context, scope)
+ }
+ }
+ ) {
+ tonalElevationDialogVisible = false
+ }
+
+ RadioDialog(
+ visible = fontsDialogVisible,
+ title = stringResource(R.string.reading_fonts),
+ options = ReadingFontsPreference.values.map {
+ RadioDialogOption(
+ text = it.toDesc(context),
+ style = TextStyle(fontFamily = it.asFontFamily()),
+ selected = it == fonts,
+ ) {
+ it.put(context, scope)
+ }
+ }
+ ) {
+ fontsDialogVisible = false
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingTextPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingTextPage.kt
new file mode 100644
index 0000000..aeb4d63
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingTextPage.kt
@@ -0,0 +1,202 @@
+package me.ash.reader.ui.page.settings.color.reading
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.ArrowBack
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+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.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.page.settings.SettingItem
+import me.ash.reader.ui.theme.palette.onLight
+
+@Composable
+fun ReadingTextPage(
+ navController: NavHostController,
+) {
+ val context = LocalContext.current
+ val scope = rememberCoroutineScope()
+
+ val readingTheme = LocalReadingTheme.current
+ val fontSize = LocalReadingTextFontSize.current
+ val letterSpacing = LocalReadingLetterSpacing.current
+ val horizontalPadding = LocalReadingTextHorizontalPadding.current
+ val align = LocalReadingTextAlign.current
+ val bold = LocalReadingTextBold.current
+
+ var fontSizeDialogVisible by remember { mutableStateOf(false) }
+ var letterSpacingDialogVisible by remember { mutableStateOf(false) }
+ var horizontalPaddingDialogVisible by remember { mutableStateOf(false) }
+ var alignDialogVisible by remember { mutableStateOf(false) }
+
+ var fontSizeValue: Int? by remember { mutableStateOf(fontSize) }
+ var letterSpacingValue: String? by remember { mutableStateOf(letterSpacing.toString()) }
+ var horizontalPaddingValue: Int? by remember { mutableStateOf(horizontalPadding) }
+
+ RYScaffold(
+ containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
+ navigationIcon = {
+ FeedbackIconButton(
+ imageVector = Icons.Rounded.ArrowBack,
+ contentDescription = stringResource(R.string.back),
+ tint = MaterialTheme.colorScheme.onSurface
+ ) {
+ navController.popBackStack()
+ }
+ },
+ content = {
+ LazyColumn {
+ item {
+ DisplayText(text = stringResource(R.string.text), desc = "")
+ }
+
+ // Preview
+ item {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp)
+ .clip(RoundedCornerShape(24.dp))
+ .background(
+ MaterialTheme.colorScheme.inverseOnSurface
+ onLight MaterialTheme.colorScheme.surface.copy(0.7f)
+ )
+ .clickable { },
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ TitleAndTextPreview()
+ }
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+
+ // Text
+ item {
+ Subtitle(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = stringResource(R.string.text)
+ )
+ SettingItem(
+ title = stringResource(R.string.font_size),
+ desc = "${fontSize}sp",
+ onClick = { fontSizeDialogVisible = true },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.bold),
+ onClick = {
+ (!bold).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ },
+ ) {
+ RYSwitch(activated = bold.value) {
+ (!bold).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.letter_spacing),
+ desc = "${letterSpacing}sp",
+ onClick = { letterSpacingDialogVisible = true },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.horizontal_padding),
+ desc = "${horizontalPadding}dp",
+ onClick = { horizontalPaddingDialogVisible = true },
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.alignment),
+ desc = align.toDesc(context),
+ onClick = { alignDialogVisible = true },
+ ) {}
+ }
+
+ item {
+ Spacer(modifier = Modifier.height(24.dp))
+ Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
+ }
+ }
+ }
+ )
+
+ TextFieldDialog(
+ visible = fontSizeDialogVisible,
+ title = stringResource(R.string.font_size),
+ value = (fontSizeValue ?: "").toString(),
+ placeholder = stringResource(R.string.value),
+ onValueChange = {
+ fontSizeValue = it.filter { it.isDigit() }.toIntOrNull()
+ },
+ onDismissRequest = {
+ fontSizeDialogVisible = false
+ },
+ onConfirm = {
+ ReadingTextFontSizePreference.put(context, scope, fontSizeValue ?: 0)
+ ReadingThemePreference.Custom.put(context, scope)
+ fontSizeDialogVisible = false
+ }
+ )
+
+ TextFieldDialog(
+ visible = letterSpacingDialogVisible,
+ title = stringResource(R.string.letter_spacing),
+ value = (letterSpacingValue ?: "").toString(),
+ placeholder = stringResource(R.string.value),
+ onValueChange = {
+ letterSpacingValue = it
+ },
+ onDismissRequest = {
+ letterSpacingDialogVisible = false
+ },
+ onConfirm = {
+ ReadingLetterSpacingPreference.put(context, scope, letterSpacingValue?.toDoubleOrNull() ?: 0.0)
+ ReadingThemePreference.Custom.put(context, scope)
+ letterSpacingDialogVisible = false
+ }
+ )
+
+ TextFieldDialog(
+ visible = horizontalPaddingDialogVisible,
+ title = stringResource(R.string.horizontal_padding),
+ value = (horizontalPaddingValue ?: "").toString(),
+ placeholder = stringResource(R.string.value),
+ onValueChange = {
+ horizontalPaddingValue = it.filter { it.isDigit() }.toIntOrNull()
+ },
+ onDismissRequest = {
+ horizontalPaddingDialogVisible = false
+ },
+ onConfirm = {
+ ReadingTextHorizontalPaddingPreference.put(context, scope, horizontalPaddingValue ?: 0)
+ ReadingThemePreference.Custom.put(context, scope)
+ horizontalPaddingDialogVisible = false
+ }
+ )
+
+ RadioDialog(
+ visible = alignDialogVisible,
+ title = stringResource(R.string.alignment),
+ options = ReadingTextAlignPreference.values.map {
+ RadioDialogOption(
+ text = it.toDesc(context),
+ selected = it == align,
+ ) {
+ it.put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ ) {
+ alignDialogVisible = false
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingTitlePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingTitlePage.kt
new file mode 100644
index 0000000..6be85a1
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingTitlePage.kt
@@ -0,0 +1,196 @@
+package me.ash.reader.ui.page.settings.color.reading
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.ArrowBack
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+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.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.page.settings.SettingItem
+import me.ash.reader.ui.theme.palette.onLight
+
+@Composable
+fun ReadingTitlePage(
+ navController: NavHostController,
+) {
+ val context = LocalContext.current
+ val scope = rememberCoroutineScope()
+
+ val titleBold = LocalReadingTitleBold.current
+ val subtitleBold = LocalReadingSubheadBold.current
+ val titleAlign = LocalReadingTitleAlign.current
+ val subtitleAlign = LocalReadingSubheadAlign.current
+ val titleUpperCase = LocalReadingTitleUpperCase.current
+ val subheadUpperCase = LocalReadingSubheadUpperCase.current
+
+ var titleAlignDialogVisible by remember { mutableStateOf(false) }
+ var subtitleAlignDialogVisible by remember { mutableStateOf(false) }
+
+ RYScaffold(
+ containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
+ navigationIcon = {
+ FeedbackIconButton(
+ imageVector = Icons.Rounded.ArrowBack,
+ contentDescription = stringResource(R.string.back),
+ tint = MaterialTheme.colorScheme.onSurface
+ ) {
+ navController.popBackStack()
+ }
+ },
+ content = {
+ LazyColumn {
+ item {
+ DisplayText(text = stringResource(R.string.title), desc = "")
+ }
+
+ // Preview
+ item {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp)
+ .clip(RoundedCornerShape(24.dp))
+ .background(
+ MaterialTheme.colorScheme.inverseOnSurface
+ onLight MaterialTheme.colorScheme.surface.copy(0.7f)
+ )
+ .clickable { },
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ TitleAndTextPreview()
+ }
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+
+ // Title
+ item {
+ Subtitle(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = stringResource(R.string.title)
+ )
+ SettingItem(
+ title = stringResource(R.string.bold),
+ onClick = {
+ (!titleBold).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ },
+ ) {
+ RYSwitch(activated = titleBold.value) {
+ (!titleBold).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.upper_case),
+ onClick = {
+ (!titleUpperCase).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ },
+ ) {
+ RYSwitch(activated = titleUpperCase.value) {
+ (!titleUpperCase).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.alignment),
+ desc = titleAlign.toDesc(context),
+ onClick = { titleAlignDialogVisible = true },
+ ) {}
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+
+ // Subhead
+ item {
+ Subtitle(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = stringResource(R.string.subhead)
+ )
+ SettingItem(
+ title = stringResource(R.string.bold),
+ onClick = {
+ (!subtitleBold).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ },
+ ) {
+ RYSwitch(activated = subtitleBold.value) {
+ (!subtitleBold).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.upper_case),
+ onClick = {
+ (!subheadUpperCase).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ },
+ ) {
+ RYSwitch(activated = subheadUpperCase.value) {
+ (!subheadUpperCase).put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ SettingItem(
+ title = stringResource(R.string.alignment),
+ desc = subtitleAlign.toDesc(context),
+ enable = false,
+ onClick = {
+// subtitleAlignDialogVisible = true
+ },
+ ) {}
+ }
+
+ item {
+ Spacer(modifier = Modifier.height(24.dp))
+ Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
+ }
+ }
+ }
+ )
+
+ RadioDialog(
+ visible = titleAlignDialogVisible,
+ title = stringResource(R.string.alignment),
+ options = ReadingTitleAlignPreference.values.map {
+ RadioDialogOption(
+ text = it.toDesc(context),
+ selected = it == titleAlign,
+ ) {
+ it.put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ ) {
+ titleAlignDialogVisible = false
+ }
+
+ RadioDialog(
+ visible = subtitleAlignDialogVisible,
+ title = stringResource(R.string.alignment),
+ options = ReadingSubheadAlignPreference.values.map {
+ RadioDialogOption(
+ text = it.toDesc(context),
+ selected = it == subtitleAlign,
+ ) {
+ it.put(context, scope)
+ ReadingThemePreference.Custom.put(context, scope)
+ }
+ }
+ ) {
+ subtitleAlignDialogVisible = false
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingVideoPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingVideoPage.kt
new file mode 100644
index 0000000..cf34fa3
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingVideoPage.kt
@@ -0,0 +1,102 @@
+package me.ash.reader.ui.page.settings.color.reading
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.ArrowBack
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+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.unit.dp
+import androidx.navigation.NavHostController
+import me.ash.reader.R
+import me.ash.reader.ui.component.base.DisplayText
+import me.ash.reader.ui.component.base.FeedbackIconButton
+import me.ash.reader.ui.component.base.RYScaffold
+import me.ash.reader.ui.component.base.Subtitle
+import me.ash.reader.ui.page.settings.SettingItem
+import me.ash.reader.ui.theme.palette.onLight
+
+@Composable
+fun ReadingVideoPage(
+ navController: NavHostController,
+) {
+ val context = LocalContext.current
+ val scope = rememberCoroutineScope()
+
+ RYScaffold(
+ containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
+ navigationIcon = {
+ FeedbackIconButton(
+ imageVector = Icons.Rounded.ArrowBack,
+ contentDescription = stringResource(R.string.back),
+ tint = MaterialTheme.colorScheme.onSurface
+ ) {
+ navController.popBackStack()
+ }
+ },
+ content = {
+ LazyColumn {
+ item {
+ DisplayText(text = stringResource(R.string.videos), desc = "")
+ }
+
+ // Preview
+ item {
+ Row(modifier = Modifier.horizontalScroll(rememberScrollState())
+ ) {
+ }
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp)
+ .clip(RoundedCornerShape(24.dp))
+ .background(
+ MaterialTheme.colorScheme.inverseOnSurface
+ onLight MaterialTheme.colorScheme.surface.copy(0.7f)
+ )
+ .clickable { },
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+
+ }
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+
+ // Videos
+ item {
+ Subtitle(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = stringResource(R.string.videos)
+ )
+ SettingItem(
+ title = stringResource(R.string.rounded_corners),
+ onClick = {},
+ ) {}
+ SettingItem(
+ title = stringResource(R.string.horizontal_padding),
+ desc = "dp",
+ onClick = {},
+ ) {}
+ }
+
+ item {
+ Spacer(modifier = Modifier.height(24.dp))
+ Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
+ }
+ }
+ }
+ )
+}
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
new file mode 100644
index 0000000..f02f2d8
--- /dev/null
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/TitleAndTextPreview.kt
@@ -0,0 +1,71 @@
+package me.ash.reader.ui.page.settings.color.reading
+
+import androidx.compose.foundation.layout.*
+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.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import me.ash.reader.R
+import me.ash.reader.data.model.preference.*
+import me.ash.reader.ui.component.reader.bodyStyle
+import me.ash.reader.ui.component.reader.h3Style
+import me.ash.reader.ui.component.reader.textHorizontalPadding
+
+@Composable
+fun TitleAndTextPreview() {
+ val context = LocalContext.current
+ val titleBold = LocalReadingTitleBold.current
+ val subtitleBold = LocalReadingSubheadBold.current
+ val titleUpperCase = LocalReadingTitleUpperCase.current
+ val subheadUpperCase = LocalReadingSubheadUpperCase.current
+ val titleAlign = LocalReadingTitleAlign.current
+ val subtitleAlign = LocalReadingSubheadAlign.current
+
+
+ val titleUpperCaseString by remember {
+ derivedStateOf {
+ context.getString(R.string.title).uppercase()
+ }
+ }
+
+ val subheadUpperCaseString by remember {
+ derivedStateOf {
+ context.getString(R.string.subhead).uppercase()
+ }
+ }
+ Column(modifier = Modifier.padding(24.dp)) {
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = textHorizontalPadding().dp),
+ text = if (titleUpperCase.value) titleUpperCaseString else stringResource(id = R.string.title),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.headlineLarge.copy(
+ fontFamily = LocalReadingFonts.current.asFontFamily(isDisplay = true),
+ fontWeight = if (titleBold.value) FontWeight.SemiBold else FontWeight.Normal,
+ ),
+ textAlign = titleAlign.toTextAlign(),
+ )
+ Spacer(modifier = Modifier.height(20.dp))
+ Text(
+ text = if (subheadUpperCase.value) subheadUpperCaseString else stringResource(id = R.string.subhead),
+ style = h3Style().copy(textAlign = bodyStyle().textAlign),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = textHorizontalPadding().dp)
+ )
+ Spacer(modifier = Modifier.height(20.dp))
+ Text(
+ text = stringResource(id = R.string.preview_article_desc),
+ style = bodyStyle(),
+ modifier = Modifier.padding(horizontal = textHorizontalPadding().dp)
+ )
+ }
+}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt
index df6f89f..026e245 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt
@@ -69,6 +69,7 @@ fun InteractionPage(
) {}
}
item {
+ Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt
index e71a3e1..f64929a 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt
@@ -88,6 +88,7 @@ fun LanguagesPage(
}
}
item {
+ Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
diff --git a/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt b/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt
index 9905264..54f6b22 100644
--- a/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt
+++ b/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt
@@ -16,6 +16,9 @@ val Shapes = Shapes(
@Stable
val Shape20 = RoundedCornerShape(20.0.dp)
+@Stable
+val Shape24 = RoundedCornerShape(24.0.dp)
+
@Stable
val Shape32 = RoundedCornerShape(32.0.dp)
diff --git a/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt b/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt
index 3dbc416..74685c3 100644
--- a/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt
+++ b/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt
@@ -5,6 +5,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.Color
import me.ash.reader.data.model.preference.LocalAmoledDarkTheme
@@ -80,7 +81,9 @@ infix fun Color.onLight(lightColor: Color): Color =
infix fun Color.onDark(darkColor: Color): Color =
if (LocalDarkTheme.current.isDarkTheme()) darkColor else this
+@Stable
@Composable
+@ReadOnlyComposable
infix fun Color.alwaysLight(isAlways: Boolean): Color {
val colorScheme = MaterialTheme.colorScheme
return if (isAlways && LocalDarkTheme.current.isDarkTheme()) {
@@ -120,6 +123,48 @@ infix fun Color.alwaysLight(isAlways: Boolean): Color {
}
}
+@Stable
+@Composable
+@ReadOnlyComposable
+infix fun Color.alwaysDark(isAlways: Boolean): Color {
+ val colorScheme = MaterialTheme.colorScheme
+ return if (isAlways && !LocalDarkTheme.current.isDarkTheme()) {
+ when (this) {
+ colorScheme.primary -> colorScheme.onPrimary
+ colorScheme.secondary -> colorScheme.onSecondary
+ colorScheme.tertiary -> colorScheme.onTertiary
+ colorScheme.background -> colorScheme.onBackground
+ colorScheme.error -> colorScheme.onError
+ colorScheme.surface -> colorScheme.onSurface
+ colorScheme.surfaceVariant -> colorScheme.onSurfaceVariant
+ colorScheme.error -> colorScheme.onError
+ colorScheme.primaryContainer -> colorScheme.onPrimaryContainer
+ colorScheme.secondaryContainer -> colorScheme.onSecondaryContainer
+ colorScheme.tertiaryContainer -> colorScheme.onTertiaryContainer
+ colorScheme.errorContainer -> colorScheme.onErrorContainer
+ colorScheme.inverseSurface -> colorScheme.inverseOnSurface
+
+ colorScheme.onPrimary -> colorScheme.primary
+ colorScheme.onSecondary -> colorScheme.secondary
+ colorScheme.onTertiary -> colorScheme.tertiary
+ colorScheme.onBackground -> colorScheme.background
+ colorScheme.onError -> colorScheme.error
+ colorScheme.onSurface -> colorScheme.surface
+ colorScheme.onSurfaceVariant -> colorScheme.surfaceVariant
+ colorScheme.onError -> colorScheme.error
+ colorScheme.onPrimaryContainer -> colorScheme.primaryContainer
+ colorScheme.onSecondaryContainer -> colorScheme.secondaryContainer
+ colorScheme.onTertiaryContainer -> colorScheme.tertiaryContainer
+ colorScheme.onErrorContainer -> colorScheme.errorContainer
+ colorScheme.inverseOnSurface -> colorScheme.inverseSurface
+
+ else -> Color.Unspecified
+ }
+ } else {
+ this
+ }
+}
+
fun String.checkColorHex(): String? {
var s = this.trim()
if (s.length > 6) {
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 44a5b15..d040b59 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -239,7 +239,7 @@
Ostatní
Tmavé téma AMOLED
Zvýraznění tónů
- Písma
+ Přečtení písma
Základní písma
Stránka se zdroji
Stránka Flow
@@ -263,7 +263,7 @@
Blázen je osmým a závěrečným dílem série Pán záhad, jehož autorem je sépie, která se ráda potápí.
Reddit
value
- Odsazení na obou koncích
+ Horizontální polstrování
Čas zveřejnění článku
Popis článku
Obrázky článku
@@ -286,4 +286,33 @@
Zvýšení tónu seznamu skupin je dostupné pouze ve světlém režimu.
Sdílet
Klepněte pro přehráníé videa
+ Text
+ Font size
+ Letter spacing
+ Line spacing
+ Alignment
+ General
+ Auto hide toolbars
+ Rearrange buttons
+ Bionic Reading
+ Images
+ Rounded corners
+ Videos
+ Align left
+ Align right
+ Center text
+ Justify
+ External fonts
+ Title
+ Bold
+ Upper case
+ Subhead
+ Use app theme
+ Advanced
+ Dark reading theme
+ Bold, upper case, alignment
+ Font size, letter spacing, alignment
+ Rounded corners, horizontal padding
+ Rounded corners, horizontal padding
+ Maximize
diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml
index 4a280e8..b0956bb 100644
--- a/app/src/main/res/values-de-rDE/strings.xml
+++ b/app/src/main/res/values-de-rDE/strings.xml
@@ -98,7 +98,7 @@
Interaktion
Beim Start, haptisches Feedback
Sprachen
- English, Deutsch, mehr
+ Deutsch, Englisch, andere
Hilf uns beim Übersetzen
Gerätesprache verwenden
Tipps & Support
@@ -233,7 +233,7 @@
Sonstiges
AMOLED Dunkles Design
Farbliches Hervorheben
- Schriftarten
+ Leseschriftarten
Grundschriftarten
Feeds Seite
Flow Seite
@@ -257,7 +257,7 @@
The Fool ist der achte und letzte Band der Lord of the Mysteries Reihe, geschrieben von Cuttlefish That Loves Diving.
Reddit
Wert
- Rand an beiden Enden
+ Horizontaler Rand
Artikel Veröffentlichungszeit
Artikel Beschreibungen
Artikel Bilder
@@ -278,6 +278,35 @@
Das farbliche Hervorheben der oberen Leiste ist nur beim Scrollen verfügbar.
Das farbliche Hervorheben der Artikelliste ist nur für das helle Design verfügbar.
Das farbliche Hervorheben der Gruppenliste ist nur für das helle Design verfügbar.
- Aktie
+ Teilen
Berühren, um Video abzuspielen
+ Text
+ Schriftgröße
+ Zeichenabstand
+ Zeilenabstand
+ Ausrichtung
+ Allgemein
+ Leisten automatisch ausblenden
+ Schaltflächen umpositionieren
+ Bionic Reading
+ Bilder
+ Abgerundete Ecken
+ Videos
+ Linksbündig
+ Rechtsbündig
+ Zentriert
+ Blocksatz
+ Externe Schriftart
+ Titel
+ Fettgedruckt
+ Großbuchstaben
+ Untertitel
+ Appdesign verwenden
+ Erweitert
+ Dunkles Lesedesign
+ Fettgedruckt, Großbuchstaben, Ausrichtung
+ Schriftgröße, Zeichenabstand, Ausrichtung
+ Abgerundete Ecken, Horizontaler Rand
+ Abgerundete Ecken, Horizontaler Rand
+ Maximieren
diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml
index 914ef0a..cbe624d 100644
--- a/app/src/main/res/values-fr-rFR/strings.xml
+++ b/app/src/main/res/values-fr-rFR/strings.xml
@@ -236,7 +236,7 @@
Autres options
Thème sombre AMOLED
Intensité
- Polices
+ Polices de lecture
Polices de base
Page des flux
Page des articles
@@ -260,7 +260,7 @@
Le Fou est le huitième et dernier volume de la série Le Seigneur des Mystères écrite par Cuttlefish That Loves Diving.
Reddit
Valeur
- Espacement des deux côtés
+ Rembourrage Horizontal
Heure de publication des articles
Descriptions des articles
Images des articles
@@ -283,4 +283,33 @@
L\'intensité de la liste des groupes est uniquement disponible pour le thème clair.
Partager
Appuyer pour lancer la lecture
+ Text
+ Font size
+ Letter spacing
+ Line spacing
+ Alignment
+ General
+ Auto hide toolbars
+ Rearrange buttons
+ Bionic Reading
+ Images
+ Rounded corners
+ Videos
+ Align left
+ Align right
+ Center text
+ Justify
+ External fonts
+ Title
+ Bold
+ Upper case
+ Subhead
+ Use app theme
+ Advanced
+ Dark reading theme
+ Bold, upper case, alignment
+ Font size, letter spacing, alignment
+ Rounded corners, horizontal padding
+ Rounded corners, horizontal padding
+ Maximize
diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml
new file mode 100644
index 0000000..60958e1
--- /dev/null
+++ b/app/src/main/res/values-hi-rIN/strings.xml
@@ -0,0 +1,311 @@
+
+ सभी
+
+ - %1$d संग्रहित पाठ
+ - %1$d संग्रहित पाठ
+
+ अपठित पाठ
+
+ - %1$d अपठित पाठ
+ - %1$d अपठित पाठ
+
+ चिन्हित
+
+ - %1$d चिन्हित पाठ
+ - %1$d चिन्हित पाठ
+
+ फीड
+ सिंक्रनाइज़ किए जा रहे…
+ लोड हो रहा है…
+ समेटे
+ विस्तृत करें
+ पुष्टि करें
+ रद्द करें
+ अनुमति
+ अस्वीकार
+ सामान्य विकल्प
+ अज्ञात
+ पीछे
+ गो टू
+ सेटिंग्स
+ रिफ्रेश
+ खोजो
+ खोज रहे है …
+ सदस्यता लें
+ सदस्यता ले ली है
+ क्लिअर
+ पेस्ट
+ यु अरे एल अथवा फ़ीड्स
+ ओ पी एम् एल से आयात करें
+ प्रीसेट
+ चयनित
+ नोटिफिकेशन की अनुमति दें
+ \"%1$s\" ग्रुप के सभी फ़ीड्स को नोटीफिकेशन की स्वीकृति दें
+ \"%1$s\" ग्रुप के सभी नोटीफिकेशन स्वीकृत है
+ \"%1$s\" ग्रुप के सभी नोटीफिकेशन अस्वीकृत है
+ पूरा पाठ दिखाएँ
+ \"%1$s\" ग्रुप के सभी पाठ पूरे दिखाएँ
+ \"%1$s\" ग्रुप के सभी पाठ पूरे दिखाएँ
+ \"%1$s\" ग्रुप के सभी पाठ पूरे ना दिखाएँ
+ पाठ हटाएँ
+ \"%1$s\" फीड के सभी संग्रहित पाठो को हटाएँ
+ \"%1$s\"फीड के सभी संग्रहित पाठ हटा दिए है
+ \"%1$s\" फीड के सभी संग्रहित पाठ हटाएँ .
+ \"%1$s\" ग्रुप के सभी संग्रहित पाठो को हटाएँ.
+ ग्रुप में जोड़ें
+ ग्रुप में स्तानान्तरित करें
+ \"%1$s\" ग्रुप के सभी पाठो को \"%2$s\" में स्थानांतरित करें .
+ \"%1$s\" ग्रुप में स्थानांतरण पूरा हुआ
+ नाम बदलें
+ यु आर एल बदलें
+ नया नाम \"%1$s\"
+ नया ग्रुप बनाएँ
+ नाम
+ खोलें %1$s
+ विकल्प
+ मिटाएं
+ मिट गया \"%1$s\"
+ सदस्यता छोडें
+ \"%1$s\" से सदस्यता छोडें और सभी संग्रहित पाठों को मिटाएं
+ ग्रुप को मिटाएँ
+ \"%1$s\" ग्रुप के सभी फ़ीड्स अथवा संग्रहित पाठों को मिटाएँ
+ निम्नलिखित विकल्प ग्रुप के सभी फ़ीडस् पर लागू होंगे
+ आज
+ कल
+ तिथि %1$s समय %2$s
+ %1$s के लिए \"%2$s\ में खोजें "
+ पाठों में %1$s खोजें
+ पढ़ा हुआ चिन्हित करें
+ सभी को पढ़ा हुआ चिन्हित करें
+ अपठित के रूप में चिह्नित करें
+ तारांकित के रूप में चिह्नित करें
+ अतारांकित के रूप में चिह्नित करें
+ 1 दिन से अधिक पढ़ें के रूप में चिह्नित करें
+ 3 दिन से अधिक पढ़ें के रूप में चिह्नित करें
+ 7 दिन से अधिक पढ़ें के रूप में चिह्नित करें
+ 1 दिन
+ 3 दिन
+ 7 दिन
+ बंद करें
+ नया अपडेट प्राप्त करें
+ %1$s वर्ज़न उपलब्ध है
+ कोडिंग चल रही है....
+ जल्द आ रहा है...
+ खाते
+ लोकल, नया आर एस एस
+ रंग अथवा शैली
+ थीम , रंग शैली , लिपि आकार
+ Interaction
+ शुरू होने पे , हैप्टिक फीडबैक
+ भाषाएँ
+ अंग्रेजी , चाइनीज, और..
+ अनुवाद में मदद करें
+ डिवाइस भाषा का उपयोग करें
+ सुझाव और सहायता
+ परिचय, ओपन सोर्स लाइसेंस
+ स्वागत है
+ आगे बढ़ने के लिए Read Youकी सेवा शर्तें और गोपनीयता नीति को पढ़े और स्वीकार करें
+ यहाँ <i><u>सेवा शर्तें और गोपनीयता नीति पढ़े </u></i>
+
+ सेवा की शर्तें
+
+ <h5>
+
+ गोपनीयता नीति
+
+ </h5>
+ <br/>
+ <p>
+
+ मैं आपकी गोपनीयता को बहुत गंभीरता से लेता हूं
+
+ </p>
+ <br/>
+ <p>
+ <b>Read You</b>
+
+ कोई उपयोगकर्ता डेटा एकत्र नहीं करता है, और सभी संवेदनशील जानकारी (पासवर्ड और अन्य खाता जानकारी)
+ आपके डिवाइस पर स्थानीय एप्लिकेशन डेटाबेस में सुरक्षित रूप से संग्रहीत है|
+
+ </p>
+ <br/>
+ <p>
+ <b>Read You</b>
+
+ आपको सेवा प्रदान करने के लिए निम्नलिखित अनुमतियों का उपयोग किया जाएगा।
+
+ </p>
+ <br/>
+ <p>
+
+ - एक्सेस नेटवर्क अनुमति (आपके द्वारा निर्देशित ऑनलाइन सामग्री तक पहुँचने के लिए)
+
+ </p>
+ <p>
+
+ - नेटवर्क स्थिति की अनुमति (यह जानने करने के लिए कि क्या डिवाइस में वर्तमान में नेटवर्क की स्थिति उपलब्ध है)
+
+ </p>
+ <p>
+
+ -पृष्ठभूमिक सेवा अनुमति (नियमित रूप से अपने पसंदीदार पाठों को स्वचालित रूप से नियमित सिंक करने के लिए आधार)
+
+ </p>
+ <br/>
+ <br/>
+ <h5>
+
+ तृतीय पक्ष सेवाएं
+
+ </h5>
+ <br/>
+ <p>
+
+ यह नीति आपके द्वारा <b>Read You</b>के साथ उपयोग की जाने वाली तृतीय-पक्ष सेवाओं पर लागू नहीं होती है. आप उन वेबसाइटों की गोपनीयता नीतियों की समीक्षा
+ करने हेतु स्वतंत्र है|
+
+ </p>
+ <br/>
+ <br/>
+ <h5>
+
+ अस्वीकरण
+
+ </h5>
+ <br/>
+ <p>
+ <b>Read You</b>
+
+ केवल एक सामग्री संग्रह उपकरण है।. आपके द्वारा <b>Read You</b> का उपयोग आपके देश/राष्ट्र और क्षेत्र के नियम ऐवं कानून के अंतर्गत है
+ और आपके कार्यों से उत्पन्न होने वाले किसी भी दायित्व का वहन आपके द्वारा ही किया जाएगा |
+
+ </p>
+ <br/>
+ <br/>
+ <h5>
+
+ ओपन सोर्स लाइसेंस
+
+ </h5>
+ <br/>
+ <p>
+ <b>Read You</b>
+
+ GNU GPL 3.0 Open Source License[1] के अंतर्गत एक ओपन सौरसे प्रोजेक्ट है, जो आपको <b>Read You</b> के सोर्स कोड को उपयोग,
+ प्रासंगिक सन्दर्भ और मुफ्त में संशोधित करेने के अनुमति देता है, परन्तु संशोधित और व्युत्पन्न कोड को क्लोज्ड-सोर्स वाणिज्यिक सॉफ्टवेयर के रूप में वितरित और बेचा जाने की अनुमति नहीं देता ।
+ पूर्ण जानकारी हेतु GNU GPL 3.0 Open Source License[2]. देखें।
+
+ </p>
+ <br/>
+ <br/>
+ <h5>
+
+ शेषसंग्रह
+
+ </h5>
+ <br/>
+ <p>
+
+ - [1] https://github.com/Ashinch/ReadYou
+
+ </p>
+ <p>
+
+ - [2] https://www.gnu.org/licenses/gpl-3.0.html
+
+ </p>
+
+ स्वीकृत करें
+ वॉलपेपर रंग
+ कोई पैलेट नहीं
+ केवल Android 8.1+ हेतु
+ मूल रंग
+ प्राथमिक रंग
+ जैसे #666666 या 666666
+ रूप
+ शैली
+ डार्क थीम
+ डिवाइस थीम का उपयोग करें
+ ओन
+ ऑफ
+ अन्य
+ अमोलेड डार्क थीम
+ टोनल एलिवेशन
+ रीडिंग फोंट
+ बेसिक फोंट
+ फ़ीड्स पेज
+ फ्लो पेज
+ रीडिंग पेज
+ प्रायोजक
+ Open source licenses
+ https://api.github.com/repos/Ashinch/ReadYou/releases/latest
+ बदलाव सूचि
+ अपडेट
+ इस वर्जन को छोडें
+ अपडेट के लिए जाँच कर रहा है
+ यह लेटेस्ट अपडेट है
+ अपडेट की जाँच नहीं कर पाए
+ अपडेट डाउनलोड नहीं कर पाए
+ अनुरोध दर सीमित है
+ सहायता
+ चालू होने पर
+ प्रारंभिक पृष्ठ
+ प्रारंभिक फ़िल्टर
+ "रेत-समाधि" उपन्यास अंततः समाप्त हो गई
+ रेत-समाधि गीतांजलि श्री की लिखी हुए उत्तम श्रेष्ट अथवा पहली हिंदी उपन्यास है जिसे अंतर्राष्ट्रीय बुकर प्रिज़े से सम्मानित किया गया है
+
+ रेडइट
+ अंक
+ दोनों सिरों पर पैडिंग
+ पाठ प्रकाशन दिनांक
+ पाठ विवरण
+ पाठ चित्र कलाएँ
+ फीड के नाम
+ फीड फविकों
+ प्रकाशन तिथि के लिए स्टिकी हेडर (प्रयोगात्मक)
+ पाठों की सूचि
+ ग्रुप की सूचि
+ हमेशा विस्तृत रखें
+ टॉप
+ \"पढ़ा हुआ चिन्हित करें " \बटन का स्थान
+ टॉप बार
+ चयनित आइकन भरें
+ फ़िल्टर बार
+ चिह्न
+ चिह्न ऐवं लेबल
+ चिह्न ऐवं लेबल (केवल चयनित)
+ यह टूल एलिवेशन केवल स्क्रॉल करते समय उपलब्ध है
+ यह टोनल एलिवेशन केवल लाइट थीम में उपलब्ध है
+ यह टोनल एलिवेशन केवल लाइट थीम में उपलब्ध है
+ साँझा करें
+ विडियो चलने हेतु टच करें
+ पाठा
+ फोंट साइज
+ लैटर स्पेसिंग
+ लाइन स्पेसिंग
+ संरेखण
+ सामान्य
+ टूलबार स्वतः छुपाएँ
+ बटन पुनर्व्यवस्थित करें
+ बैओनिक पाठन
+ चित्र
+ गोल कोने
+ चलचित्र
+ बाये संरेखित करें
+ दाएँ संरेखित करें
+ पाठ मध्य संरेखित करें
+ संयोजिक करें
+ बाहरी फोंट
+ शीर्षक
+ बोल्ड
+ अपरकेस
+ उप शीर्षक
+ एप की थीम लगाएं
+ एडवांस
+ डार्क रीडर थीम
+ बोल्ड, उपरेखा, संरेखित
+ फोंट साईज, पंक्ति और अनुछेद रिक्त , संरेख
+ गोल कोण ,क्षैतिज पेडिंग
+ गोल कोण ,क्षैतिज पेडिंग
+ बड़ा करें
+
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 4a641b2..46dafe4 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -233,7 +233,7 @@
Altro
Tema scuro AMOLED
Elevazione tonale
- Font
+ Font di lettura
Font di base
Pagina dei feed
Pagina dei flow
@@ -257,7 +257,7 @@
Lo Sciocco è l\'ottavo e ultimo volume della serie Il Signore dei Misteri, scritta da Seppia Che Ama Le Immersioni.
Reddit
valore
- Padding su entrambe le estremità
+ Imbottitura orizzontale
Tempo di pubblicazione dell\'articolo
Descrizione dell\'articolo
Immagini dell\'articolo
@@ -280,4 +280,33 @@
Questa elevazione tonale è disponibile solo con il tema chiaro.
Condividi
Tocca per riprodurre il video
+ Text
+ Font size
+ Letter spacing
+ Line spacing
+ Alignment
+ General
+ Auto hide toolbars
+ Rearrange buttons
+ Bionic Reading
+ Images
+ Rounded corners
+ Videos
+ Align left
+ Align right
+ Center text
+ Justify
+ External fonts
+ Title
+ Bold
+ Upper case
+ Subhead
+ Use app theme
+ Advanced
+ Dark reading theme
+ Bold, upper case, alignment
+ Font size, letter spacing, alignment
+ Rounded corners, horizontal padding
+ Rounded corners, horizontal padding
+ Maximize
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 6f46a71..6798640 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -224,7 +224,7 @@
AMOLED 深色主题
其他
色调海拔
- 字体
+ 阅读字体
基本字体
订阅源页面
信息流页面
@@ -245,10 +245,12 @@
起始页面
起始过滤条件
呜呜呜,黎明之剑完结了
- 是宴席,就有结束的时候,但这本书真的陪了我好久啊,好舍不得,而且主线结束了坑却没填完,不知道啥时候才有番外啊
+
+ 是宴席,就有结束的时候,但这本书真的陪了我好久啊,好舍不得,而且主线结束了坑却没填完,不知道啥时候才有番外啊
+
漩涡书院
值
- 两端边距
+ 水平填充
文章发布时间
文章描述
文章插图
@@ -271,4 +273,33 @@
分组列表的色调海拔仅在亮色主题时可用。
分享
轻触播放视频
+ 文本
+ 字体大小
+ 字符间距
+ 行间距
+ 对齐
+ 通用
+ 自动隐藏工具条
+ 重新排列按钮
+ 仿生阅读
+ 图片
+ 圆角
+ 视频
+ 左对齐
+ 右对齐
+ 居中对齐
+ 两端对齐
+ 外部字体
+ 标题
+ 加粗
+ 大写字母
+ 子标题
+ 跟随应用设置
+ 高级
+ 深色阅读主题
+ 加粗、大写字母、对齐
+ 字体大小、字符间距、对齐
+ 圆角、水平边距
+ 圆角、水平边距
+ 最大化
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 63d30db..dab1e2f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -111,6 +111,7 @@
français
Čeština
italiano
+ हिंदी
Tips & support
About, open source licenses
Welcome
@@ -244,7 +245,7 @@
Other
AMOLED dark theme
Tonal elevation
- Fonts
+ Reading fonts
Basic fonts
Feeds page
Flow page
@@ -272,7 +273,7 @@
Reddit
value
- Padding on both ends
+ Horizontal padding
Article publication time
Article descriptions
Article images
@@ -295,4 +296,33 @@
This tonal elevation is only available in the light theme.
Share
Touch to play video
+ Text
+ Font size
+ Letter spacing
+ Line spacing
+ Alignment
+ General
+ Auto hide toolbars
+ Rearrange buttons
+ Bionic Reading
+ Images
+ Rounded corners
+ Videos
+ Align left
+ Align right
+ Center text
+ Justify
+ External fonts
+ Title
+ Bold
+ Upper case
+ Subhead
+ Use app theme
+ Advanced
+ Dark reading theme
+ Bold, upper case, alignment
+ Font size, letter spacing, alignment
+ Rounded corners, horizontal padding
+ Rounded corners, horizontal padding
+ Maximize
diff --git a/fastlane/metadata/android/de-DE/changelogs/12.txt b/fastlane/metadata/android/de-DE/changelogs/12.txt
new file mode 100644
index 0000000..f692785
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/12.txt
@@ -0,0 +1,17 @@
+## 0.8.1
+
+1. Französische Sprache hinzugefügt (Dank an DodoLeDev).
+
+2. Tschechische Sprache hinzugefügt (Dank an Fjuro).
+
+3. Einige übersetzte Texte geändert (Dank an comradekingu).
+
+4. Behebung des Problems, das nach dem Import einer OPML-Datei aus Version 0.8.0 nicht synchronisiert werden kann.
+
+5. Behebung eines Absturzes beim Laden sehr großer Bilder (50 MB+) (Dank an Feeder).
+
+6. Behebung des Problems, dass einige Feeds nicht abonniert werden konnten (Dank an kzaemrio).
+
+7. Optimierung im Falle von zu vielen Feeds (100+).
+
+8. Einige Leistungsoptimierungen und Detailänderungen.
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ