Add reading page style settings (#132)

* Add reading page style settings

* improved german language (#134)

* Add Hindi translation

Thanks augurer

* Add auto hide toolbar and image maximize setting

* Add tonal elevation setting in reading page

* Add reading dark theme and previews

* Add dynamic custom reading theme preview

Co-authored-by: helloworldtest123 <36381315+helloworldtest123@users.noreply.github.com>
This commit is contained in:
Ashinch 2022-07-10 14:43:09 +08:00 committed by GitHub
parent e32c0fbfd3
commit f21a0c9fd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 3456 additions and 227 deletions

View File

@ -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
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on-de.png"
alt="Jetzt bei F-Droid"
height="80">](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.

View File

@ -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
}
}

View File

@ -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),
)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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> { AmoledDarkThemePreference.default }
// Feeds page
val LocalFeedsFilterBarStyle =
compositionLocalOf<FeedsFilterBarStylePreference> { FeedsFilterBarStylePreference.default }
val LocalFeedsFilterBarFilled =
@ -137,6 +112,7 @@ val LocalFeedsGroupListExpand =
val LocalFeedsGroupListTonalElevation =
compositionLocalOf<FeedsGroupListTonalElevationPreference> { FeedsGroupListTonalElevationPreference.default }
// Flow page
val LocalFlowFilterBarStyle =
compositionLocalOf<FlowFilterBarStylePreference> { FlowFilterBarStylePreference.default }
val LocalFlowFilterBarFilled =
@ -162,9 +138,119 @@ val LocalFlowArticleListDateStickyHeader =
val LocalFlowArticleListTonalElevation =
compositionLocalOf<FlowArticleListTonalElevationPreference> { FlowArticleListTonalElevationPreference.default }
// Reading page
val LocalReadingTheme = compositionLocalOf<ReadingThemePreference> { ReadingThemePreference.default }
val LocalReadingDarkTheme = compositionLocalOf<ReadingDarkThemePreference> { ReadingDarkThemePreference.default }
val LocalReadingPageTonalElevation = compositionLocalOf<ReadingPageTonalElevationPreference> { ReadingPageTonalElevationPreference.default }
val LocalReadingAutoHideToolbar = compositionLocalOf<ReadingAutoHideToolbarPreference> { ReadingAutoHideToolbarPreference.default }
val LocalReadingTextFontSize = compositionLocalOf { ReadingTextFontSizePreference.default }
val LocalReadingLetterSpacing = compositionLocalOf { ReadingLetterSpacingPreference.default }
val LocalReadingTextHorizontalPadding = compositionLocalOf { ReadingTextHorizontalPaddingPreference.default }
val LocalReadingTextAlign = compositionLocalOf<ReadingTextAlignPreference> { ReadingTextAlignPreference.default }
val LocalReadingTextBold = compositionLocalOf<ReadingTextBoldPreference> { ReadingTextBoldPreference.default }
val LocalReadingTitleAlign = compositionLocalOf<ReadingTitleAlignPreference> { ReadingTitleAlignPreference.default }
val LocalReadingSubheadAlign =
compositionLocalOf<ReadingSubheadAlignPreference> { ReadingSubheadAlignPreference.default }
val LocalReadingFonts = compositionLocalOf<ReadingFontsPreference> { ReadingFontsPreference.default }
val LocalReadingTitleBold = compositionLocalOf<ReadingTitleBoldPreference> { ReadingTitleBoldPreference.default }
val LocalReadingSubheadBold =
compositionLocalOf<ReadingSubheadBoldPreference> { ReadingSubheadBoldPreference.default }
val LocalReadingTitleUpperCase =
compositionLocalOf<ReadingTitleUpperCasePreference> { ReadingTitleUpperCasePreference.default }
val LocalReadingSubheadUpperCase =
compositionLocalOf<ReadingSubheadUpperCasePreference> { ReadingSubheadUpperCasePreference.default }
val LocalReadingImageHorizontalPadding = compositionLocalOf { ReadingImageHorizontalPaddingPreference.default }
val LocalReadingImageRoundedCorners = compositionLocalOf { ReadingImageRoundedCornersPreference.default }
val LocalReadingImageMaximize = compositionLocalOf<ReadingImageMaximizePreference> { ReadingImageMaximizePreference.default }
// Interaction
val LocalInitialPage = compositionLocalOf<InitialPagePreference> { InitialPagePreference.default }
val LocalInitialFilter =
compositionLocalOf<InitialFilterPreference> { InitialFilterPreference.default }
// Languages
val LocalLanguages =
compositionLocalOf<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(
// 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()
}
}

View File

@ -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,
)
}

View File

@ -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 = {},
)
)

View File

@ -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<Node>,
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,

View File

@ -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(
)
}
)
}
}

View File

@ -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,
)
)

View File

@ -69,6 +69,7 @@ sealed class DataStoreKeys<T> {
abstract val key: Preferences.Key<T>
// Version
object IsFirstLaunch : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
@ -147,6 +148,7 @@ sealed class DataStoreKeys<T> {
get() = booleanPreferencesKey("amoledDarkTheme")
}
// Feeds page
object FeedsFilterBarStyle : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
@ -189,6 +191,7 @@ sealed class DataStoreKeys<T> {
get() = intPreferencesKey("feedsGroupListTonalElevation")
}
// Flow page
object FlowFilterBarStyle : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
@ -261,6 +264,122 @@ sealed class DataStoreKeys<T> {
get() = intPreferencesKey("flowArticleListTonalElevation")
}
// Reading page
object ReadingDarkTheme : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingDarkTheme")
}
object ReadingPageTonalElevation : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("ReadingPageTonalElevation")
}
object ReadingTextFontSize : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingTextFontSize")
}
object ReadingLetterSpacing : DataStoreKeys<Double>() {
override val key: Preferences.Key<Double>
get() = doublePreferencesKey("readingLetterSpacing")
}
object ReadingTextHorizontalPadding : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingTextHorizontalPadding")
}
object ReadingTextBold : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("readingTextBold")
}
object ReadingTextAlign : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingTextAlign")
}
object ReadingTitleAlign : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingTitleAlign")
}
object ReadingSubheadAlign : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingSubheadAlign")
}
object ReadingTheme : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingTheme")
}
object ReadingFonts : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingFonts")
}
object ReadingAutoHideToolbar : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("readingAutoHideToolbar")
}
object ReadingTitleBold : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("readingTitleBold")
}
object ReadingSubheadBold : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("ReadingSubheadBold")
}
object ReadingTitleUpperCase : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("readingTitleUpperCase")
}
object ReadingSubheadUpperCase : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("ReadingSubheadUpperCase")
}
object ReadingImageMaximize : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("readingImageMaximize")
}
object ReadingImageHorizontalPadding : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingImageHorizontalPadding")
}
object ReadingImageRoundedCorners : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
get() = intPreferencesKey("readingImageRoundedCorners")
}
// Interaction
object InitialPage : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>
@ -273,6 +392,7 @@ sealed class DataStoreKeys<T> {
get() = intPreferencesKey("initialFilter")
}
// Languages
object Languages : DataStoreKeys<Int>() {
override val key: Preferences.Key<Int>

View File

@ -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) {

View File

@ -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"

View File

@ -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(
}
}
}
}
}

View File

@ -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
)

View File

@ -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,
)
}
}

View File

@ -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(),
)
}
}

View File

@ -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,

View File

@ -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(
)
}
}
}
}

View File

@ -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*/ })
}

View File

@ -140,6 +140,7 @@ fun SettingsPage(
}
}
item {
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}

View File

@ -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))
}
}

View File

@ -78,6 +78,7 @@ fun DarkThemePage(
}
}
item {
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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))
}
}
}
)
}

View File

@ -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
}
)
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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))
}
}
}
)
}

View File

@ -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)
)
}
}

View File

@ -69,6 +69,7 @@ fun InteractionPage(
) {}
}
item {
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}

View File

@ -88,6 +88,7 @@ fun LanguagesPage(
}
}
item {
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}

View File

@ -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)

View File

@ -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) {

View File

@ -239,7 +239,7 @@
<string name="other">Ostatní</string>
<string name="amoled_dark_theme">Tmavé téma AMOLED</string>
<string name="tonal_elevation">Zvýraznění tónů</string>
<string name="fonts">Písma</string>
<string name="reading_fonts">Přečtení písma</string>
<string name="basic_fonts">Základní písma</string>
<string name="feeds_page">Stránka se zdroji</string>
<string name="flow_page">Stránka Flow</string>
@ -263,7 +263,7 @@
<string name="preview_article_desc">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í.</string>
<string name="preview_feed_name">Reddit</string>
<string name="value">value</string>
<string name="padding_on_both_ends">Odsazení na obou koncích</string>
<string name="horizontal_padding">Horizontální polstrování</string>
<string name="article_date">Čas zveřejnění článku</string>
<string name="article_desc">Popis článku</string>
<string name="article_images">Obrázky článku</string>
@ -286,4 +286,33 @@
<string name="tips_group_list_tonal_elevation">Zvýšení tónu seznamu skupin je dostupné pouze ve světlém režimu.</string>
<string name="share">Sdílet</string>
<string name="touch_to_play_video">Klepněte pro přehráníé videa</string>
<string name="text">Text</string>
<string name="font_size">Font size</string>
<string name="letter_spacing">Letter spacing</string>
<string name="line_spacing">Line spacing</string>
<string name="alignment">Alignment</string>
<string name="general">General</string>
<string name="auto_hide_toolbars">Auto hide toolbars</string>
<string name="rearrange_buttons">Rearrange buttons</string>
<string name="bionic_reading">Bionic Reading</string>
<string name="images">Images</string>
<string name="rounded_corners">Rounded corners</string>
<string name="videos">Videos</string>
<string name="align_left">Align left</string>
<string name="align_right">Align right</string>
<string name="center_text">Center text</string>
<string name="justify">Justify</string>
<string name="external_fonts">External fonts</string>
<string name="title">Title</string>
<string name="bold">Bold</string>
<string name="upper_case">Upper case</string>
<string name="subhead">Subhead</string>
<string name="use_app_theme">Use app theme</string>
<string name="advanced">Advanced</string>
<string name="dark_reading_theme">Dark reading theme</string>
<string name="title_desc">Bold, upper case, alignment</string>
<string name="text_desc">Font size, letter spacing, alignment</string>
<string name="images_desc">Rounded corners, horizontal padding</string>
<string name="videos_desc">Rounded corners, horizontal padding</string>
<string name="maximize">Maximize</string>
</resources>

View File

@ -98,7 +98,7 @@
<string name="interaction">Interaktion</string>
<string name="interaction_desc">Beim Start, haptisches Feedback</string>
<string name="languages">Sprachen</string>
<string name="languages_desc">English, Deutsch, mehr</string>
<string name="languages_desc">Deutsch, Englisch, andere</string>
<string name="help_translate">Hilf uns beim Übersetzen</string>
<string name="use_device_languages">Gerätesprache verwenden</string>
<string name="tips_and_support">Tipps &amp; Support</string>
@ -233,7 +233,7 @@
<string name="other">Sonstiges</string>
<string name="amoled_dark_theme">AMOLED Dunkles Design</string>
<string name="tonal_elevation">Farbliches Hervorheben</string>
<string name="fonts">Schriftarten</string>
<string name="reading_fonts">Leseschriftarten</string>
<string name="basic_fonts">Grundschriftarten</string>
<string name="feeds_page">Feeds Seite</string>
<string name="flow_page">Flow Seite</string>
@ -257,7 +257,7 @@
<string name="preview_article_desc">The Fool ist der achte und letzte Band der Lord of the Mysteries Reihe, geschrieben von Cuttlefish That Loves Diving.</string>
<string name="preview_feed_name">Reddit</string>
<string name="value">Wert</string>
<string name="padding_on_both_ends">Rand an beiden Enden</string>
<string name="horizontal_padding">Horizontaler Rand</string>
<string name="article_date">Artikel Veröffentlichungszeit</string>
<string name="article_desc">Artikel Beschreibungen</string>
<string name="article_images">Artikel Bilder</string>
@ -278,6 +278,35 @@
<string name="tips_top_bar_tonal_elevation">Das farbliche Hervorheben der oberen Leiste ist nur beim Scrollen verfügbar.</string>
<string name="tips_article_list_tonal_elevation">Das farbliche Hervorheben der Artikelliste ist nur für das helle Design verfügbar.</string>
<string name="tips_group_list_tonal_elevation">Das farbliche Hervorheben der Gruppenliste ist nur für das helle Design verfügbar.</string>
<string name="share">Aktie</string>
<string name="share">Teilen</string>
<string name="touch_to_play_video">Berühren, um Video abzuspielen</string>
<string name="text">Text</string>
<string name="font_size">Schriftgröße</string>
<string name="letter_spacing">Zeichenabstand</string>
<string name="line_spacing">Zeilenabstand</string>
<string name="alignment">Ausrichtung</string>
<string name="general">Allgemein</string>
<string name="auto_hide_toolbars">Leisten automatisch ausblenden</string>
<string name="rearrange_buttons">Schaltflächen umpositionieren</string>
<string name="bionic_reading">Bionic Reading</string>
<string name="images">Bilder</string>
<string name="rounded_corners">Abgerundete Ecken</string>
<string name="videos">Videos</string>
<string name="align_left">Linksbündig</string>
<string name="align_right">Rechtsbündig</string>
<string name="center_text">Zentriert</string>
<string name="justify">Blocksatz</string>
<string name="external_fonts">Externe Schriftart</string>
<string name="title">Titel</string>
<string name="bold">Fettgedruckt</string>
<string name="upper_case">Großbuchstaben</string>
<string name="subhead">Untertitel</string>
<string name="use_app_theme">Appdesign verwenden</string>
<string name="advanced">Erweitert</string>
<string name="dark_reading_theme">Dunkles Lesedesign</string>
<string name="title_desc">Fettgedruckt, Großbuchstaben, Ausrichtung</string>
<string name="text_desc">Schriftgröße, Zeichenabstand, Ausrichtung</string>
<string name="images_desc">Abgerundete Ecken, Horizontaler Rand</string>
<string name="videos_desc">Abgerundete Ecken, Horizontaler Rand</string>
<string name="maximize">Maximieren</string>
</resources>

View File

@ -236,7 +236,7 @@
<string name="other">Autres options</string>
<string name="amoled_dark_theme">Thème sombre AMOLED</string>
<string name="tonal_elevation">Intensité</string>
<string name="fonts">Polices</string>
<string name="reading_fonts">Polices de lecture</string>
<string name="basic_fonts">Polices de base</string>
<string name="feeds_page">Page des flux</string>
<string name="flow_page">Page des articles</string>
@ -260,7 +260,7 @@
<string name="preview_article_desc">Le Fou est le huitième et dernier volume de la série Le Seigneur des Mystères écrite par Cuttlefish That Loves Diving.</string>
<string name="preview_feed_name">Reddit</string>
<string name="value">Valeur</string>
<string name="padding_on_both_ends">Espacement des deux côtés</string>
<string name="horizontal_padding">Rembourrage Horizontal</string>
<string name="article_date">Heure de publication des articles</string>
<string name="article_desc">Descriptions des articles</string>
<string name="article_images">Images des articles</string>
@ -283,4 +283,33 @@
<string name="tips_group_list_tonal_elevation">L\'intensité de la liste des groupes est uniquement disponible pour le thème clair.</string>
<string name="share">Partager</string>
<string name="touch_to_play_video">Appuyer pour lancer la lecture</string>
<string name="text">Text</string>
<string name="font_size">Font size</string>
<string name="letter_spacing">Letter spacing</string>
<string name="line_spacing">Line spacing</string>
<string name="alignment">Alignment</string>
<string name="general">General</string>
<string name="auto_hide_toolbars">Auto hide toolbars</string>
<string name="rearrange_buttons">Rearrange buttons</string>
<string name="bionic_reading">Bionic Reading</string>
<string name="images">Images</string>
<string name="rounded_corners">Rounded corners</string>
<string name="videos">Videos</string>
<string name="align_left">Align left</string>
<string name="align_right">Align right</string>
<string name="center_text">Center text</string>
<string name="justify">Justify</string>
<string name="external_fonts">External fonts</string>
<string name="title">Title</string>
<string name="bold">Bold</string>
<string name="upper_case">Upper case</string>
<string name="subhead">Subhead</string>
<string name="use_app_theme">Use app theme</string>
<string name="advanced">Advanced</string>
<string name="dark_reading_theme">Dark reading theme</string>
<string name="title_desc">Bold, upper case, alignment</string>
<string name="text_desc">Font size, letter spacing, alignment</string>
<string name="images_desc">Rounded corners, horizontal padding</string>
<string name="videos_desc">Rounded corners, horizontal padding</string>
<string name="maximize">Maximize</string>
</resources>

View File

@ -0,0 +1,311 @@
<resources>
<string name="all">सभी</string>
<plurals name="all_desc">
<item quantity="one">%1$d संग्रहित पाठ </item>
<item quantity="other">%1$d संग्रहित पाठ</item>
</plurals>
<string name="unread">अपठित पाठ </string>
<plurals name="unread_desc">
<item quantity="one">%1$d अपठित पाठ </item>
<item quantity="other">%1$d अपठित पाठ </item>
</plurals>
<string name="starred">चिन्हित</string>
<plurals name="starred_desc">
<item quantity="one">%1$d चिन्हित पाठ</item>
<item quantity="other">%1$d चिन्हित पाठ</item>
</plurals>
<string name="feeds">फीड </string>
<string name="syncing">सिंक्रनाइज़ किए जा रहे…</string>
<string name="loading">लोड हो रहा है…</string>
<string name="expand_less">समेटे</string>
<string name="expand_more">विस्तृत करें</string>
<string name="confirm">पुष्टि करें</string>
<string name="cancel">रद्द करें</string>
<string name="allow">अनुमति </string>
<string name="deny">अस्वीकार </string>
<string name="defaults">सामान्य विकल्प </string>
<string name="unknown">अज्ञात</string>
<string name="back">पीछे </string>
<string name="go_to"> गो टू</string>
<string name="settings">सेटिंग्स</string>
<string name="refresh">रिफ्रेश</string>
<string name="search">खोजो</string>
<string name="searching">खोज रहे है …</string>
<string name="subscribe">सदस्यता लें </string>
<string name="already_subscribed">सदस्यता ले ली है </string>
<string name="clear">क्लिअर</string>
<string name="paste">पेस्ट</string>
<string name="feed_or_site_url">यु अरे एल अथवा फ़ीड्स </string>
<string name="import_from_opml">ओ पी एम् एल से आयात करें </string>
<string name="preset">प्रीसेट</string>
<string name="selected">चयनित</string>
<string name="allow_notification">नोटिफिकेशन की अनुमति दें</string>
<string name="all_allow_notification_tips"> \"%1$s\" ग्रुप के सभी फ़ीड्स को नोटीफिकेशन की स्वीकृति दें </string>
<string name="all_allow_notification_toast">\"%1$s\" ग्रुप के सभी नोटीफिकेशन स्वीकृत है </string>
<string name="all_deny_notification_toast"> \"%1$s\" ग्रुप के सभी नोटीफिकेशन अस्वीकृत है </string>
<string name="parse_full_content">पूरा पाठ दिखाएँ </string>
<string name="all_parse_full_content_tips"> \"%1$s\" ग्रुप के सभी पाठ पूरे दिखाएँ </string>
<string name="all_parse_full_content_toast"> \"%1$s\" ग्रुप के सभी पाठ पूरे दिखाएँ</string>
<string name="all_deny_parse_full_content_toast"> \"%1$s\" ग्रुप के सभी पाठ पूरे ना दिखाएँ</string>
<string name="clear_articles">पाठ हटाएँ </string>
<string name="clear_articles_in_feed_toast">\"%1$s\" फीड के सभी संग्रहित पाठो को हटाएँ </string>
<string name="clear_articles_in_group_toast"> \"%1$s\"फीड के सभी संग्रहित पाठ हटा दिए है </string>
<string name="clear_articles_feed_tips"> \"%1$s\" फीड के सभी संग्रहित पाठ हटाएँ .</string>
<string name="clear_articles_group_tips">\"%1$s\" ग्रुप के सभी संग्रहित पाठो को हटाएँ.</string>
<string name="add_to_group">ग्रुप में जोड़ें </string>
<string name="move_to_group">ग्रुप में स्तानान्तरित करें </string>
<string name="all_move_to_group_tips">\"%1$s\" ग्रुप के सभी पाठो को \"%2$s\" में स्थानांतरित करें .</string>
<string name="all_move_to_group_toast">\"%1$s\" ग्रुप में स्थानांतरण पूरा हुआ </string>
<string name="rename">नाम बदलें </string>
<string name="change_url">यु आर एल बदलें </string>
<string name="rename_toast">नया नाम \"%1$s\"</string>
<string name="create_new_group">नया ग्रुप बनाएँ </string>
<string name="name">नाम </string>
<string name="open_with">खोलें %1$s</string>
<string name="options">विकल्प </string>
<string name="delete">मिटाएं </string>
<string name="delete_toast">मिट गया \"%1$s\"</string>
<string name="unsubscribe">सदस्यता छोडें</string>
<string name="unsubscribe_tips"> \"%1$s\" से सदस्यता छोडें और सभी संग्रहित पाठों को मिटाएं </string>
<string name="delete_group">ग्रुप को मिटाएँ </string>
<string name="delete_group_tips"> \"%1$s\" ग्रुप के सभी फ़ीड्स अथवा संग्रहित पाठों को मिटाएँ </string>
<string name="group_option_tips">निम्नलिखित विकल्प ग्रुप के सभी फ़ीडस् पर लागू होंगे</string>
<string name="today">आज </string>
<string name="yesterday">कल </string>
<string name="date_at_time">तिथि %1$s समय %2$s</string>
<string name="search_for_in"> %1$s के लिए \"%2$s\ में खोजें "</string>
<string name="search_for">पाठों में %1$s खोजें </string>
<string name="mark_as_read">पढ़ा हुआ चिन्हित करें </string>
<string name="mark_all_as_read">सभी को पढ़ा हुआ चिन्हित करें</string>
<string name="mark_as_unread">अपठित के रूप में चिह्नित करें</string>
<string name="mark_as_starred">तारांकित के रूप में चिह्नित करें</string>
<string name="mark_as_unstar">अतारांकित के रूप में चिह्नित करें</string>
<string name="mark_as_read_one_day">1 दिन से अधिक पढ़ें के रूप में चिह्नित करें</string>
<string name="mark_as_read_three_days">3 दिन से अधिक पढ़ें के रूप में चिह्नित करें</string>
<string name="mark_as_read_seven_days">7 दिन से अधिक पढ़ें के रूप में चिह्नित करें</string>
<string name="one_day">1 दिन </string>
<string name="three_days">3 दिन </string>
<string name="seven_days">7 दिन </string>
<string name="close">बंद करें </string>
<string name="get_new_updates">नया अपडेट प्राप्त करें</string>
<string name="get_new_updates_desc"> %1$s वर्ज़न उपलब्ध है </string>
<string name="in_coding">कोडिंग चल रही है....</string>
<string name="coming_soon">जल्द आ रहा है...</string>
<string name="accounts">खाते</string>
<string name="accounts_desc">लोकल, नया आर एस एस </string>
<string name="color_and_style">रंग अथवा शैली </string>
<string name="color_and_style_desc">थीम , रंग शैली , लिपि आकार </string>
<string name="interaction">Interaction</string>
<string name="interaction_desc">शुरू होने पे , हैप्टिक फीडबैक </string>
<string name="languages">भाषाएँ </string>
<string name="languages_desc">अंग्रेजी , चाइनीज, और..</string>
<string name="help_translate">अनुवाद में मदद करें</string>
<string name="use_device_languages">डिवाइस भाषा का उपयोग करें</string>
<string name="tips_and_support">सुझाव और सहायता</string>
<string name="tips_and_support_desc">परिचय, ओपन सोर्स लाइसेंस</string>
<string name="welcome">स्वागत है </string>
<string name="tos_tips">आगे बढ़ने के लिए Read Youकी सेवा शर्तें और गोपनीयता नीति को पढ़े और स्वीकार करें </string>
<string name="browse_tos_tips">यहाँ &lt;i&gt;&lt;u&gt;सेवा शर्तें और गोपनीयता नीति पढ़े &lt;/u&gt;&lt;/i&gt;
</string>
<string name="terms_of_service">सेवा की शर्तें</string>
<string name="tos_content">
&lt;h5&gt;
गोपनीयता नीति
&lt;/h5&gt;
&lt;br/&gt;
&lt;p&gt;
मैं आपकी गोपनीयता को बहुत गंभीरता से लेता हूं
&lt;/p&gt;
&lt;br/&gt;
&lt;p&gt;
&lt;b&gt;Read You&lt;/b&gt;
कोई उपयोगकर्ता डेटा एकत्र नहीं करता है, और सभी संवेदनशील जानकारी (पासवर्ड और अन्य खाता जानकारी)
आपके डिवाइस पर स्थानीय एप्लिकेशन डेटाबेस में सुरक्षित रूप से संग्रहीत है|
&lt;/p&gt;
&lt;br/&gt;
&lt;p&gt;
&lt;b&gt;Read You&lt;/b&gt;
आपको सेवा प्रदान करने के लिए निम्नलिखित अनुमतियों का उपयोग किया जाएगा।
&lt;/p&gt;
&lt;br/&gt;
&lt;p&gt;
- एक्सेस नेटवर्क अनुमति (आपके द्वारा निर्देशित ऑनलाइन सामग्री तक पहुँचने के लिए)
&lt;/p&gt;
&lt;p&gt;
- नेटवर्क स्थिति की अनुमति (यह जानने करने के लिए कि क्या डिवाइस में वर्तमान में नेटवर्क की स्थिति उपलब्ध है)
&lt;/p&gt;
&lt;p&gt;
-पृष्ठभूमिक सेवा अनुमति (नियमित रूप से अपने पसंदीदार पाठों को स्वचालित रूप से नियमित सिंक करने के लिए आधार)
&lt;/p&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;h5&gt;
तृतीय पक्ष सेवाएं
&lt;/h5&gt;
&lt;br/&gt;
&lt;p&gt;
यह नीति आपके द्वारा &lt;b&gt;Read You&lt;/b&gt;के साथ उपयोग की जाने वाली तृतीय-पक्ष सेवाओं पर लागू नहीं होती है. आप उन वेबसाइटों की गोपनीयता नीतियों की समीक्षा
करने हेतु स्वतंत्र है|
&lt;/p&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;h5&gt;
अस्वीकरण
&lt;/h5&gt;
&lt;br/&gt;
&lt;p&gt;
&lt;b&gt;Read You&lt;/b&gt;
केवल एक सामग्री संग्रह उपकरण है।. आपके द्वारा &lt;b&gt;Read You&lt;/b&gt; का उपयोग आपके देश/राष्ट्र और क्षेत्र के नियम ऐवं कानून के अंतर्गत है
और आपके कार्यों से उत्पन्न होने वाले किसी भी दायित्व का वहन आपके द्वारा ही किया जाएगा |
&lt;/p&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;h5&gt;
ओपन सोर्स लाइसेंस
&lt;/h5&gt;
&lt;br/&gt;
&lt;p&gt;
&lt;b&gt;Read You&lt;/b&gt;
GNU GPL 3.0 Open Source License[1] के अंतर्गत एक ओपन सौरसे प्रोजेक्ट है, जो आपको &lt;b&gt;Read You&lt;/b&gt; के सोर्स कोड को उपयोग,
प्रासंगिक सन्दर्भ और मुफ्त में संशोधित करेने के अनुमति देता है, परन्तु संशोधित और व्युत्पन्न कोड को क्लोज्ड-सोर्स वाणिज्यिक सॉफ्टवेयर के रूप में वितरित और बेचा जाने की अनुमति नहीं देता ।
पूर्ण जानकारी हेतु GNU GPL 3.0 Open Source License[2]. देखें।
&lt;/p&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;h5&gt;
शेषसंग्रह
&lt;/h5&gt;
&lt;br/&gt;
&lt;p&gt;
- [1] https://github.com/Ashinch/ReadYou
&lt;/p&gt;
&lt;p&gt;
- [2] https://www.gnu.org/licenses/gpl-3.0.html
&lt;/p&gt;
</string>
<string name="agree">स्वीकृत करें </string>
<string name="wallpaper_colors">वॉलपेपर रंग</string>
<string name="no_palettes">कोई पैलेट नहीं</string>
<string name="only_android_8.1_plus">केवल Android 8.1+ हेतु </string>
<string name="basic_colors">मूल रंग</string>
<string name="primary_color">प्राथमिक रंग</string>
<string name="primary_color_hint">जैसे #666666 या 666666</string>
<string name="appearance">रूप </string>
<string name="style">शैली</string>
<string name="dark_theme">डार्क थीम </string>
<string name="use_device_theme"> डिवाइस थीम का उपयोग करें </string>
<string name="on">ओन</string>
<string name="off">ऑफ</string>
<string name="other">अन्य </string>
<string name="amoled_dark_theme">अमोलेड डार्क थीम</string>
<string name="tonal_elevation">टोनल एलिवेशन</string>
<string name="reading_fonts">रीडिंग फोंट</string>
<string name="basic_fonts">बेसिक फोंट </string>
<string name="feeds_page">फ़ीड्स पेज </string>
<string name="flow_page">फ्लो पेज </string>
<string name="reading_page">रीडिंग पेज </string>
<string name="sponsor">प्रायोजक</string>
<string name="open_source_licenses">Open source licenses</string>
<string name="update_link">https://api.github.com/repos/Ashinch/ReadYou/releases/latest</string>
<string name="change_log">बदलाव सूचि </string>
<string name="update">अपडेट</string>
<string name="skip_this_version">इस वर्जन को छोडें </string>
<string name="checking_updates">अपडेट के लिए जाँच कर रहा है</string>
<string name="is_latest_version">यह लेटेस्ट अपडेट है </string>
<string name="check_failure">अपडेट की जाँच नहीं कर पाए </string>
<string name="download_failure">अपडेट डाउनलोड नहीं कर पाए </string>
<string name="rate_limit">अनुरोध दर सीमित है</string>
<string name="help">सहायता </string>
<string name="on_start">चालू होने पर</string>
<string name="initial_page">प्रारंभिक पृष्ठ</string>
<string name="initial_filter">प्रारंभिक फ़िल्टर</string>
<string name="preview_article_title">"रेत-समाधि" उपन्यास अंततः समाप्त हो गई </string>
<string name="preview_article_desc">रेत-समाधि गीतांजलि श्री की लिखी हुए उत्तम श्रेष्ट अथवा पहली हिंदी उपन्यास है जिसे अंतर्राष्ट्रीय बुकर प्रिज़े से सम्मानित किया गया है
</string>
<string name="preview_feed_name">रेडइट</string>
<string name="value">अंक</string>
<string name="horizontal_padding">दोनों सिरों पर पैडिंग</string>
<string name="article_date">पाठ प्रकाशन दिनांक </string>
<string name="article_desc">पाठ विवरण </string>
<string name="article_images">पाठ चित्र कलाएँ</string>
<string name="feed_names">फीड के नाम </string>
<string name="feed_favicons">फीड फविकों </string>
<string name="article_date_sticky_header">प्रकाशन तिथि के लिए स्टिकी हेडर (प्रयोगात्मक)</string>
<string name="article_list">पाठों की सूचि </string>
<string name="group_list">ग्रुप की सूचि </string>
<string name="always_expand">हमेशा विस्तृत रखें </string>
<string name="top">टॉप </string>
<string name="mark_as_read_button_position">\"पढ़ा हुआ चिन्हित करें " \बटन का स्थान </string>
<string name="top_bar">टॉप बार </string>
<string name="fill_selected_icon">चयनित आइकन भरें</string>
<string name="filter_bar">फ़िल्टर बार</string>
<string name="icons">चिह्न</string>
<string name="icons_and_labels">चिह्न ऐवं लेबल</string>
<string name="icons_and_label_only_selected">चिह्न ऐवं लेबल (केवल चयनित)</string>
<string name="tips_top_bar_tonal_elevation">यह टूल एलिवेशन केवल स्क्रॉल करते समय उपलब्ध है </string>
<string name="tips_article_list_tonal_elevation">यह टोनल एलिवेशन केवल लाइट थीम में उपलब्ध है</string>
<string name="tips_group_list_tonal_elevation">यह टोनल एलिवेशन केवल लाइट थीम में उपलब्ध है</string>
<string name="share">साँझा करें </string>
<string name="touch_to_play_video">विडियो चलने हेतु टच करें </string>
<string name="text">पाठा </string>
<string name="font_size">फोंट साइज</string>
<string name="letter_spacing">लैटर स्पेसिंग</string>
<string name="line_spacing">लाइन स्पेसिंग </string>
<string name="alignment">संरेखण</string>
<string name="general">सामान्य </string>
<string name="auto_hide_toolbars">टूलबार स्वतः छुपाएँ </string>
<string name="rearrange_buttons">बटन पुनर्व्यवस्थित करें </string>
<string name="bionic_reading">बैओनिक पाठन</string>
<string name="images">चित्र</string>
<string name="rounded_corners">गोल कोने </string>
<string name="videos">चलचित्र</string>
<string name="align_left">बाये संरेखित करें </string>
<string name="align_right">दाएँ संरेखित करें </string>
<string name="center_text">पाठ मध्य संरेखित करें </string>
<string name="justify">संयोजिक करें </string>
<string name="external_fonts">बाहरी फोंट</string>
<string name="title">शीर्षक</string>
<string name="bold">बोल्ड</string>
<string name="upper_case">अपरकेस</string>
<string name="subhead">उप शीर्षक</string>
<string name="use_app_theme">एप की थीम लगाएं </string>
<string name="advanced">एडवांस </string>
<string name="dark_reading_theme">डार्क रीडर थीम</string>
<string name="title_desc">बोल्ड, उपरेखा, संरेखित </string>
<string name="text_desc">फोंट साईज, पंक्ति और अनुछेद रिक्त , संरेख</string>
<string name="images_desc">गोल कोण ,क्षैतिज पेडिंग </string>
<string name="videos_desc">गोल कोण ,क्षैतिज पेडिंग</string>
<string name="maximize">बड़ा करें</string>
</resources>

View File

@ -233,7 +233,7 @@
<string name="other">Altro</string>
<string name="amoled_dark_theme">Tema scuro AMOLED</string>
<string name="tonal_elevation">Elevazione tonale</string>
<string name="fonts">Font</string>
<string name="reading_fonts">Font di lettura</string>
<string name="basic_fonts">Font di base</string>
<string name="feeds_page">Pagina dei feed</string>
<string name="flow_page">Pagina dei flow</string>
@ -257,7 +257,7 @@
<string name="preview_article_desc">Lo Sciocco è l\'ottavo e ultimo volume della serie Il Signore dei Misteri, scritta da Seppia Che Ama Le Immersioni.</string>
<string name="preview_feed_name">Reddit</string>
<string name="value">valore</string>
<string name="padding_on_both_ends">Padding su entrambe le estremità</string>
<string name="horizontal_padding">Imbottitura orizzontale</string>
<string name="article_date">Tempo di pubblicazione dell\'articolo</string>
<string name="article_desc">Descrizione dell\'articolo</string>
<string name="article_images">Immagini dell\'articolo</string>
@ -280,4 +280,33 @@
<string name="tips_group_list_tonal_elevation">Questa elevazione tonale è disponibile solo con il tema chiaro.</string>
<string name="share">Condividi</string>
<string name="touch_to_play_video">Tocca per riprodurre il video</string>
<string name="text">Text</string>
<string name="font_size">Font size</string>
<string name="letter_spacing">Letter spacing</string>
<string name="line_spacing">Line spacing</string>
<string name="alignment">Alignment</string>
<string name="general">General</string>
<string name="auto_hide_toolbars">Auto hide toolbars</string>
<string name="rearrange_buttons">Rearrange buttons</string>
<string name="bionic_reading">Bionic Reading</string>
<string name="images">Images</string>
<string name="rounded_corners">Rounded corners</string>
<string name="videos">Videos</string>
<string name="align_left">Align left</string>
<string name="align_right">Align right</string>
<string name="center_text">Center text</string>
<string name="justify">Justify</string>
<string name="external_fonts">External fonts</string>
<string name="title">Title</string>
<string name="bold">Bold</string>
<string name="upper_case">Upper case</string>
<string name="subhead">Subhead</string>
<string name="use_app_theme">Use app theme</string>
<string name="advanced">Advanced</string>
<string name="dark_reading_theme">Dark reading theme</string>
<string name="title_desc">Bold, upper case, alignment</string>
<string name="text_desc">Font size, letter spacing, alignment</string>
<string name="images_desc">Rounded corners, horizontal padding</string>
<string name="videos_desc">Rounded corners, horizontal padding</string>
<string name="maximize">Maximize</string>
</resources>

View File

@ -224,7 +224,7 @@
<string name="amoled_dark_theme">AMOLED 深色主题</string>
<string name="other">其他</string>
<string name="tonal_elevation">色调海拔</string>
<string name="fonts">字体</string>
<string name="reading_fonts">阅读字体</string>
<string name="basic_fonts">基本字体</string>
<string name="feeds_page">订阅源页面</string>
<string name="flow_page">信息流页面</string>
@ -245,10 +245,12 @@
<string name="initial_page">起始页面</string>
<string name="initial_filter">起始过滤条件</string>
<string name="preview_article_title">呜呜呜,黎明之剑完结了</string>
<string name="preview_article_desc">是宴席,就有结束的时候,但这本书真的陪了我好久啊,好舍不得,而且主线结束了坑却没填完,不知道啥时候才有番外啊</string>
<string name="preview_article_desc">
是宴席,就有结束的时候,但这本书真的陪了我好久啊,好舍不得,而且主线结束了坑却没填完,不知道啥时候才有番外啊
</string>
<string name="preview_feed_name">漩涡书院</string>
<string name="value"></string>
<string name="padding_on_both_ends">两端边距</string>
<string name="horizontal_padding">水平填充</string>
<string name="article_date">文章发布时间</string>
<string name="article_desc">文章描述</string>
<string name="article_images">文章插图</string>
@ -271,4 +273,33 @@
<string name="tips_group_list_tonal_elevation">分组列表的色调海拔仅在亮色主题时可用。</string>
<string name="share">分享</string>
<string name="touch_to_play_video">轻触播放视频</string>
<string name="text">文本</string>
<string name="font_size">字体大小</string>
<string name="letter_spacing">字符间距</string>
<string name="line_spacing">行间距</string>
<string name="alignment">对齐</string>
<string name="general">通用</string>
<string name="auto_hide_toolbars">自动隐藏工具条</string>
<string name="rearrange_buttons">重新排列按钮</string>
<string name="bionic_reading">仿生阅读</string>
<string name="images">图片</string>
<string name="rounded_corners">圆角</string>
<string name="videos">视频</string>
<string name="align_left">左对齐</string>
<string name="align_right">右对齐</string>
<string name="center_text">居中对齐</string>
<string name="justify">两端对齐</string>
<string name="external_fonts">外部字体</string>
<string name="title">标题</string>
<string name="bold">加粗</string>
<string name="upper_case">大写字母</string>
<string name="subhead">子标题</string>
<string name="use_app_theme">跟随应用设置</string>
<string name="advanced">高级</string>
<string name="dark_reading_theme">深色阅读主题</string>
<string name="title_desc">加粗、大写字母、对齐</string>
<string name="text_desc">字体大小、字符间距、对齐</string>
<string name="images_desc">圆角、水平边距</string>
<string name="videos_desc">圆角、水平边距</string>
<string name="maximize">最大化</string>
</resources>

View File

@ -111,6 +111,7 @@
<string name="french" translatable="false">français</string>
<string name="czech" translatable="false">Čeština</string>
<string name="italian" translatable="false">italiano</string>
<string name="hindi" translatable="false">हिंदी</string>
<string name="tips_and_support">Tips &amp; support</string>
<string name="tips_and_support_desc">About, open source licenses</string>
<string name="welcome">Welcome</string>
@ -244,7 +245,7 @@
<string name="other">Other</string>
<string name="amoled_dark_theme">AMOLED dark theme</string>
<string name="tonal_elevation">Tonal elevation</string>
<string name="fonts">Fonts</string>
<string name="reading_fonts">Reading fonts</string>
<string name="basic_fonts">Basic fonts</string>
<string name="feeds_page">Feeds page</string>
<string name="flow_page">Flow page</string>
@ -272,7 +273,7 @@
</string>
<string name="preview_feed_name">Reddit</string>
<string name="value">value</string>
<string name="padding_on_both_ends">Padding on both ends</string>
<string name="horizontal_padding">Horizontal padding</string>
<string name="article_date">Article publication time</string>
<string name="article_desc">Article descriptions</string>
<string name="article_images">Article images</string>
@ -295,4 +296,33 @@
<string name="tips_group_list_tonal_elevation">This tonal elevation is only available in the light theme.</string>
<string name="share">Share</string>
<string name="touch_to_play_video">Touch to play video</string>
<string name="text">Text</string>
<string name="font_size">Font size</string>
<string name="letter_spacing">Letter spacing</string>
<string name="line_spacing">Line spacing</string>
<string name="alignment">Alignment</string>
<string name="general">General</string>
<string name="auto_hide_toolbars">Auto hide toolbars</string>
<string name="rearrange_buttons">Rearrange buttons</string>
<string name="bionic_reading">Bionic Reading</string>
<string name="images">Images</string>
<string name="rounded_corners">Rounded corners</string>
<string name="videos">Videos</string>
<string name="align_left">Align left</string>
<string name="align_right">Align right</string>
<string name="center_text">Center text</string>
<string name="justify">Justify</string>
<string name="external_fonts">External fonts</string>
<string name="title">Title</string>
<string name="bold">Bold</string>
<string name="upper_case">Upper case</string>
<string name="subhead">Subhead</string>
<string name="use_app_theme">Use app theme</string>
<string name="advanced">Advanced</string>
<string name="dark_reading_theme">Dark reading theme</string>
<string name="title_desc">Bold, upper case, alignment</string>
<string name="text_desc">Font size, letter spacing, alignment</string>
<string name="images_desc">Rounded corners, horizontal padding</string>
<string name="videos_desc">Rounded corners, horizontal padding</string>
<string name="maximize">Maximize</string>
</resources>

View File

@ -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.

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.