From b6c548b9a533e1ec811c9e5500b26f2a4cd7efd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 29 Nov 2022 20:30:03 +0100 Subject: [PATCH 01/45] Minor changes --- .../org/readium/adapters/pdfium/navigator/PdfiumSettings.kt | 2 +- .../adapters/pspdfkit/navigator/PsPdfKitSettingsResolver.kt | 2 +- .../org/readium/r2/navigator/image/ImageNavigatorFragment.kt | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumSettings.kt b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumSettings.kt index acfc10381d..dc83987411 100644 --- a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumSettings.kt +++ b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumSettings.kt @@ -10,7 +10,7 @@ import org.readium.r2.navigator.preferences.* import org.readium.r2.shared.ExperimentalReadiumApi /** - * Settings values of the PDF navigator with the PDFIUM adapter. + * Settings values of the PDF navigator with the PDFium adapter. * * @see PdfiumPreferences */ diff --git a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitSettingsResolver.kt b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitSettingsResolver.kt index a157f18728..a56213138b 100644 --- a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitSettingsResolver.kt +++ b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitSettingsResolver.kt @@ -57,7 +57,7 @@ internal class PsPdfKitSettingsResolver( val pageSpacing: Double = preferences.pageSpacing ?: defaults.pageSpacing - ?: 15.0 + ?: 16.0 return PsPdfKitSettings( fit = fit, diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt index a09cc6ee2a..a09dc468e8 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.runBlocking import org.readium.r2.navigator.SimplePresentation import org.readium.r2.navigator.VisualNavigator @@ -91,7 +92,7 @@ class ImageNavigatorFragment private constructor( scroll = false, axis = Axis.HORIZONTAL ) - ) + ).asStateFlow() override fun onCreate(savedInstanceState: Bundle?) { childFragmentManager.fragmentFactory = createFragmentFactory { From cc35ad4f0ab3c74ad6a45f145a61a73960fee14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 29 Nov 2022 20:31:57 +0100 Subject: [PATCH 02/45] Deprecate `VisualNavigator.readingProgression` --- .../main/java/org/readium/r2/navigator/Navigator.kt | 11 ++++++----- .../r2/navigator/audiobook/R2AudiobookActivity.kt | 1 + .../org/readium/r2/navigator/cbz/R2CbzActivity.kt | 1 + .../org/readium/r2/navigator/epub/R2EpubActivity.kt | 1 + 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/Navigator.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/Navigator.kt index d088ff2629..9bb6b2032e 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/Navigator.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/Navigator.kt @@ -104,11 +104,6 @@ interface NavigatorDelegate { */ interface VisualNavigator : Navigator { - /** - * Current reading progression direction. - */ - val readingProgression: PublicationReadingProgression - /** * Current presentation rendered by the navigator. */ @@ -179,6 +174,12 @@ interface VisualNavigator : Navigator { @ExperimentalDragGesture fun onDragEnd(startPoint: PointF, offset: PointF): Boolean = false } + + /** + * Current reading progression direction. + */ + @Deprecated("Use `presentation.value.readingProgression` instead", ReplaceWith("presentation.value.readingProgression")) + val readingProgression: PublicationReadingProgression } /** diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/audiobook/R2AudiobookActivity.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/audiobook/R2AudiobookActivity.kt index c609cca669..d0aa2c25ec 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/audiobook/R2AudiobookActivity.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/audiobook/R2AudiobookActivity.kt @@ -101,6 +101,7 @@ open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activit return true } + @Deprecated("Use `presentation.value.readingProgression` instead", replaceWith = ReplaceWith("presentation.value.readingProgression")) override val readingProgression: ReadingProgression get() = TODO("not implemented") // To change initializer of created properties use File | Settings | File Templates. diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/cbz/R2CbzActivity.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/cbz/R2CbzActivity.kt index f7627aa5f3..1ef8a4b0c8 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/cbz/R2CbzActivity.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/cbz/R2CbzActivity.kt @@ -75,6 +75,7 @@ open class R2CbzActivity : AppCompatActivity(), CoroutineScope, IR2Activity, Vis return navigatorFragment.goBackward(animated, completion) } + @Deprecated("Use `presentation.value.readingProgression` instead", replaceWith = ReplaceWith("presentation.value.readingProgression")) override val readingProgression: ReadingProgression get() = navigatorFragment.readingProgression diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/R2EpubActivity.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/R2EpubActivity.kt index acb79f5500..fb0e46b428 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/R2EpubActivity.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/R2EpubActivity.kt @@ -154,6 +154,7 @@ open class R2EpubActivity : AppCompatActivity(), IR2Activity, IR2Selectable, IR2 } } + @Deprecated("Use `presentation.value.readingProgression` instead", replaceWith = ReplaceWith("presentation.value.readingProgression")) override val readingProgression: ReadingProgression get() = navigatorFragment().readingProgression From 68159d7392f5ba83f62320a1de434d038814d834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 29 Nov 2022 20:32:15 +0100 Subject: [PATCH 03/45] Adjust EPUB preferences editor --- .../org/readium/r2/navigator/epub/EpubPreferencesEditor.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt index 3fea176f2f..ea76f4dded 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt @@ -89,7 +89,7 @@ class EpubPreferencesEditor internal constructor( getEffectiveValue = { state.settings.fontFamily }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, updateValue = { value -> updateValues { it.copy(fontFamily = value) } }, - supportedValues = configuration.fontFamilies, + supportedValues = listOf(null) + configuration.fontFamilies, ) val fontSize: RangePreference = @@ -162,7 +162,7 @@ class EpubPreferencesEditor internal constructor( RangePreferenceDelegate( getValue = { preferences.pageMargins }, getEffectiveValue = { state.settings.pageMargins }, - getIsEffective = { layout == EpubLayout.REFLOWABLE && !state.settings.scroll }, + getIsEffective = { layout == EpubLayout.REFLOWABLE }, updateValue = { value -> updateValues { it.copy(pageMargins = value) } }, supportedRange = configuration.pageMarginsRange, progressionStrategy = configuration.pageMarginsProgression, From 7852b353de8f8993bda31c06f7d38e2a849f4204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 30 Nov 2022 12:36:35 +0100 Subject: [PATCH 04/45] Remove `SwitchPreference` --- docs/guides/navigator-settings.md | 10 ++++----- .../navigator/PsPdfKitPreferencesEditor.kt | 22 ++++++++++++++----- .../navigator/epub/EpubPreferencesEditor.kt | 20 ++++++++--------- .../r2/navigator/preferences/Preference.kt | 22 ++++++++----------- .../preferences/PreferenceDelegate.kt | 15 ------------- .../reader/preferences/UserPreferences.kt | 18 +++++++-------- 6 files changed, 50 insertions(+), 57 deletions(-) diff --git a/docs/guides/navigator-settings.md b/docs/guides/navigator-settings.md index 0b4e8c9e67..99d1111d39 100644 --- a/docs/guides/navigator-settings.md +++ b/docs/guides/navigator-settings.md @@ -158,7 +158,7 @@ This stateless composable displays the actual preferences for a reflowable publi ```kotlin @Composable fun ReflowableUserPreferences( - publisherStyles: SwitchPreference? = null, + publisherStyles: Preference? = null, fontSize: RangePreference? = null, fontFamily: EnumPreference? = null, commit: () -> Unit @@ -179,15 +179,15 @@ fun ReflowableUserPreferences( } ``` -### Composable for a `SwitchPreference` +### Composable for a boolean `Preference` -A `SwitchPreference` can be represented as a simple switch button. +A `Preference` can be represented as a simple switch button. ```kotlin @Composable fun SwitchItem( title: String, - preference: SwitchPreference, + preference: Preference, commit: () -> Unit ) { ListItem( @@ -210,7 +210,7 @@ fun SwitchItem( } ``` -This composable takes advantage of the helpers in `SwitchPreference` to set the preference in two different ways: +This composable takes advantage of the helpers in `Preference` to set the preference in two different ways: * `toggle()` will invert the current preference when tapping on the whole list item. * `set(checked)` sets an explicit value provided by the `Switch`'s `onCheckedChange` callback. diff --git a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt index 3e92c97911..b9519932b7 100644 --- a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt +++ b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt @@ -7,7 +7,19 @@ package org.readium.adapters.pspdfkit.navigator import org.readium.r2.navigator.extensions.format -import org.readium.r2.navigator.preferences.* +import org.readium.r2.navigator.preferences.Axis +import org.readium.r2.navigator.preferences.DoubleIncrement +import org.readium.r2.navigator.preferences.EnumPreference +import org.readium.r2.navigator.preferences.EnumPreferenceDelegate +import org.readium.r2.navigator.preferences.Fit +import org.readium.r2.navigator.preferences.Preference +import org.readium.r2.navigator.preferences.PreferenceDelegate +import org.readium.r2.navigator.preferences.PreferencesEditor +import org.readium.r2.navigator.preferences.ProgressionStrategy +import org.readium.r2.navigator.preferences.RangePreference +import org.readium.r2.navigator.preferences.RangePreferenceDelegate +import org.readium.r2.navigator.preferences.ReadingProgression +import org.readium.r2.navigator.preferences.Spread import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.Metadata @@ -64,8 +76,8 @@ class PsPdfKitPreferencesEditor internal constructor( supportedValues = listOf(Fit.CONTAIN, Fit.WIDTH), ) - val offsetFirstPage: SwitchPreference = - SwitchPreferenceDelegate( + val offsetFirstPage: Preference = + PreferenceDelegate( getValue = { preferences.offsetFirstPage }, getEffectiveValue = { state.settings.offsetFirstPage }, getIsEffective = { !state.settings.scroll && state.settings.spread != Spread.NEVER }, @@ -81,8 +93,8 @@ class PsPdfKitPreferencesEditor internal constructor( supportedValues = listOf(ReadingProgression.LTR, ReadingProgression.RTL), ) - val scroll: SwitchPreference = - SwitchPreferenceDelegate( + val scroll: Preference = + PreferenceDelegate( getValue = { preferences.scroll }, getEffectiveValue = { state.settings.scroll }, getIsEffective = { true }, diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt index ea76f4dded..cf35f05ff7 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt @@ -103,8 +103,8 @@ class EpubPreferencesEditor internal constructor( valueFormatter = percentFormatter(), ) - val hyphens: SwitchPreference = - SwitchPreferenceDelegate( + val hyphens: Preference = + PreferenceDelegate( getValue = { preferences.hyphens }, getEffectiveValue = { state.settings.hyphens }, getIsEffective = { isHyphensEffective() }, @@ -139,8 +139,8 @@ class EpubPreferencesEditor internal constructor( valueFormatter = percentFormatter(), ) - val ligatures: SwitchPreference = - SwitchPreferenceDelegate( + val ligatures: Preference = + PreferenceDelegate( getValue = { preferences.ligatures }, getEffectiveValue = { state.settings.ligatures }, getIsEffective = { isLigaturesSpacing() }, @@ -191,8 +191,8 @@ class EpubPreferencesEditor internal constructor( valueFormatter = percentFormatter(), ) - val publisherStyles: SwitchPreference = - SwitchPreferenceDelegate( + val publisherStyles: Preference = + PreferenceDelegate( getValue = { preferences.publisherStyles }, getEffectiveValue = { state.settings.publisherStyles }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, @@ -208,8 +208,8 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(ReadingProgression.LTR, ReadingProgression.RTL), ) - val scroll: SwitchPreference = - SwitchPreferenceDelegate( + val scroll: Preference = + PreferenceDelegate( getValue = { preferences.scroll }, getEffectiveValue = { state.settings.scroll }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, @@ -271,8 +271,8 @@ class EpubPreferencesEditor internal constructor( progressionStrategy = StepsProgression(1.0, 1.067, 1.125, 1.2, 1.25, 1.333, 1.414, 1.5, 1.618), ) - val verticalText: SwitchPreference = - SwitchPreferenceDelegate( + val verticalText: Preference = + PreferenceDelegate( getValue = { preferences.verticalText }, getEffectiveValue = { state.settings.verticalText }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt index 17b34b76dd..eda44e322f 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt @@ -45,6 +45,15 @@ interface Preference { fun Preference.clear() = set(null) +/** + * Toggle the preference value. A default value is taken as the initial one if + * the preference is currently unset. + */ +@OptIn(ExperimentalReadiumApi::class) +fun Preference.toggle() { + set(!(value ?: effectiveValue)) +} + /** * A [Preference] which accepts a closed set of values. */ @@ -77,16 +86,3 @@ interface RangePreference> : Preference { */ fun formatValue(value: T): String } - -/** - * A [Boolean] preference. - */ -@ExperimentalReadiumApi -interface SwitchPreference : Preference { - - /** - * Toggle the preference value. A default value is taken as the initial one if - * the preference is currently unset. - */ - fun toggle() -} diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt index e02b81ffed..774c49dc5e 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt @@ -48,21 +48,6 @@ class EnumPreferenceDelegate( } } -@InternalReadiumApi -class SwitchPreferenceDelegate( - getValue: () -> Boolean?, - getEffectiveValue: () -> Boolean, - getIsEffective: () -> Boolean, - updateValue: (Boolean?) -> Unit, -) : PreferenceDelegate(getValue, getEffectiveValue, getIsEffective, updateValue), - SwitchPreference { - - override fun toggle() { - val currentValue = value ?: effectiveValue - set(!currentValue) - } -} - @InternalReadiumApi class RangePreferenceDelegate>( getValue: () -> T?, diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index 63c7d90b02..26401942b0 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -143,11 +143,11 @@ private fun ColumnScope.FixedLayoutUserPreferences( commit: () -> Unit, language: Preference? = null, readingProgression: EnumPreference? = null, - scroll: SwitchPreference? = null, + scroll: Preference? = null, scrollAxis: EnumPreference? = null, fit: EnumPreference? = null, spread: EnumPreference? = null, - offsetFirstPage: SwitchPreference? = null, + offsetFirstPage: Preference? = null, pageSpacing: RangePreference? = null ) { if (language != null || readingProgression != null) { @@ -267,24 +267,24 @@ private fun ColumnScope.ReflowableUserPreferences( columnCount: EnumPreference? = null, fontFamily: EnumPreference? = null, fontSize: RangePreference? = null, - hyphens: SwitchPreference? = null, + hyphens: Preference? = null, imageFilter: EnumPreference? = null, language: Preference? = null, letterSpacing: RangePreference? = null, - ligatures: SwitchPreference? = null, + ligatures: Preference? = null, lineHeight: RangePreference? = null, pageMargins: RangePreference? = null, paragraphIndent: RangePreference? = null, paragraphSpacing: RangePreference? = null, - publisherStyles: SwitchPreference? = null, + publisherStyles: Preference? = null, readingProgression: EnumPreference? = null, - scroll: SwitchPreference? = null, + scroll: Preference? = null, textAlign: EnumPreference? = null, textColor: Preference? = null, textNormalization: EnumPreference? = null, theme: EnumPreference? = null, typeScale: RangePreference? = null, - verticalText: SwitchPreference? = null, + verticalText: Preference? = null, wordSpacing: RangePreference? = null, ) { if (language != null || readingProgression != null || verticalText != null) { @@ -722,12 +722,12 @@ private fun StepperItem( } /** - * Component for a [SwitchPreference]. + * Component for a boolean [Preference]. */ @Composable private fun SwitchItem( title: String, - preference: SwitchPreference, + preference: Preference, commit: () -> Unit ) { SwitchItem( From ac0516f906bdc1dc4599e19465312042f49873dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 30 Nov 2022 12:36:56 +0100 Subject: [PATCH 05/45] Add `Preference.toggle(T?)`, which is useful for button groups --- .../r2/navigator/preferences/Preference.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt index eda44e322f..e1084f66dc 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt @@ -45,6 +45,20 @@ interface Preference { fun Preference.clear() = set(null) +/** + * Toggles the preference to the given [value]. + * + * If the preference was already set to the same value, it is removed. + */ +@ExperimentalReadiumApi +fun Preference.toggle(value: T?) { + if (this.value != value) { + set(value) + } else { + clear() + } +} + /** * Toggle the preference value. A default value is taken as the initial one if * the preference is currently unset. @@ -59,7 +73,9 @@ fun Preference.toggle() { */ @ExperimentalReadiumApi interface EnumPreference : Preference { - + /** + * List of valid values for this preference. + */ val supportedValues: List } From 3d0914078aeecd21652a2fbebf9ad8cca54907b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Sat, 3 Dec 2022 16:19:34 +0100 Subject: [PATCH 06/45] Add mapping capabilities --- .../navigator/preferences/MappedPreference.kt | 157 ++++++++++++++++++ .../r2/navigator/preferences/Preference.kt | 7 + 2 files changed, 164 insertions(+) create mode 100644 readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt new file mode 100644 index 0000000000..c1f0e08977 --- /dev/null +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt @@ -0,0 +1,157 @@ +package org.readium.r2.navigator.preferences + +import org.readium.r2.shared.ExperimentalReadiumApi + +/** + * Creates a new [Preference] object wrapping the receiver and converting its value [from] and [to] + * the target type [V]. + */ +@ExperimentalReadiumApi +fun Preference.map(from: (T) -> V, to: (V) -> T): Preference = + MappedPreference(this, from, to) + +/** + * Creates a new [EnumPreference] object wrapping the receiver and converting its value and + * [supportedValues], [from] and [to] the target type [V]. + */ +@ExperimentalReadiumApi +fun EnumPreference.map( + from: (T) -> V, + to: (V) -> T, + supportedValues: (List) -> List = { it.map(from) } +): EnumPreference = + MappedEnumPreference(this, from, to, supportedValues) + +/** + * Creates a new [EnumPreference] object wrapping the receiver with the provided [supportedValues]. + */ +@ExperimentalReadiumApi +fun EnumPreference.withSupportedValues(supportedValues: List): EnumPreference = + map(from = { it }, to = { it }, supportedValues = { supportedValues }) + +/** + * Creates a new [EnumPreference] object wrapping the receiver and transforming its supported + * values with [transform]. + */ +@ExperimentalReadiumApi +fun EnumPreference.mapSupportedValues(transform: (List) -> List): EnumPreference = + map(from = { it }, to = { it }, supportedValues = transform) + +/** + * Creates a new [RangePreference] object wrapping the receiver and converting its value and + * [supportedRange], [from] and [to] the target type [V]. + * + * The value formatter, or [increment] and [decrement] strategy of the receiver can be overwritten. + */ +@ExperimentalReadiumApi +fun , V : Comparable> RangePreference.map( + from: (T) -> V, + to: (V) -> T, + supportedRange: (ClosedRange) -> ClosedRange = { from(it.start)..from(it.endInclusive) }, + formatValue: ((V) -> String)? = null, + increment: (() -> Unit)? = null, + decrement: (() -> Unit)? = null, +): RangePreference = + MappedRangePreference( + this, from, to, + transformSupportedRange = supportedRange, + valueFormatter = formatValue, + incrementer = increment, + decrementer = decrement + ) + +/** + * Creates a new [RangePreference] object wrapping the receiver and transforming its + * [supportedRange], or overwriting its [formatValue] or [increment] and [decrement] strategy. + */ +@ExperimentalReadiumApi +fun > RangePreference.map( + supportedRange: (ClosedRange) -> ClosedRange = { it }, + formatValue: ((T) -> String)? = null, + increment: (() -> Unit)? = null, + decrement: (() -> Unit)? = null, +): RangePreference = + MappedRangePreference( + this, { it }, { it }, + transformSupportedRange = supportedRange, + valueFormatter = formatValue, + incrementer = increment, + decrementer = decrement + ) + +/** + * Creates a new [RangePreference] object wrapping the receiver and using a different supported + * [range]. A new [progressionStrategy] can be provided to customize the implementation of increment + * and decrement. + */ +@ExperimentalReadiumApi +fun > RangePreference.withSupportedRange(range: ClosedRange, progressionStrategy: ProgressionStrategy? = null): RangePreference = + map( + supportedRange = { range }, + increment = progressionStrategy?.run {{ + val currentValue = value ?: effectiveValue + val newValue = increment(currentValue).coerceIn(range) + set(newValue) + }}, + decrement = progressionStrategy?.run {{ + val currentValue = value ?: effectiveValue + val newValue = decrement(currentValue).coerceIn(range) + set(newValue) + }} + ) + +@ExperimentalReadiumApi +private open class MappedPreference( + protected open val original: Preference, + protected val from: (T) -> V, + protected val to: (V) -> T +) : Preference { + override val value: V? + get() = original.value?.let(from) + override val effectiveValue: V + get() = original.effectiveValue.let(from) + override val isEffective: Boolean + get() = original.isEffective + + override fun set(value: V?) { + original.set(value?.let(to)) + } +} + +@ExperimentalReadiumApi +private class MappedEnumPreference( + override val original: EnumPreference, + from: (T) -> V, + to: (V) -> T, + private val transformSupportedValues: (List) -> List +) : MappedPreference(original, from, to), EnumPreference { + + override val supportedValues: List + get() = transformSupportedValues(original.supportedValues) +} + +@ExperimentalReadiumApi +private class MappedRangePreference, V : Comparable>( + override val original: RangePreference, + from: (T) -> V, + to: (V) -> T, + private val transformSupportedRange: (ClosedRange) -> ClosedRange, + private val valueFormatter: ((V) -> String)?, + private val incrementer: (() -> Unit)?, + private val decrementer: (() -> Unit)? +) : MappedPreference(original, from, to), RangePreference { + + override val supportedRange: ClosedRange + get() = transformSupportedRange(original.supportedRange) + + override fun increment() { + incrementer?.invoke() ?: original.increment() + } + + override fun decrement() { + decrementer?.invoke() ?: original.decrement() + } + + override fun formatValue(value: V): String = + valueFormatter?.invoke(value) ?: original.formatValue(to(value)) +} diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt index e1084f66dc..96cf35ee5d 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt @@ -68,6 +68,13 @@ fun Preference.toggle() { set(!(value ?: effectiveValue)) } +/** + * Returns a new preference with its boolean value flipped. + */ +@OptIn(ExperimentalReadiumApi::class) +fun Preference.flipped(): Preference = + map(from = { !it }, to = { !it }) + /** * A [Preference] which accepts a closed set of values. */ From 64b5f969a93bf21672c035d189ed1156081076d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Sat, 3 Dec 2022 19:27:46 +0100 Subject: [PATCH 07/45] Add more font weight variants --- .../navigator/epub/css/FontFamilyDeclaration.kt | 17 +++++++++++++---- .../java/org/readium/r2/shared/util/Either.kt | 3 +++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt index 609ca0fd52..83124a3a47 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt @@ -57,7 +57,7 @@ data class FontFaceDeclaration internal constructor( set("src", src) fontStyle?.let { set("font-style", it.name.lowercase()) } - fontWeight?.let { set("font-weight", it.name.lowercase()) } + fontWeight?.let { set("font-weight", it.value) } } val descriptorList = descriptors @@ -135,9 +135,18 @@ enum class FontStyle { /** * Weight (or boldness) of a font. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-weight#common_weight_name_mapping */ @ExperimentalReadiumApi -enum class FontWeight { - NORMAL, - BOLD; +enum class FontWeight(internal val value: Int) { + THIN(100), + EXTRA_LIGHT(200), + LIGHT(300), + NORMAL(400), + MEDIUM(500), + SEMI_BOLD(600), + BOLD(700), + EXTRA_BOLD(800), + BLACK(900); } diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/Either.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/Either.kt index 2142031e14..3a9d05e8e7 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/Either.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/Either.kt @@ -20,6 +20,9 @@ sealed class Either { is B -> Right(value) else -> throw IllegalArgumentException("Provided value must be an instance of ${A::class.simpleName} or ${B::class.simpleName}") } + + inline fun listOf(vararg values: Any): List> = + values.map { Either(it) } } val left: A? From 7cb5935a806802e0152ad18ca6e486b5a3170192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Sat, 3 Dec 2022 19:45:10 +0100 Subject: [PATCH 08/45] Font family is not an enum anymore --- .../r2/navigator/epub/EpubPreferencesEditor.kt | 7 +++---- .../r2/navigator/preferences/MappedPreference.kt | 13 +++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt index cf35f05ff7..5b0522bb52 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt @@ -83,13 +83,12 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(ColumnCount.AUTO, ColumnCount.ONE, ColumnCount.TWO), ) - val fontFamily: EnumPreference = - EnumPreferenceDelegate( + val fontFamily: Preference = + PreferenceDelegate( getValue = { preferences.fontFamily }, getEffectiveValue = { state.settings.fontFamily }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, - updateValue = { value -> updateValues { it.copy(fontFamily = value) } }, - supportedValues = listOf(null) + configuration.fontFamilies, + updateValue = { value -> updateValues { it.copy(fontFamily = value) } } ) val fontSize: RangePreference = diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt index c1f0e08977..2c974562c9 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt @@ -10,6 +10,13 @@ import org.readium.r2.shared.ExperimentalReadiumApi fun Preference.map(from: (T) -> V, to: (V) -> T): Preference = MappedPreference(this, from, to) +/** + * Creates a new [EnumPreference] object wrapping the receiver with the provided [supportedValues]. + */ +@ExperimentalReadiumApi +fun Preference.withSupportedValues(supportedValues: List): EnumPreference = + PreferenceWithSupportedValues(this, supportedValues) + /** * Creates a new [EnumPreference] object wrapping the receiver and converting its value and * [supportedValues], [from] and [to] the target type [V]. @@ -118,6 +125,12 @@ private open class MappedPreference( } } +@ExperimentalReadiumApi +private class PreferenceWithSupportedValues( + override val original: Preference, + override val supportedValues: List, +) : MappedPreference(original, from = { it }, to = { it }), EnumPreference + @ExperimentalReadiumApi private class MappedEnumPreference( override val original: EnumPreference, From 1889e9caecc1cc06f15095ae4ebc4f470067a8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Sat, 3 Dec 2022 20:56:38 +0100 Subject: [PATCH 09/45] Fix coercion of range preference --- .../navigator/preferences/MappedPreference.kt | 41 +++++++++++-------- .../preferences/PreferenceDelegate.kt | 11 +++-- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt index 2c974562c9..afcca11730 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt @@ -56,8 +56,8 @@ fun , V : Comparable> RangePreference.map( to: (V) -> T, supportedRange: (ClosedRange) -> ClosedRange = { from(it.start)..from(it.endInclusive) }, formatValue: ((V) -> String)? = null, - increment: (() -> Unit)? = null, - decrement: (() -> Unit)? = null, + increment: (RangePreference.() -> Unit)? = null, + decrement: (RangePreference.() -> Unit)? = null, ): RangePreference = MappedRangePreference( this, from, to, @@ -75,8 +75,8 @@ fun , V : Comparable> RangePreference.map( fun > RangePreference.map( supportedRange: (ClosedRange) -> ClosedRange = { it }, formatValue: ((T) -> String)? = null, - increment: (() -> Unit)? = null, - decrement: (() -> Unit)? = null, + increment: (RangePreference.() -> Unit)? = null, + decrement: (RangePreference.() -> Unit)? = null, ): RangePreference = MappedRangePreference( this, { it }, { it }, @@ -92,19 +92,17 @@ fun > RangePreference.map( * and decrement. */ @ExperimentalReadiumApi -fun > RangePreference.withSupportedRange(range: ClosedRange, progressionStrategy: ProgressionStrategy? = null): RangePreference = +fun > RangePreference.withSupportedRange(range: ClosedRange, progressionStrategy: ProgressionStrategy): RangePreference = map( supportedRange = { range }, - increment = progressionStrategy?.run {{ + increment = { val currentValue = value ?: effectiveValue - val newValue = increment(currentValue).coerceIn(range) - set(newValue) - }}, - decrement = progressionStrategy?.run {{ + this.set(progressionStrategy.increment(currentValue)) + }, + decrement = { val currentValue = value ?: effectiveValue - val newValue = decrement(currentValue).coerceIn(range) - set(newValue) - }} + set(progressionStrategy.decrement(currentValue)) + } ) @ExperimentalReadiumApi @@ -141,6 +139,11 @@ private class MappedEnumPreference( override val supportedValues: List get() = transformSupportedValues(original.supportedValues) + + override fun set(value: V?) { + require(value == null || value in supportedValues) + super.set(value) + } } @ExperimentalReadiumApi @@ -150,19 +153,23 @@ private class MappedRangePreference, V : Comparable>( to: (V) -> T, private val transformSupportedRange: (ClosedRange) -> ClosedRange, private val valueFormatter: ((V) -> String)?, - private val incrementer: (() -> Unit)?, - private val decrementer: (() -> Unit)? + private val incrementer: (RangePreference.() -> Unit)?, + private val decrementer: (RangePreference.() -> Unit)? ) : MappedPreference(original, from, to), RangePreference { override val supportedRange: ClosedRange get() = transformSupportedRange(original.supportedRange) + override fun set(value: V?) { + super.set(value?.coerceIn(supportedRange)) + } + override fun increment() { - incrementer?.invoke() ?: original.increment() + incrementer?.invoke(this) ?: original.increment() } override fun decrement() { - decrementer?.invoke() ?: original.decrement() + decrementer?.invoke(this) ?: original.decrement() } override fun formatValue(value: V): String = diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt index 774c49dc5e..c470ea55ef 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt @@ -60,18 +60,21 @@ class RangePreferenceDelegate>( ) : PreferenceDelegate(getValue, getEffectiveValue, getIsEffective, updateValue), RangePreference { + override fun set(value: T?) { + super.set(value?.coerceIn(supportedRange)) + } + override fun formatValue(value: T): String = valueFormatter.invoke(value) + override fun increment() { val currentValue = value ?: effectiveValue - val newValue = progressionStrategy.increment(currentValue).coerceIn(supportedRange) - set(newValue) + set(progressionStrategy.increment(currentValue)) } override fun decrement() { val currentValue = value ?: effectiveValue - val newValue = progressionStrategy.decrement(currentValue).coerceIn(supportedRange) - set(newValue) + set(progressionStrategy.decrement(currentValue)) } } From b092343ab702ad0d1e9015bf27d980898753ba5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Sun, 4 Dec 2022 20:10:42 +0100 Subject: [PATCH 10/45] Combine preferences filter --- .../r2/navigator/preferences/Configurable.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt index b0a96b91f4..6bb65e7421 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt @@ -93,3 +93,16 @@ fun interface PreferencesFilter

> { fun filter(preferences: P): P } + +@ExperimentalReadiumApi +operator fun

> PreferencesFilter

.plus(other: PreferencesFilter

): PreferencesFilter

= + CombinedPreferencesFilter(this, other) + +@ExperimentalReadiumApi +private class CombinedPreferencesFilter

>( + private val inner: PreferencesFilter

, + private val outer: PreferencesFilter

+) : PreferencesFilter

{ + override fun filter(preferences: P): P = + outer.filter(inner.filter(preferences)) +} From 0e4c75f8d302b87dcdf513577f672704a7d64b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Sun, 4 Dec 2022 22:05:21 +0100 Subject: [PATCH 11/45] Improve background color support --- .../r2/navigator/epub/EpubNavigatorFragment.kt | 5 +++++ .../readium/r2/testapp/reader/EpubReaderFragment.kt | 11 ----------- .../r2/testapp/reader/preferences/UserPreferences.kt | 9 +++++++-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index f92c7df883..3b3da9fdfe 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -366,6 +366,8 @@ class EpubNavigatorFragment internal constructor( EpubLayout.REFLOWABLE, null -> Publication.TYPE.EPUB EpubLayout.FIXED -> Publication.TYPE.FXL } + resourcePager.setBackgroundColor(viewModel.settings.value.backgroundColor.int) + parent.addView(resourcePager) resetResourcePagerAdapter() @@ -457,6 +459,9 @@ class EpubNavigatorFragment internal constructor( if (previous.fontSize != new.fontSize) { r2PagerAdapter?.setFontSize(new.fontSize) } + if (previous.backgroundColor != new.backgroundColor) { + resourcePager.setBackgroundColor(new.backgroundColor.int) + } } private fun R2PagerAdapter.setFontSize(fontSize: Double) { diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt index b433f03630..b44fc9cf36 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt @@ -123,17 +123,6 @@ class EpubReaderFragment : VisualReaderFragment(), EpubNavigatorFragment.Listene @Suppress("Unchecked_cast") (model.settings as UserPreferencesViewModel) .bind(navigator, viewLifecycleOwner) - - // This is a hack to draw the right background color on top and bottom blank spaces - viewLifecycleOwner.lifecycleScope.launchWhenStarted { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - model.settings?.theme - ?.onEach { theme -> - navigator.resourcePager.setBackgroundColor(theme.backgroundColor) - } - ?.launchIn(this) - } - } } override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) { diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index 26401942b0..fffa898f55 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -265,7 +265,7 @@ private fun ColumnScope.ReflowableUserPreferences( commit: () -> Unit, backgroundColor: Preference? = null, columnCount: EnumPreference? = null, - fontFamily: EnumPreference? = null, + fontFamily: Preference? = null, fontSize: RangePreference? = null, hyphens: Preference? = null, imageFilter: EnumPreference? = null, @@ -423,7 +423,12 @@ private fun ColumnScope.ReflowableUserPreferences( if (fontFamily != null) { MenuItem( title = "Typeface", - preference = fontFamily, + preference = fontFamily + .withSupportedValues(listOf( + null, FontFamily.SERIF, FontFamily.SANS_SERIF, FontFamily.MONOSPACE, + FontFamily.ACCESSIBLE_DFA, FontFamily.IA_WRITER_DUOSPACE, + FontFamily.OPEN_DYSLEXIC + )), commit = commit ) { value -> when (value) { From 9d0de3ed900d687c7654730a83ffa53e697591ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 5 Dec 2022 16:22:04 +0100 Subject: [PATCH 12/45] Remove preferences editor configurations --- .../pdfium/navigator/PdfiumEngineProvider.kt | 13 ++--- .../navigator/PdfiumPreferencesEditor.kt | 16 +------ .../navigator/PsPdfKitEngineProvider.kt | 13 ++--- .../navigator/PsPdfKitPreferencesEditor.kt | 17 +------ .../r2/navigator/epub/EpubNavigatorFactory.kt | 6 +-- .../navigator/epub/EpubPreferencesEditor.kt | 47 +++++++++---------- .../navigator/preferences/MappedPreference.kt | 5 +- .../r2/navigator/preferences/Preference.kt | 14 ------ .../preferences/PreferenceDelegate.kt | 1 - .../java/org/readium/r2/testapp/Readium.kt | 16 ------- .../r2/testapp/reader/EpubReaderFragment.kt | 5 -- .../r2/testapp/reader/ReaderRepository.kt | 2 +- .../reader/preferences/UserPreferences.kt | 15 ++++-- .../readium/r2/testapp/shared/views/List.kt | 8 ++-- 14 files changed, 58 insertions(+), 120 deletions(-) diff --git a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt index 2bbe40b9e6..33cbcb1509 100644 --- a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt +++ b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt @@ -17,16 +17,14 @@ import org.readium.r2.shared.publication.Publication /** * Main component to use the PDF navigator with the PDFium adapter. * - * Provide [PdfiumDefaults] and [PdfiumPreferencesEditor.Configuration] to customize - * the default values that will be used by the navigator for some preferences and the - * way that preferences will be allowed to be modified through preferences editors created - * by this component. + * Provide [PdfiumDefaults] to customize the default values that will be used by the navigator for + * some preferences and the way that preferences will be allowed to be modified through preferences + * editors created by this component. */ @ExperimentalReadiumApi class PdfiumEngineProvider( private val listener: PdfiumDocumentFragment.Listener? = null, - private val defaults: PdfiumDefaults = PdfiumDefaults(), - private val preferencesEditorConfiguration: PdfiumPreferencesEditor.Configuration = PdfiumPreferencesEditor.Configuration() + private val defaults: PdfiumDefaults = PdfiumDefaults() ) : PdfEngineProvider { override suspend fun createDocumentFragment(input: PdfDocumentFragmentInput) = @@ -58,8 +56,7 @@ class PdfiumEngineProvider( PdfiumPreferencesEditor( initialPreferences, publication.metadata, - defaults, - preferencesEditorConfiguration + defaults ) override fun createEmptyPreferences(): PdfiumPreferences = diff --git a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt index 842e6f6216..35ef686698 100644 --- a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt +++ b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt @@ -23,20 +23,8 @@ class PdfiumPreferencesEditor internal constructor( initialPreferences: PdfiumPreferences, publicationMetadata: Metadata, defaults: PdfiumDefaults, - configuration: Configuration ) : PreferencesEditor { - /** - * Configuration for [PdfiumPreferencesEditor]. - * - * @param pageSpacingRange The allowed range for page spacing. - * @param pageSpacingProgression The progression strategy for page spacing. - */ - data class Configuration( - val pageSpacingRange: ClosedRange = 0.0..50.0, - val pageSpacingProgression: ProgressionStrategy = DoubleIncrement(5.0), - ) - private data class State( val preferences: PdfiumPreferences, val settings: PdfiumSettings @@ -70,8 +58,8 @@ class PdfiumPreferencesEditor internal constructor( getEffectiveValue = { state.settings.pageSpacing }, getIsEffective = { true }, updateValue = { value -> updateValues { it.copy(pageSpacing = value) } }, - supportedRange = configuration.pageSpacingRange, - progressionStrategy = configuration.pageSpacingProgression, + supportedRange = 0.0..50.0, + progressionStrategy = DoubleIncrement(5.0), valueFormatter = { "${it.format(1)} dp" }, ) diff --git a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt index 16fcc80743..30784410fe 100644 --- a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt +++ b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt @@ -22,16 +22,14 @@ import org.readium.r2.shared.util.pdf.cachedIn /** * Main component to use the PDF navigator with PSPDFKit. * - * Provide [PsPdfKitDefaults] and [PsPdfKitPreferencesEditor.Configuration] to customize - * the default values that will be used by the navigator for some preferences and the - * way that preferences will be allowed to be modified through preferences editors created - * by this component. + * Provide [PsPdfKitDefaults] to customize the default values that will be used by the navigator for + * some preferences and the way that preferences will be allowed to be modified through preferences + * editors created by this component. */ @ExperimentalReadiumApi class PsPdfKitEngineProvider( private val context: Context, - private val defaults: PsPdfKitDefaults = PsPdfKitDefaults(), - private val preferencesEditorConfiguration: PsPdfKitPreferencesEditor.Configuration = PsPdfKitPreferencesEditor.Configuration() + private val defaults: PsPdfKitDefaults = PsPdfKitDefaults() ) : PdfEngineProvider { override suspend fun createDocumentFragment( @@ -71,8 +69,7 @@ class PsPdfKitEngineProvider( PsPdfKitPreferencesEditor( initialPreferences = initialPreferences, publicationMetadata = publication.metadata, - defaults = defaults, - configuration = preferencesEditorConfiguration + defaults = defaults ) override fun createEmptyPreferences(): PsPdfKitPreferences = diff --git a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt index b9519932b7..8c21277ccf 100644 --- a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt +++ b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt @@ -15,7 +15,6 @@ import org.readium.r2.navigator.preferences.Fit import org.readium.r2.navigator.preferences.Preference import org.readium.r2.navigator.preferences.PreferenceDelegate import org.readium.r2.navigator.preferences.PreferencesEditor -import org.readium.r2.navigator.preferences.ProgressionStrategy import org.readium.r2.navigator.preferences.RangePreference import org.readium.r2.navigator.preferences.RangePreferenceDelegate import org.readium.r2.navigator.preferences.ReadingProgression @@ -35,20 +34,8 @@ class PsPdfKitPreferencesEditor internal constructor( initialPreferences: PsPdfKitPreferences, publicationMetadata: Metadata, defaults: PsPdfKitDefaults, - configuration: Configuration ) : PreferencesEditor { - /** - * Configuration for [PsPdfKitPreferencesEditor]. - * - * @param pageSpacingRange The allowed range for page spacing. - * @param pageSpacingProgression The progression strategy for page spacing. - */ - data class Configuration( - val pageSpacingRange: ClosedRange = 0.0..50.0, - val pageSpacingProgression: ProgressionStrategy = DoubleIncrement(5.0), - ) - private data class State( val preferences: PsPdfKitPreferences, val settings: PsPdfKitSettings @@ -125,8 +112,8 @@ class PsPdfKitPreferencesEditor internal constructor( getEffectiveValue = { state.settings.pageSpacing }, getIsEffective = { true }, updateValue = { value -> updateValues { it.copy(pageSpacing = value) } }, - supportedRange = configuration.pageSpacingRange, - progressionStrategy = configuration.pageSpacingProgression, + supportedRange = 0.0..50.0, + progressionStrategy = DoubleIncrement(5.0), valueFormatter = { "${it.format(1)} dp" }, ) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFactory.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFactory.kt index 7335fc5d52..e02a03b13c 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFactory.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFactory.kt @@ -28,10 +28,9 @@ class EpubNavigatorFactory( * Configuration for the [EpubNavigatorFactory]. * * @param defaults navigator fallbacks for some preferences - * @param preferencesEditorConfiguration configuration of preferences editors that will be created */ + */ data class Configuration( val defaults: EpubDefaults = EpubDefaults(), - val preferencesEditorConfiguration: EpubPreferencesEditor.Configuration = EpubPreferencesEditor.Configuration(), ) private val layout: EpubLayout = @@ -64,7 +63,6 @@ class EpubNavigatorFactory( initialPreferences = currentPreferences, publicationMetadata = publication.metadata, layout = layout, - defaults = configuration.defaults, - configuration = configuration.preferencesEditorConfiguration, + defaults = configuration.defaults ) } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt index 5b0522bb52..751b374974 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt @@ -8,7 +8,24 @@ package org.readium.r2.navigator.epub import org.readium.r2.navigator.epub.css.Layout import org.readium.r2.navigator.extensions.format -import org.readium.r2.navigator.preferences.* +import org.readium.r2.navigator.preferences.Color +import org.readium.r2.navigator.preferences.ColumnCount +import org.readium.r2.navigator.preferences.DoubleIncrement +import org.readium.r2.navigator.preferences.EnumPreference +import org.readium.r2.navigator.preferences.EnumPreferenceDelegate +import org.readium.r2.navigator.preferences.FontFamily +import org.readium.r2.navigator.preferences.ImageFilter +import org.readium.r2.navigator.preferences.Preference +import org.readium.r2.navigator.preferences.PreferenceDelegate +import org.readium.r2.navigator.preferences.PreferencesEditor +import org.readium.r2.navigator.preferences.RangePreference +import org.readium.r2.navigator.preferences.RangePreferenceDelegate +import org.readium.r2.navigator.preferences.ReadingProgression +import org.readium.r2.navigator.preferences.Spread +import org.readium.r2.navigator.preferences.StepsProgression +import org.readium.r2.navigator.preferences.TextAlign +import org.readium.r2.navigator.preferences.TextNormalization +import org.readium.r2.navigator.preferences.Theme import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.Metadata import org.readium.r2.shared.publication.epub.EpubLayout @@ -26,27 +43,9 @@ class EpubPreferencesEditor internal constructor( initialPreferences: EpubPreferences, publicationMetadata: Metadata, val layout: EpubLayout, - defaults: EpubDefaults, - configuration: Configuration + defaults: EpubDefaults ) : PreferencesEditor { - /** - * Configuration for [EpubPreferencesEditor]. - * - * @param fontFamilies a list of font families that can be selected in the editor - * @param fontSizeRange the range of font size values that can be set in the editor - * @param fontSizeProgression the way the font size value is to be increased or decreased - * @param pageMarginsRange the range of page margins values that can be set in the editor - * @param pageMarginsProgression the way the page margins value is to be increased or decreased - */ - data class Configuration( - val fontFamilies: List = DEFAULT_FONT_FAMILIES, - val fontSizeRange: ClosedRange = 0.4..5.0, - val fontSizeProgression: ProgressionStrategy = DoubleIncrement(0.1), - val pageMarginsRange: ClosedRange = 0.5..4.0, - val pageMarginsProgression: ProgressionStrategy = DoubleIncrement(0.3) - ) - private data class State( val preferences: EpubPreferences, val settings: EpubSettings, @@ -97,8 +96,8 @@ class EpubPreferencesEditor internal constructor( getEffectiveValue = { state.settings.fontSize }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, updateValue = { value -> updateValues { it.copy(fontSize = value) } }, - supportedRange = configuration.fontSizeRange, - progressionStrategy = configuration.fontSizeProgression, + supportedRange = 0.4..5.0, + progressionStrategy = DoubleIncrement(0.1), valueFormatter = percentFormatter(), ) @@ -163,8 +162,8 @@ class EpubPreferencesEditor internal constructor( getEffectiveValue = { state.settings.pageMargins }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, updateValue = { value -> updateValues { it.copy(pageMargins = value) } }, - supportedRange = configuration.pageMarginsRange, - progressionStrategy = configuration.pageMarginsProgression, + supportedRange = 0.5..4.0, + progressionStrategy = DoubleIncrement(0.3), valueFormatter = { it.format(5) }, ) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt index afcca11730..6b33ed9c59 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt @@ -92,7 +92,10 @@ fun > RangePreference.map( * and decrement. */ @ExperimentalReadiumApi -fun > RangePreference.withSupportedRange(range: ClosedRange, progressionStrategy: ProgressionStrategy): RangePreference = +fun > RangePreference.withSupportedRange( + range: ClosedRange, + progressionStrategy: ProgressionStrategy +): RangePreference = map( supportedRange = { range }, increment = { diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt index 96cf35ee5d..0788520078 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Preference.kt @@ -45,20 +45,6 @@ interface Preference { fun Preference.clear() = set(null) -/** - * Toggles the preference to the given [value]. - * - * If the preference was already set to the same value, it is removed. - */ -@ExperimentalReadiumApi -fun Preference.toggle(value: T?) { - if (this.value != value) { - set(value) - } else { - clear() - } -} - /** * Toggle the preference value. A default value is taken as the initial one if * the preference is currently unset. diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt index c470ea55ef..66c45882b2 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/PreferenceDelegate.kt @@ -67,7 +67,6 @@ class RangePreferenceDelegate>( override fun formatValue(value: T): String = valueFormatter.invoke(value) - override fun increment() { val currentValue = value ?: effectiveValue set(progressionStrategy.increment(currentValue)) diff --git a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt index 39745e8e96..c15378e2c7 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt @@ -9,9 +9,6 @@ package org.readium.r2.testapp import android.content.Context import org.readium.adapters.pdfium.document.PdfiumDocumentFactory import org.readium.r2.lcp.LcpService -import org.readium.r2.navigator.epub.EpubNavigatorFactory -import org.readium.r2.navigator.epub.EpubPreferencesEditor -import org.readium.r2.navigator.preferences.FontFamily import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.util.Try import org.readium.r2.streamer.Streamer @@ -41,17 +38,4 @@ class Readium(context: Context) { // Only required if you want to support PDF files using the PDFium adapter. pdfFactory = PdfiumDocumentFactory(context) ) - - val epubNavigatorConfig: EpubNavigatorFactory.Configuration = - EpubNavigatorFactory.Configuration( - preferencesEditorConfiguration = EpubPreferencesEditor.Configuration( - fontFamilies = listOf( - FontFamily.LITERATA, - FontFamily.SANS_SERIF, - FontFamily.IA_WRITER_DUOSPACE, - FontFamily.ACCESSIBLE_DFA, - FontFamily.OPEN_DYSLEXIC - ) - ) - ) } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt index b44fc9cf36..f3cf28e925 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt @@ -17,11 +17,6 @@ import androidx.appcompat.widget.SearchView import androidx.fragment.app.FragmentResultListener import androidx.fragment.app.commit import androidx.fragment.app.commitNow -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import org.readium.r2.navigator.ExperimentalDecorator import org.readium.r2.navigator.epub.* import org.readium.r2.navigator.epub.css.FontStyle diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderRepository.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderRepository.kt index bea5914387..5af2a13c6c 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderRepository.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderRepository.kt @@ -126,7 +126,7 @@ class ReaderRepository( val preferencesManager = EpubPreferencesManagerFactory(preferencesDataStore) .createPreferenceManager(bookId) - val navigatorFactory = EpubNavigatorFactory(publication, readium.epubNavigatorConfig) + val navigatorFactory = EpubNavigatorFactory(publication) return EpubReaderInitData( bookId, publication, initialLocator, diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index fffa898f55..22c0601f23 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -424,11 +424,16 @@ private fun ColumnScope.ReflowableUserPreferences( MenuItem( title = "Typeface", preference = fontFamily - .withSupportedValues(listOf( - null, FontFamily.SERIF, FontFamily.SANS_SERIF, FontFamily.MONOSPACE, - FontFamily.ACCESSIBLE_DFA, FontFamily.IA_WRITER_DUOSPACE, - FontFamily.OPEN_DYSLEXIC - )), + .withSupportedValues( + listOf( + null, + FontFamily.LITERATA, + FontFamily.SANS_SERIF, + FontFamily.IA_WRITER_DUOSPACE, + FontFamily.ACCESSIBLE_DFA, + FontFamily.OPEN_DYSLEXIC + ) + ), commit = commit ) { value -> when (value) { diff --git a/test-app/src/main/java/org/readium/r2/testapp/shared/views/List.kt b/test-app/src/main/java/org/readium/r2/testapp/shared/views/List.kt index de7a91e093..b2ad6b3ddd 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/shared/views/List.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/shared/views/List.kt @@ -29,10 +29,10 @@ fun SelectorListItem( fun dismiss() { isExpanded = false } ListItem( - modifier = Modifier.run { - if (enabled) clickable { isExpanded = true } - else this - }, + modifier = Modifier + .clickable(enabled = enabled) { + isExpanded = true + }, text = { Group(enabled = enabled) { Text(label) From dceec908c98ef115fb7eb09f035ec479651e9f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 5 Dec 2022 17:10:14 +0100 Subject: [PATCH 13/45] Allow preloading of font faces --- .../epub/css/FontFamilyDeclaration.kt | 31 ++++++++++++++++--- .../r2/navigator/epub/css/ReadiumCss.kt | 21 ++++++++----- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt index 83124a3a47..59cd71a970 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt @@ -43,16 +43,23 @@ internal fun buildFontFaceDeclaration( @ExperimentalReadiumApi data class FontFaceDeclaration internal constructor( private val fontFamily: String, - private val sources: List, + private val sources: List, private var fontStyle: FontStyle? = null, private var fontWeight: FontWeight? = null, ) { + internal fun links(urlNormalizer: (String) -> String): List = + sources + .filter { it.preload } + .map { + """""" + } + internal fun toCss(urlNormalizer: (String) -> String): String { val descriptors = buildMap { set("font-family", """"$fontFamily"""") - val urls = sources.map(urlNormalizer) + val urls = sources.map { urlNormalizer(it.href) } val src = urls.joinToString(", ") { """url("$it")""" } set("src", src) @@ -68,6 +75,17 @@ data class FontFaceDeclaration internal constructor( } } +/** + * Represents an individual font file. + * + * @param preload Indicates whether this source will be declared for preloading in the HTML using + * ``. + */ +data class FontFaceSource( + val href: String, + val preload: Boolean = false +) + /** * A mutable font family declaration. */ @@ -94,16 +112,19 @@ data class MutableFontFamilyDeclaration internal constructor( @ExperimentalReadiumApi data class MutableFontFaceDeclaration internal constructor( private val fontFamily: String, - private val sources: MutableList = mutableListOf(), + private val sources: MutableList = mutableListOf(), private var fontStyle: FontStyle? = null, private var fontWeight: FontWeight? = null, ) { /** * Add a source for the font face. + * + * @param preload Indicates whether this source will be declared for preloading in the HTML + * using ``. */ - fun addSource(url: String) { - this.sources.add(url) + fun addSource(href: String, preload: Boolean = false) { + this.sources.add(FontFaceSource(href = href, preload = preload)) } /** diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt index 3f02ca8f33..8a15409db1 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt @@ -53,6 +53,8 @@ internal data class ReadiumCss( content.insert( headBeforeIndex, "\n" + buildList { + addAll(fontsInjectableLinks) + add(stylesheetLink(stylesheetsFolder + "ReadiumCSS-before.css")) // Fix Readium CSS issue with the positioning of

, E : PreferencesEditor

> UserPref columnCount = editor.columnCount, fontFamily = editor.fontFamily, fontSize = editor.fontSize, + fontWeight = editor.fontWeight, hyphens = editor.hyphens, imageFilter = editor.imageFilter, language = editor.language, @@ -267,6 +268,7 @@ private fun ColumnScope.ReflowableUserPreferences( columnCount: EnumPreference? = null, fontFamily: Preference? = null, fontSize: RangePreference? = null, + fontWeight: RangePreference? = null, hyphens: Preference? = null, imageFilter: EnumPreference? = null, language: Preference? = null, @@ -281,7 +283,7 @@ private fun ColumnScope.ReflowableUserPreferences( scroll: Preference? = null, textAlign: EnumPreference? = null, textColor: Preference? = null, - textNormalization: EnumPreference? = null, + textNormalization: Preference? = null, theme: EnumPreference? = null, typeScale: RangePreference? = null, verticalText: Preference? = null, @@ -452,18 +454,20 @@ private fun ColumnScope.ReflowableUserPreferences( ) } + if (fontWeight != null) { + StepperItem( + title = "Font weight", + preference = fontWeight, + commit = commit + ) + } + if (textNormalization != null) { - ButtonGroupItem( + SwitchItem( title = "Text normalization", preference = textNormalization, - commit = commit, - ) { value -> - when (value) { - TextNormalization.NONE -> "None" - TextNormalization.BOLD -> "Bold" - TextNormalization.ACCESSIBILITY -> "A11y" - } - } + commit = commit + ) } Divider() @@ -953,7 +957,7 @@ val

> PreferencesEditor

.presets: List Preset("Increase legibility") { wordSpacing.set(0.6) fontSize.set(1.4) - textNormalization.set(TextNormalization.ACCESSIBILITY) + fontWeight.set(2.0) }, Preset("Document") { scroll.set(true) From 5311857d60135e7c559dc65e1a7a298bb057c45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Fri, 9 Dec 2022 17:23:10 +0100 Subject: [PATCH 19/45] Enable variable fonts --- .../epub/css/FontFamilyDeclaration.kt | 46 +++++++++++++------ .../r2/testapp/reader/EpubReaderFragment.kt | 2 + .../reader/preferences/UserPreferences.kt | 1 - 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt index 59cd71a970..e5bba8f44d 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt @@ -64,7 +64,15 @@ data class FontFaceDeclaration internal constructor( set("src", src) fontStyle?.let { set("font-style", it.name.lowercase()) } - fontWeight?.let { set("font-weight", it.value) } + + fontWeight?.let { + when (it) { + is FontWeight.Range -> + set("font-weight", "${it.range.start} ${it.range.endInclusive}") + is FontWeight.Value -> + set("font-weight", it.value) + } + } } val descriptorList = descriptors @@ -81,7 +89,7 @@ data class FontFaceDeclaration internal constructor( * @param preload Indicates whether this source will be declared for preloading in the HTML using * ``. */ -data class FontFaceSource( +internal data class FontFaceSource( val href: String, val preload: Boolean = false ) @@ -114,7 +122,7 @@ data class MutableFontFaceDeclaration internal constructor( private val fontFamily: String, private val sources: MutableList = mutableListOf(), private var fontStyle: FontStyle? = null, - private var fontWeight: FontWeight? = null, + private var fontWeight: FontWeight? = null ) { /** @@ -141,6 +149,13 @@ data class MutableFontFaceDeclaration internal constructor( this.fontWeight = fontWeight } + /** + * Set the font weight range of a variable font face. + */ + fun setFontWeightRange(range: ClosedRange = 1..1000) { + this.fontWeight = FontWeight.Range(range) + } + internal fun toFontFaceDeclaration() = FontFaceDeclaration(fontFamily, sources, fontStyle, fontWeight) } @@ -160,14 +175,19 @@ enum class FontStyle { * See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-weight#common_weight_name_mapping */ @ExperimentalReadiumApi -enum class FontWeight(internal val value: Int) { - THIN(100), - EXTRA_LIGHT(200), - LIGHT(300), - NORMAL(400), - MEDIUM(500), - SEMI_BOLD(600), - BOLD(700), - EXTRA_BOLD(800), - BLACK(900); +sealed class FontWeight { + data class Range(val range: ClosedRange = 1..1000) : FontWeight() + data class Value(val value: Int) : FontWeight() + + companion object { + val THIN = Value(100) + val EXTRA_LIGHT = Value(200) + val LIGHT = Value(300) + val NORMAL = Value(400) + val MEDIUM = Value(500) + val SEMI_BOLD = Value(600) + val BOLD = Value(700) + val EXTRA_BOLD = Value(800) + val BLACK = Value(900) + } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt index f3cf28e925..23ac551c01 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt @@ -69,10 +69,12 @@ class EpubReaderFragment : VisualReaderFragment(), EpubNavigatorFragment.Listene addFontFace { addSource("fonts/Literata-VariableFont_opsz,wght.ttf") setFontStyle(FontStyle.NORMAL) + setFontWeightRange() } addFontFace { addSource("fonts/Literata-Italic-VariableFont_opsz,wght.ttf") setFontStyle(FontStyle.ITALIC) + setFontWeightRange() } } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index 932ee74d58..c41b8e50cf 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -428,7 +428,6 @@ private fun ColumnScope.ReflowableUserPreferences( preference = fontFamily .withSupportedValues( listOf( - null, FontFamily.LITERATA, FontFamily.SANS_SERIF, FontFamily.IA_WRITER_DUOSPACE, From ce62c4854ac3eb2a0c19b09522589c083843ed2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 12 Dec 2022 18:16:24 +0100 Subject: [PATCH 20/45] Make most EPUB settings nullable --- .../navigator/epub/EpubNavigatorFragment.kt | 15 +- .../navigator/epub/EpubPreferencesEditor.kt | 102 +++++------- .../readium/r2/navigator/epub/EpubSettings.kt | 64 +++++--- .../r2/navigator/epub/EpubSettingsResolver.kt | 26 ++-- .../reader/preferences/UserPreferences.kt | 147 +++++++++--------- 5 files changed, 177 insertions(+), 177 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index 763abdea9f..09b92148fe 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -7,6 +7,7 @@ package org.readium.r2.navigator.epub import android.content.SharedPreferences +import android.graphics.Color as AndroidColor import android.graphics.PointF import android.graphics.RectF import android.os.Bundle @@ -388,7 +389,10 @@ class EpubNavigatorFragment internal constructor( EpubLayout.REFLOWABLE, null -> Publication.TYPE.EPUB EpubLayout.FIXED -> Publication.TYPE.FXL } - resourcePager.setBackgroundColor(viewModel.settings.value.backgroundColor.int) + resourcePager.setBackgroundColor( + viewModel.settings.value.backgroundColor?.int + ?: AndroidColor.TRANSPARENT + ) parent.addView(resourcePager) @@ -479,10 +483,13 @@ class EpubNavigatorFragment internal constructor( if (viewModel.layout == EpubLayout.FIXED) return if (previous.fontSize != new.fontSize) { - r2PagerAdapter?.setFontSize(new.fontSize) + r2PagerAdapter?.setFontSize(new.fontSize ?: 1.0) } if (previous.backgroundColor != new.backgroundColor) { - resourcePager.setBackgroundColor(new.backgroundColor.int) + resourcePager.setBackgroundColor( + new.backgroundColor?.int + ?: AndroidColor.TRANSPARENT + ) } } @@ -498,7 +505,7 @@ class EpubNavigatorFragment internal constructor( return } - (fragment as? R2EpubPageFragment)?.setFontSize(settings.value.fontSize) + (fragment as? R2EpubPageFragment)?.setFontSize(settings.value.fontSize ?: 1.0) } } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt index 8baa690924..e5dbec3da3 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt @@ -8,23 +8,7 @@ package org.readium.r2.navigator.epub import org.readium.r2.navigator.epub.css.Layout import org.readium.r2.navigator.extensions.format -import org.readium.r2.navigator.preferences.Color -import org.readium.r2.navigator.preferences.ColumnCount -import org.readium.r2.navigator.preferences.DoubleIncrement -import org.readium.r2.navigator.preferences.EnumPreference -import org.readium.r2.navigator.preferences.EnumPreferenceDelegate -import org.readium.r2.navigator.preferences.FontFamily -import org.readium.r2.navigator.preferences.ImageFilter -import org.readium.r2.navigator.preferences.Preference -import org.readium.r2.navigator.preferences.PreferenceDelegate -import org.readium.r2.navigator.preferences.PreferencesEditor -import org.readium.r2.navigator.preferences.RangePreference -import org.readium.r2.navigator.preferences.RangePreferenceDelegate -import org.readium.r2.navigator.preferences.ReadingProgression -import org.readium.r2.navigator.preferences.Spread -import org.readium.r2.navigator.preferences.StepsProgression -import org.readium.r2.navigator.preferences.TextAlign -import org.readium.r2.navigator.preferences.Theme +import org.readium.r2.navigator.preferences.* import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.Metadata import org.readium.r2.shared.publication.epub.EpubLayout @@ -67,8 +51,8 @@ class EpubPreferencesEditor internal constructor( val backgroundColor: Preference = PreferenceDelegate( getValue = { preferences.backgroundColor }, - getEffectiveValue = { state.settings.backgroundColor }, - getIsEffective = { layout == EpubLayout.REFLOWABLE }, + getEffectiveValue = { state.settings.backgroundColor ?: Color((theme.value ?: theme.effectiveValue).backgroundColor) }, + getIsEffective = { layout == EpubLayout.REFLOWABLE && preferences.backgroundColor != null }, updateValue = { value -> updateValues { it.copy(backgroundColor = value) } }, ) @@ -93,7 +77,7 @@ class EpubPreferencesEditor internal constructor( RangePreferenceDelegate( getValue = { preferences.fontWeight }, getEffectiveValue = { state.settings.fontWeight ?: 1.0 }, - getIsEffective = { layout == EpubLayout.REFLOWABLE }, + getIsEffective = { layout == EpubLayout.REFLOWABLE && preferences.fontWeight != null }, updateValue = { value -> updateValues { it.copy(fontWeight = value) } }, valueFormatter = percentFormatter(), supportedRange = 0.0..2.5, @@ -103,7 +87,7 @@ class EpubPreferencesEditor internal constructor( val fontSize: RangePreference = RangePreferenceDelegate( getValue = { preferences.fontSize }, - getEffectiveValue = { state.settings.fontSize }, + getEffectiveValue = { state.settings.fontSize ?: 1.0 }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, updateValue = { value -> updateValues { it.copy(fontSize = value) } }, supportedRange = 0.4..5.0, @@ -114,8 +98,8 @@ class EpubPreferencesEditor internal constructor( val hyphens: Preference = PreferenceDelegate( getValue = { preferences.hyphens }, - getEffectiveValue = { state.settings.hyphens }, - getIsEffective = { isHyphensEffective() }, + getEffectiveValue = { state.settings.hyphens ?: false }, + getIsEffective = ::isHyphensEffective, updateValue = { value -> updateValues { it.copy(hyphens = value) } }, ) @@ -139,8 +123,8 @@ class EpubPreferencesEditor internal constructor( val letterSpacing: RangePreference = RangePreferenceDelegate( getValue = { preferences.letterSpacing }, - getEffectiveValue = { state.settings.letterSpacing }, - getIsEffective = { isLetterSpacing() }, + getEffectiveValue = { state.settings.letterSpacing ?: 0.0 }, + getIsEffective = ::isLetterSpacingEffective, updateValue = { value -> updateValues { it.copy(letterSpacing = value) } }, supportedRange = 0.0..1.0, progressionStrategy = DoubleIncrement(0.1), @@ -150,16 +134,16 @@ class EpubPreferencesEditor internal constructor( val ligatures: Preference = PreferenceDelegate( getValue = { preferences.ligatures }, - getEffectiveValue = { state.settings.ligatures }, - getIsEffective = { isLigaturesSpacing() }, + getEffectiveValue = { state.settings.ligatures ?: false }, + getIsEffective = ::isLigaturesEffective, updateValue = { value -> updateValues { it.copy(ligatures = value) } }, ) val lineHeight: RangePreference = RangePreferenceDelegate( getValue = { preferences.lineHeight }, - getEffectiveValue = { state.settings.lineHeight }, - getIsEffective = { layout == EpubLayout.REFLOWABLE && !state.settings.publisherStyles }, + getEffectiveValue = { state.settings.lineHeight ?: 1.2 }, + getIsEffective = { layout == EpubLayout.REFLOWABLE && !state.settings.publisherStyles && preferences.lineHeight != null }, updateValue = { value -> updateValues { it.copy(lineHeight = value) } }, supportedRange = 1.0..2.0, progressionStrategy = DoubleIncrement(0.1), @@ -180,8 +164,8 @@ class EpubPreferencesEditor internal constructor( val paragraphIndent: RangePreference = RangePreferenceDelegate( getValue = { preferences.paragraphIndent }, - getEffectiveValue = { state.settings.paragraphIndent }, - getIsEffective = { isParagraphIndentEffective() }, + getEffectiveValue = { state.settings.paragraphIndent ?: 0.0 }, + getIsEffective = ::isParagraphIndentEffective, updateValue = { value -> updateValues { it.copy(paragraphIndent = value) } }, supportedRange = 0.0..3.0, progressionStrategy = DoubleIncrement(0.2), @@ -191,8 +175,8 @@ class EpubPreferencesEditor internal constructor( val paragraphSpacing: RangePreference = RangePreferenceDelegate( getValue = { preferences.paragraphSpacing }, - getEffectiveValue = { state.settings.paragraphSpacing }, - getIsEffective = { layout == EpubLayout.REFLOWABLE && !state.settings.publisherStyles }, + getEffectiveValue = { state.settings.paragraphSpacing ?: 0.0 }, + getIsEffective = { layout == EpubLayout.REFLOWABLE && !state.settings.publisherStyles && preferences.paragraphSpacing != null }, updateValue = { value -> updateValues { it.copy(paragraphSpacing = value) } }, supportedRange = 0.0..2.0, progressionStrategy = DoubleIncrement(0.1), @@ -233,11 +217,11 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(Spread.NEVER, Spread.ALWAYS), ) - val textAlign: EnumPreference = + val textAlign: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.textAlign }, getEffectiveValue = { state.settings.textAlign }, - getIsEffective = { isTextAlignEffective() }, + getIsEffective = ::isTextAlignEffective, updateValue = { value -> updateValues { it.copy(textAlign = value) } }, supportedValues = listOf(TextAlign.START, TextAlign.LEFT, TextAlign.RIGHT, TextAlign.JUSTIFY), ) @@ -245,15 +229,15 @@ class EpubPreferencesEditor internal constructor( val textColor: Preference = PreferenceDelegate( getValue = { preferences.textColor }, - getEffectiveValue = { state.settings.textColor }, - getIsEffective = { layout == EpubLayout.REFLOWABLE }, + getEffectiveValue = { state.settings.textColor ?: Color((theme.value ?: theme.effectiveValue).contentColor) }, + getIsEffective = { layout == EpubLayout.REFLOWABLE && preferences.textColor != null }, updateValue = { value -> updateValues { it.copy(textColor = value) } } ) val textNormalization: Preference = PreferenceDelegate( getValue = { preferences.textNormalization }, - getEffectiveValue = { state.settings.textNormalization }, + getEffectiveValue = { state.settings.textNormalization }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, updateValue = { value -> updateValues { it.copy(textNormalization = value) } } ) @@ -270,8 +254,8 @@ class EpubPreferencesEditor internal constructor( val typeScale: RangePreference = RangePreferenceDelegate( getValue = { preferences.typeScale }, - getEffectiveValue = { state.settings.typeScale }, - getIsEffective = { layout == EpubLayout.REFLOWABLE && !state.settings.publisherStyles }, + getEffectiveValue = { state.settings.typeScale ?: 1.2 }, + getIsEffective = { layout == EpubLayout.REFLOWABLE && !state.settings.publisherStyles && preferences.typeScale != null }, updateValue = { value -> updateValues { it.copy(typeScale = value) } }, valueFormatter = { it.format(5) }, supportedRange = 1.0..2.0, @@ -289,8 +273,8 @@ class EpubPreferencesEditor internal constructor( val wordSpacing: RangePreference = RangePreferenceDelegate( getValue = { preferences.wordSpacing }, - getEffectiveValue = { state.settings.wordSpacing }, - getIsEffective = { isWordSpacingEffective() }, + getEffectiveValue = { state.settings.wordSpacing ?: 0.0 }, + getIsEffective = ::isWordSpacingEffective, updateValue = { value -> updateValues { it.copy(wordSpacing = value) } }, supportedRange = 0.0..1.0, progressionStrategy = DoubleIncrement(0.1), @@ -318,37 +302,31 @@ class EpubPreferencesEditor internal constructor( private fun isHyphensEffective() = layout == EpubLayout.REFLOWABLE && state.layout.stylesheets == Layout.Stylesheets.Default && - !state.settings.publisherStyles + !state.settings.publisherStyles && + preferences.hyphens != null - private fun isLetterSpacing() = layout == EpubLayout.REFLOWABLE && + private fun isLetterSpacingEffective() = layout == EpubLayout.REFLOWABLE && state.layout.stylesheets == Layout.Stylesheets.Default && - !state.settings.publisherStyles + !state.settings.publisherStyles && + preferences.letterSpacing != null - private fun isLigaturesSpacing() = layout == EpubLayout.REFLOWABLE && + private fun isLigaturesEffective() = layout == EpubLayout.REFLOWABLE && state.layout.stylesheets == Layout.Stylesheets.Rtl && - !state.settings.publisherStyles + !state.settings.publisherStyles && + preferences.ligatures != null private fun isParagraphIndentEffective() = layout == EpubLayout.REFLOWABLE && state.layout.stylesheets in listOf(Layout.Stylesheets.Default, Layout.Stylesheets.Rtl) && - !state.settings.publisherStyles + !state.settings.publisherStyles && + preferences.paragraphIndent != null private fun isTextAlignEffective() = layout == EpubLayout.REFLOWABLE && state.layout.stylesheets in listOf(Layout.Stylesheets.Default, Layout.Stylesheets.Rtl) && - !state.settings.publisherStyles + !state.settings.publisherStyles && + preferences.textAlign != null private fun isWordSpacingEffective(): Boolean = layout == EpubLayout.REFLOWABLE && state.layout.stylesheets == Layout.Stylesheets.Default && - !state.settings.publisherStyles - - companion object { - - private val DEFAULT_FONT_FAMILIES: List = listOf( - FontFamily.SERIF, - FontFamily.SANS_SERIF, - FontFamily.MONOSPACE, - FontFamily.ACCESSIBLE_DFA, - FontFamily.IA_WRITER_DUOSPACE, - FontFamily.OPEN_DYSLEXIC - ) - } + !state.settings.publisherStyles && + preferences.wordSpacing != null } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt index 8ec41c1ba0..4f426a729f 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt @@ -6,12 +6,26 @@ package org.readium.r2.navigator.epub +import org.readium.r2.navigator.epub.css.Appearance +import org.readium.r2.navigator.epub.css.ColCount import org.readium.r2.navigator.epub.css.Color as CssColor +import org.readium.r2.navigator.epub.css.FontWeight +import org.readium.r2.navigator.epub.css.Hyphens +import org.readium.r2.navigator.epub.css.Layout +import org.readium.r2.navigator.epub.css.Length +import org.readium.r2.navigator.epub.css.Ligatures +import org.readium.r2.navigator.epub.css.ReadiumCss import org.readium.r2.navigator.epub.css.TextAlign as CssTextAlign -import org.readium.r2.navigator.epub.css.* -import org.readium.r2.navigator.preferences.* +import org.readium.r2.navigator.epub.css.View import org.readium.r2.navigator.preferences.Color +import org.readium.r2.navigator.preferences.ColumnCount +import org.readium.r2.navigator.preferences.Configurable +import org.readium.r2.navigator.preferences.FontFamily +import org.readium.r2.navigator.preferences.ImageFilter +import org.readium.r2.navigator.preferences.ReadingProgression +import org.readium.r2.navigator.preferences.Spread import org.readium.r2.navigator.preferences.TextAlign +import org.readium.r2.navigator.preferences.Theme import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.util.Either import org.readium.r2.shared.util.Language @@ -23,31 +37,31 @@ import org.readium.r2.shared.util.Language */ @ExperimentalReadiumApi data class EpubSettings( - val backgroundColor: Color, + val backgroundColor: Color?, val columnCount: ColumnCount, val fontFamily: FontFamily?, - val fontSize: Double, + val fontSize: Double?, val fontWeight: Double?, - val hyphens: Boolean, + val hyphens: Boolean?, val imageFilter: ImageFilter, val language: Language?, - val letterSpacing: Double, - val ligatures: Boolean, - val lineHeight: Double, + val letterSpacing: Double?, + val ligatures: Boolean?, + val lineHeight: Double?, val pageMargins: Double, - val paragraphIndent: Double, - val paragraphSpacing: Double, + val paragraphIndent: Double?, + val paragraphSpacing: Double?, val publisherStyles: Boolean, val readingProgression: ReadingProgression, val scroll: Boolean, val spread: Spread, - val textAlign: TextAlign, - val textColor: Color, + val textAlign: TextAlign?, + val textColor: Color?, val textNormalization: Boolean, val theme: Theme, - val typeScale: Double, + val typeScale: Double?, val verticalText: Boolean, - val wordSpacing: Double + val wordSpacing: Double? ) : Configurable.Settings @OptIn(ExperimentalReadiumApi::class) @@ -83,14 +97,13 @@ internal fun ReadiumCss.update(settings: EpubSettings): ReadiumCss { }, darkenImages = imageFilter == ImageFilter.DARKEN, invertImages = imageFilter == ImageFilter.INVERT, - textColor = textColor.toCss(), - backgroundColor = backgroundColor.toCss(), + textColor = textColor?.toCss(), + backgroundColor = backgroundColor?.toCss(), fontOverride = (fontFamily != null || textNormalization), fontFamily = fontFamily?.toCss(), // Font size is handled natively with WebSettings.textZoom. // See https://github.com/readium/mobile/issues/1#issuecomment-652431984 -// fontSize = fontSize.value -// ?.let { Length.Percent(it) }, +// fontSize = fontSize?.let { Length.Percent(it) }, advancedSettings = !publisherStyles, typeScale = typeScale, textAlign = when (textAlign) { @@ -98,14 +111,15 @@ internal fun ReadiumCss.update(settings: EpubSettings): ReadiumCss { TextAlign.LEFT -> CssTextAlign.LEFT TextAlign.RIGHT -> CssTextAlign.RIGHT TextAlign.START, TextAlign.CENTER, TextAlign.END -> CssTextAlign.START + null -> null }, - lineHeight = Either(lineHeight), - paraSpacing = Length.Rem(paragraphSpacing), - paraIndent = Length.Rem(paragraphIndent), - wordSpacing = Length.Rem(wordSpacing), - letterSpacing = Length.Rem(letterSpacing / 2), - bodyHyphens = if (hyphens) Hyphens.AUTO else Hyphens.NONE, - ligatures = if (ligatures) Ligatures.COMMON else Ligatures.NONE, + lineHeight = lineHeight?.let { Either(it) }, + paraSpacing = paragraphSpacing?.let { Length.Rem(it) }, + paraIndent = paragraphIndent?.let { Length.Rem(it) }, + wordSpacing = wordSpacing?.let { Length.Rem(it) }, + letterSpacing = letterSpacing?.let { Length.Rem(it / 2) }, + bodyHyphens = hyphens?.let { if (it) Hyphens.AUTO else Hyphens.NONE }, + ligatures = ligatures?.let { if (it) Ligatures.COMMON else Ligatures.NONE }, a11yNormalize = textNormalization, overrides = mapOf( "font-weight" to diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt index e5fbe6d30a..b1aae60f65 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt @@ -27,35 +27,33 @@ internal class EpubSettingsResolver( val verticalText = resolveVerticalText(verticalPref, language, readingProgression) val theme = preferences.theme ?: Theme.LIGHT - val backgroundColor = preferences.backgroundColor ?: Color(theme.backgroundColor) - val textColor = preferences.textColor ?: Color(theme.contentColor) return EpubSettings( - backgroundColor = backgroundColor, + backgroundColor = preferences.backgroundColor, columnCount = preferences.columnCount ?: defaults.columnCount ?: ColumnCount.AUTO, fontFamily = preferences.fontFamily, - fontSize = preferences.fontSize ?: defaults.fontSize ?: 1.0, + fontSize = preferences.fontSize ?: defaults.fontSize, fontWeight = preferences.fontWeight ?: defaults.fontWeight, - hyphens = preferences.hyphens ?: defaults.hyphens ?: true, + hyphens = preferences.hyphens ?: defaults.hyphens, imageFilter = preferences.imageFilter ?: defaults.imageFilter ?: ImageFilter.NONE, language = language, - letterSpacing = preferences.letterSpacing ?: defaults.letterSpacing ?: 0.0, - ligatures = preferences.ligatures ?: defaults.ligatures ?: true, - lineHeight = preferences.lineHeight ?: defaults.lineHeight ?: 1.2, + letterSpacing = preferences.letterSpacing ?: defaults.letterSpacing, + ligatures = preferences.ligatures ?: defaults.ligatures, + lineHeight = preferences.lineHeight ?: defaults.lineHeight, pageMargins = preferences.pageMargins ?: defaults.pageMargins ?: 1.0, - paragraphIndent = preferences.paragraphIndent ?: defaults.paragraphIndent ?: 0.0, - paragraphSpacing = preferences.paragraphSpacing ?: defaults.paragraphSpacing ?: 0.0, + paragraphIndent = preferences.paragraphIndent ?: defaults.paragraphIndent, + paragraphSpacing = preferences.paragraphSpacing ?: defaults.paragraphSpacing, publisherStyles = preferences.publisherStyles ?: defaults.publisherStyles ?: true, readingProgression = readingProgression, scroll = preferences.scroll ?: defaults.scroll ?: false, spread = preferences.spread ?: defaults.spread ?: Spread.NEVER, - textAlign = preferences.textAlign ?: defaults.textAlign ?: TextAlign.START, - textColor = textColor, + textAlign = preferences.textAlign ?: defaults.textAlign, + textColor = preferences.textColor, textNormalization = preferences.textNormalization ?: defaults.textNormalization ?: false, theme = theme, - typeScale = preferences.typeScale ?: defaults.typeScale ?: 1.2, + typeScale = preferences.typeScale ?: defaults.typeScale, verticalText = verticalText, - wordSpacing = preferences.wordSpacing ?: defaults.wordSpacing ?: 0.0, + wordSpacing = preferences.wordSpacing ?: defaults.wordSpacing, ) } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index c41b8e50cf..0e9ad4e906 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -281,7 +281,7 @@ private fun ColumnScope.ReflowableUserPreferences( publisherStyles: Preference? = null, readingProgression: EnumPreference? = null, scroll: Preference? = null, - textAlign: EnumPreference? = null, + textAlign: EnumPreference? = null, textColor: Preference? = null, textNormalization: Preference? = null, theme: EnumPreference? = null, @@ -478,87 +478,90 @@ private fun ColumnScope.ReflowableUserPreferences( preference = publisherStyles, commit = commit, ) - } - if (textAlign != null) { - ButtonGroupItem( - title = "Alignment", - preference = textAlign, - commit = commit - ) { value -> - when (value) { - ReadiumTextAlign.CENTER -> "Center" - ReadiumTextAlign.JUSTIFY -> "Justify" - ReadiumTextAlign.START -> "Start" - ReadiumTextAlign.END -> "End" - ReadiumTextAlign.LEFT -> "Left" - ReadiumTextAlign.RIGHT -> "Right" + if (!(publisherStyles.value ?: publisherStyles.effectiveValue)) { + if (textAlign != null) { + ButtonGroupItem( + title = "Alignment", + preference = textAlign, + commit = commit + ) { value -> + when (value) { + ReadiumTextAlign.CENTER -> "Center" + ReadiumTextAlign.JUSTIFY -> "Justify" + ReadiumTextAlign.START -> "Start" + ReadiumTextAlign.END -> "End" + ReadiumTextAlign.LEFT -> "Left" + ReadiumTextAlign.RIGHT -> "Right" + null -> "Default" + } + } } - } - } - if (typeScale != null) { - StepperItem( - title = "Type scale", - preference = typeScale, - commit = commit - ) - } + if (typeScale != null) { + StepperItem( + title = "Type scale", + preference = typeScale, + commit = commit + ) + } - if (lineHeight != null) { - StepperItem( - title = "Line height", - preference = lineHeight, - commit = commit - ) - } + if (lineHeight != null) { + StepperItem( + title = "Line height", + preference = lineHeight, + commit = commit + ) + } - if (paragraphIndent != null) { - StepperItem( - title = "Paragraph indent", - preference = paragraphIndent, - commit = commit - ) - } + if (paragraphIndent != null) { + StepperItem( + title = "Paragraph indent", + preference = paragraphIndent, + commit = commit + ) + } - if (paragraphSpacing != null) { - StepperItem( - title = "Paragraph spacing", - preference = paragraphSpacing, - commit = commit - ) - } + if (paragraphSpacing != null) { + StepperItem( + title = "Paragraph spacing", + preference = paragraphSpacing, + commit = commit + ) + } - if (wordSpacing != null) { - StepperItem( - title = "Word spacing", - preference = wordSpacing, - commit = commit - ) - } + if (wordSpacing != null) { + StepperItem( + title = "Word spacing", + preference = wordSpacing, + commit = commit + ) + } - if (letterSpacing != null) { - StepperItem( - title = "Letter spacing", - preference = letterSpacing, - commit = commit - ) - } + if (letterSpacing != null) { + StepperItem( + title = "Letter spacing", + preference = letterSpacing, + commit = commit + ) + } - if (hyphens != null) { - SwitchItem( - title = "Hyphens", - preference = hyphens, - commit = commit - ) - } + if (hyphens != null) { + SwitchItem( + title = "Hyphens", + preference = hyphens, + commit = commit + ) + } - if (ligatures != null) { - SwitchItem( - title = "Ligatures", - preference = ligatures, - commit = commit - ) + if (ligatures != null) { + SwitchItem( + title = "Ligatures", + preference = ligatures, + commit = commit + ) + } + } } } From 0584b07cfa33ab43b52ef56b4dcd1926f7d3fabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 12 Dec 2022 19:00:28 +0100 Subject: [PATCH 21/45] Add per setting clear button --- .../reader/preferences/UserPreferences.kt | 122 +++++++++--------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index 0e9ad4e906..ee1cc27478 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -12,9 +12,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Palette -import androidx.compose.material.icons.filled.Remove import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -29,6 +26,7 @@ import org.readium.r2.navigator.epub.EpubPreferencesEditor import org.readium.r2.navigator.preferences.* import org.readium.r2.navigator.preferences.Color as ReadiumColor import org.readium.r2.navigator.preferences.TextAlign as ReadiumTextAlign +import androidx.compose.material.icons.filled.* import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.util.Language @@ -174,19 +172,6 @@ private fun ColumnScope.FixedLayoutUserPreferences( ) } - // The language preferences are specific to a publication. This button resets only the - // language preferences to the publication's default metadata for convenience. - Row( - modifier = Modifier - .padding(16.dp) - .align(Alignment.End), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - Button(onClick = ::reset) { - Text("Reset to publication", style = MaterialTheme.typography.caption) - } - } - Divider() } @@ -321,19 +306,6 @@ private fun ColumnScope.ReflowableUserPreferences( ) } - // The language settings are specific to a publication. This button resets only the - // language preferences to the publication's default metadata for convenience. - Row( - modifier = Modifier - .padding(16.dp) - .align(Alignment.End), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - Button(onClick = ::reset) { - Text("Reset to publication", style = MaterialTheme.typography.caption) - } - } - Divider() } @@ -582,15 +554,18 @@ private fun ButtonGroupItem( isActive = preference.isEffective, activeOption = preference.effectiveValue, selectedOption = preference.value, - formatValue = formatValue - ) { newValue -> - if (newValue == preference.value) { - preference.clear() - } else { - preference.set(newValue) - } - commit() - } + formatValue = formatValue, + onClear = { preference.clear(); commit() } + .takeIf { preference.value != null }, + onSelectedOptionChanged = { newValue -> + if (newValue == preference.value) { + preference.clear() + } else { + preference.set(newValue) + } + commit() + } + ) } /** @@ -604,9 +579,10 @@ private fun ButtonGroupItem( activeOption: T, selectedOption: T?, formatValue: (T) -> String, + onClear: (() -> Unit)?, onSelectedOptionChanged: (T) -> Unit, ) { - Item(title, isActive = isActive) { + Item(title, isActive = isActive, onClear = onClear) { ToggleButtonGroup( options = options, activeOption = activeOption, @@ -636,11 +612,14 @@ private fun MenuItem( value = preference.value ?: preference.effectiveValue, values = listOf(null) + preference.supportedValues, isActive = preference.isEffective, - formatValue = formatValue - ) { value -> - preference.set(value) - commit() - } + formatValue = formatValue, + onClear = { preference.clear(); commit() } + .takeIf { preference.value != null }, + onValueChanged = { value -> + preference.set(value) + commit() + } + ) } /** @@ -654,8 +633,9 @@ private fun MenuItem( isActive: Boolean, formatValue: (T) -> String, onValueChanged: (T) -> Unit, + onClear: (() -> Unit)? ) { - Item(title, isActive = isActive) { + Item(title, isActive = isActive, onClear = onClear) { DropdownMenuButton( text = { Text( @@ -693,7 +673,9 @@ private fun > StepperItem( value = preference.value ?: preference.effectiveValue, formatValue = preference::formatValue, onDecrement = { preference.decrement(); commit() }, - onIncrement = { preference.increment(); commit() } + onIncrement = { preference.increment(); commit() }, + onClear = { preference.clear(); commit() } + .takeIf { preference.value != null }, ) } @@ -708,8 +690,9 @@ private fun StepperItem( formatValue: (T) -> String, onDecrement: () -> Unit, onIncrement: () -> Unit, + onClear: (() -> Unit)? ) { - Item(title, isActive = isActive) { + Item(title, isActive = isActive, onClear = onClear) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp) @@ -751,7 +734,9 @@ private fun SwitchItem( value = preference.value ?: preference.effectiveValue, isActive = preference.isEffective, onCheckedChange = { preference.set(it); commit() }, - onToggle = { preference.toggle(); commit() } + onToggle = { preference.toggle(); commit() }, + onClear = { preference.clear(); commit() } + .takeIf { preference.value != null }, ) } @@ -765,11 +750,13 @@ private fun SwitchItem( isActive: Boolean, onCheckedChange: (Boolean) -> Unit, onToggle: () -> Unit, + onClear: (() -> Unit)? ) { Item( title = title, isActive = isActive, - onClick = onToggle + onClick = onToggle, + onClear = onClear ) { Switch( checked = value, @@ -792,7 +779,9 @@ private fun ColorItem( isActive = preference.isEffective, value = preference.value ?: preference.effectiveValue, noValueSelected = preference.value == null, - onColorChanged = { preference.set(it); commit() } + onColorChanged = { preference.set(it); commit() }, + onClear = { preference.clear(); commit() } + .takeIf { preference.value != null } ) } @@ -805,14 +794,16 @@ private fun ColorItem( isActive: Boolean, value: ReadiumColor, noValueSelected: Boolean, - onColorChanged: (ReadiumColor?) -> Unit + onColorChanged: (ReadiumColor?) -> Unit, + onClear: (() -> Unit)? ) { var isPicking by remember { mutableStateOf(false) } Item( title = title, isActive = isActive, - onClick = { isPicking = true } + onClick = { isPicking = true }, + onClear = onClear ) { val color = Color(value.int) @@ -874,11 +865,14 @@ fun LanguageItem( isActive = preference.isEffective, value = preference.value ?: preference.effectiveValue, values = languages, - formatValue = { it?.locale?.displayName ?: "Unknown" } - ) { value -> - preference.set(value) - commit() - } + formatValue = { it?.locale?.displayName ?: "Unknown" }, + onClear = { preference.clear(); commit() } + .takeIf { preference.value != null }, + onValueChanged = { value -> + preference.set(value) + commit() + } + ) } @OptIn(ExperimentalMaterialApi::class) @@ -887,6 +881,7 @@ private fun Item( title: String, isActive: Boolean = true, onClick: (() -> Unit)? = null, + onClear: (() -> Unit)? = null, content: @Composable () -> Unit ) { ListItem( @@ -899,7 +894,18 @@ private fun Item( Text(title) } }, - trailing = content + trailing = { + Row { + content() + + IconButton(onClick = onClear ?: {}, enabled = onClear != null) { + Icon( + Icons.Default.Backspace, + contentDescription = "Clear" + ) + } + } + } ) } From 64c131b14557ee57cad42616c4842136b5df66ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 12 Dec 2022 19:13:14 +0100 Subject: [PATCH 22/45] Fix `StepsProgression.decrement()` --- .../org/readium/r2/navigator/preferences/ProgressionStrategy.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt index 3de890050f..acb622952f 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt @@ -31,7 +31,7 @@ class StepsProgression>(private val steps: List) : Progress } override fun decrement(value: T): T { - val index = steps.indexOfLast { it >= value }.takeIf { it != -1 } ?: return value + val index = steps.indexOfFirst { it >= value }.takeIf { it != -1 } ?: return value return steps.getOrNull(index - 1) ?: value } } From a123252e747460410da55e718b1df67a428650a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 14 Dec 2022 10:55:55 +0100 Subject: [PATCH 23/45] Make `spread` a publication preference --- .../pspdfkit/navigator/PsPdfKitPreferencesFilters.kt | 6 ++++-- .../org/readium/r2/navigator/epub/EpubPreferencesFilters.kt | 6 ++++-- .../org/readium/r2/navigator/preferences/Configurable.kt | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesFilters.kt b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesFilters.kt index 5a49d0d692..28d01056e4 100644 --- a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesFilters.kt +++ b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesFilters.kt @@ -18,7 +18,8 @@ object PsPdfKitSharedPreferencesFilter : PreferencesFilter override fun filter(preferences: PsPdfKitPreferences): PsPdfKitPreferences = preferences.copy( readingProgression = null, - offsetFirstPage = null + offsetFirstPage = null, + spread = null, ) } @@ -31,6 +32,7 @@ object PsPdfKitPublicationPreferencesFilter : PreferencesFilter { preferences.copy( readingProgression = null, language = null, - verticalText = null + spread = null, + verticalText = null, ) } @@ -33,6 +34,7 @@ object EpubPublicationPreferencesFilter : PreferencesFilter { EpubPreferences( readingProgression = preferences.readingProgression, language = preferences.language, - verticalText = preferences.verticalText + spread = preferences.spread, + verticalText = preferences.verticalText, ) } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt index 6bb65e7421..62f1ffb176 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Configurable.kt @@ -27,7 +27,7 @@ interface Configurable> { */ interface Preferences

> { -/** + /** * Creates a new instance of [P] after merging the values of [other]. * * In case of conflict, [other] takes precedence. From 1108cf8fa9d65ad7dbc4cd48a4cde2eed87d6875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 14 Dec 2022 15:21:42 +0100 Subject: [PATCH 24/45] Fix floating points comparisons with `StepsProgression` --- .../r2/navigator/epub/fxl/R2FXLLayout.kt | 13 ++++++----- .../preferences/ProgressionStrategy.kt | 22 +++++++++++++++---- .../org/readium/r2/shared/extensions/Float.kt | 18 +++++++++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLLayout.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLLayout.kt index 29b09d2f0d..48f1ed9c64 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLLayout.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLLayout.kt @@ -16,16 +16,20 @@ import android.graphics.Matrix import android.graphics.PointF import android.graphics.RectF import android.util.AttributeSet -import android.view.* +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.ScaleGestureDetector +import android.view.View +import android.view.ViewTreeObserver import android.view.animation.DecelerateInterpolator import android.view.animation.Interpolator import android.widget.FrameLayout import androidx.core.view.ViewCompat -import java.util.* -import kotlin.math.abs +import java.util.Locale import kotlin.math.min import kotlin.math.roundToInt import kotlin.math.roundToLong +import org.readium.r2.shared.extensions.equalsDelta class R2FXLLayout : FrameLayout { @@ -94,9 +98,6 @@ class R2FXLLayout : FrameLayout { private var mOnDoubleTapListeners: MutableList? = null private var onLongTapListeners: MutableList? = null - private fun Float.equalsDelta(other: Float, delta: Float = 0.001f) = - this == other || abs(this - other) < delta - var scale: Float get() = getMatrixValue(scaleMatrix, Matrix.MSCALE_X) set(scale) = setScale(scale, true) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt index acb622952f..323f3f2a28 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt @@ -6,6 +6,9 @@ package org.readium.r2.navigator.preferences +import kotlin.math.abs +import org.readium.r2.shared.extensions.equalsDelta + /** * A strategy to increment or decrement a setting. */ @@ -20,18 +23,29 @@ interface ProgressionStrategy { * Progression strategy based on a list of preferred values for the setting. * * Steps MUST be sorted in increasing order. + * + * @param equalsDelta Provide an equality algorithm to compare floating point numbers. */ -class StepsProgression>(private val steps: List) : ProgressionStrategy { +class StepsProgression>(private val steps: List, private val equalsDelta: (T, T) -> Boolean) : ProgressionStrategy { + + companion object { + operator fun invoke(vararg steps: Int): StepsProgression = + StepsProgression(steps = steps.toList(), equalsDelta = Int::equals) - constructor(vararg steps: T) : this(steps.toList()) + operator fun invoke(vararg steps: Float): StepsProgression = + StepsProgression(steps = steps.toList(), equalsDelta = { a, b -> a.equalsDelta(b) }) + + operator fun invoke(vararg steps: Double): StepsProgression = + StepsProgression(steps = steps.toList(), equalsDelta = { a, b -> a.equalsDelta(b) }) + } override fun increment(value: T): T { - val index = steps.indexOfLast { it <= value }.takeIf { it != -1 } ?: return value + val index = steps.indexOfLast { it < value || equalsDelta(it, value) }.takeIf { it != -1 } ?: return value return steps.getOrNull(index + 1) ?: value } override fun decrement(value: T): T { - val index = steps.indexOfFirst { it >= value }.takeIf { it != -1 } ?: return value + val index = steps.indexOfFirst { it > value || equalsDelta(it, value) }.takeIf { it != -1 } ?: return value return steps.getOrNull(index - 1) ?: value } } diff --git a/readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt b/readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt new file mode 100644 index 0000000000..d5101fd4bc --- /dev/null +++ b/readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2022 Readium Foundation. All rights reserved. + * Use of this source code is governed by the BSD-style license + * available in the top-level LICENSE file of the project. + */ + +package org.readium.r2.shared.extensions + +import kotlin.math.abs +import org.readium.r2.shared.InternalReadiumApi + +@InternalReadiumApi +fun Float.equalsDelta(other: Float, delta: Float = 0.001f) = + this == other || abs(this - other) < delta + +@InternalReadiumApi +fun Double.equalsDelta(other: Double, delta: Double = 0.001) = + this == other || abs(this - other) < delta \ No newline at end of file From 1c312034a8fa612dea44d60152bb579d62b21ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 14 Dec 2022 15:37:16 +0100 Subject: [PATCH 25/45] Fix applying the background color to the resource pager --- .../navigator/epub/EpubNavigatorFragment.kt | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index 09b92148fe..2b78dfb09a 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -6,8 +6,8 @@ package org.readium.r2.navigator.epub +import org.readium.r2.shared.publication.ReadingProgression as PublicationReadingProgression import android.content.SharedPreferences -import android.graphics.Color as AndroidColor import android.graphics.PointF import android.graphics.RectF import android.os.Bundle @@ -77,7 +77,6 @@ import org.readium.r2.shared.fetcher.Resource import org.readium.r2.shared.publication.Link import org.readium.r2.shared.publication.Locator import org.readium.r2.shared.publication.Publication -import org.readium.r2.shared.publication.ReadingProgression as PublicationReadingProgression import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.publication.presentation.presentation import org.readium.r2.shared.publication.services.isRestricted @@ -389,10 +388,7 @@ class EpubNavigatorFragment internal constructor( EpubLayout.REFLOWABLE, null -> Publication.TYPE.EPUB EpubLayout.FIXED -> Publication.TYPE.FXL } - resourcePager.setBackgroundColor( - viewModel.settings.value.backgroundColor?.int - ?: AndroidColor.TRANSPARENT - ) + resourcePager.setBackgroundColor(viewModel.settings.value.effectiveBackgroundColor) parent.addView(resourcePager) @@ -485,11 +481,8 @@ class EpubNavigatorFragment internal constructor( if (previous.fontSize != new.fontSize) { r2PagerAdapter?.setFontSize(new.fontSize ?: 1.0) } - if (previous.backgroundColor != new.backgroundColor) { - resourcePager.setBackgroundColor( - new.backgroundColor?.int - ?: AndroidColor.TRANSPARENT - ) + if (previous.effectiveBackgroundColor != new.effectiveBackgroundColor) { + resourcePager.setBackgroundColor(new.effectiveBackgroundColor) } } @@ -1012,3 +1005,7 @@ class EpubNavigatorFragment internal constructor( WebViewServer.assetUrl(path) } } + +@ExperimentalReadiumApi +private val EpubSettings.effectiveBackgroundColor: Int get() = + backgroundColor?.int ?: theme.backgroundColor \ No newline at end of file From 9f13b9e6fdcd5b975976618f313b237c5e97cd31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 15 Dec 2022 11:11:00 +0100 Subject: [PATCH 26/45] Disable edge taps in scroll mode --- .../src/main/java/org/readium/r2/navigator/R2BasicWebView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt index 9ddf2629e6..8880b9cd75 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt @@ -260,11 +260,11 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte // FIXME: Call listener.onTap if scrollLeft|Right fails return when { - thresholdRange.contains(event.point.x) -> { + !scrollMode && thresholdRange.contains(event.point.x) -> { scrollLeft(false) true } - thresholdRange.contains(clientWidth - event.point.x) -> { + !scrollMode && thresholdRange.contains(clientWidth - event.point.x) -> { scrollRight(false) true } From b0234b9fc40f7b0d07edb9f5ceb132cf61778690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 15 Dec 2022 14:16:33 +0100 Subject: [PATCH 27/45] Fix memory leak when invalidating the resource pager --- .../java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index 2b78dfb09a..4e9a95dec0 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -380,6 +380,8 @@ class EpubNavigatorFragment internal constructor( val parent = requireNotNull(resourcePager.parent as? ConstraintLayout) { "The parent view of the EPUB `resourcePager` must be a ConstraintLayout" } + // We need to null out the adapter explicitly, otherwise the page fragments will leak. + resourcePager.adapter = null parent.removeView(resourcePager) resourcePager = R2ViewPager(requireContext()) From 2e32a6bf2f213ebe67daf00346d24bf74a130e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 19 Dec 2022 15:23:19 +0100 Subject: [PATCH 28/45] Disable edge tap navigation when using the new settings API --- docs/migration-guide.md | 15 +++++++++++++++ .../org/readium/r2/navigator/R2BasicWebView.kt | 7 ++++--- .../java/org/readium/r2/navigator/R2WebView.kt | 2 -- .../r2/navigator/pager/R2EpubPageFragment.kt | 5 ++--- .../r2/navigator/pager/R2FXLPageFragment.kt | 5 +++++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 3d40ce1dc5..143262e92a 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -86,6 +86,21 @@ Then, use the base URL `https://readium/assets/` to fetch your app assets from t `https://readium/assets/annotation-icon.svg` +#### Edge tap navigation + +After removing the HTTP server, tapping on the edge of the screen will not turn pages anymore. If you wish to keep this behavior, you can add it in your app by implementing `VisualNavigator.Listener.onTap()`. An instance of `EdgeTapNavigation` can help to compute the page turns by taking into account the publication reading progression and custom thresholds. [See an example in the test app](https://github.com/readium/kotlin-toolkit/blob/9b84fc42c3db34c0462e972b6a08afc0de3afc95/test-app/src/main/java/org/readium/r2/testapp/reader/VisualReaderFragment.kt#L515-L522). + +```kotlin +override fun onTap(point: PointF): Boolean { + val navigated = EdgeTapNavigation(navigator).onTap(point, requireView()) + if (!navigated) { + toggleAppBar() + } + return true +} +``` + + ### Upgrading to the new Preferences API The 2.3.0 release introduces a brand new user preferences API to configure the EPUB Navigator. This new API is easier and safer to use, [take a look at the user guide](guides/navigator-settings.md) to learn how to integrate it in your app. diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt index 8880b9cd75..99206dc06f 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt @@ -88,6 +88,7 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte lateinit var listener: Listener internal var preferences: SharedPreferences? = null + internal var useLegacySettings: Boolean = false var resourceUrl: String? = null @@ -246,7 +247,7 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte return false } - // FIXME: Let the app handle edge taps and footnotes. + // FIXME: Let the app handle footnotes. // We ignore taps on interactive element, unless it's an element we handle ourselves such as // pop-up footnotes. @@ -260,11 +261,11 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte // FIXME: Call listener.onTap if scrollLeft|Right fails return when { - !scrollMode && thresholdRange.contains(event.point.x) -> { + useLegacySettings && thresholdRange.contains(event.point.x) -> { scrollLeft(false) true } - !scrollMode && thresholdRange.contains(clientWidth - event.point.x) -> { + useLegacySettings && thresholdRange.contains(clientWidth - event.point.x) -> { scrollRight(false) true } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/R2WebView.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/R2WebView.kt index a4aa642e2a..8b7af563d0 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/R2WebView.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/R2WebView.kt @@ -186,8 +186,6 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, private var mScrollState = SCROLL_STATE_IDLE - internal var useLegacySettings = false - private fun initWebPager() { setWillNotDraw(false) descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt index 5f7689d7ed..03f4d78237 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt @@ -23,7 +23,7 @@ import android.webkit.WebResourceResponse import android.webkit.WebView import androidx.core.view.ViewCompat import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.webkit.WebViewClientCompat @@ -62,7 +62,7 @@ class R2EpubPageFragment : Fragment() { private lateinit var containerView: View private lateinit var preferences: SharedPreferences - private lateinit var viewModel: EpubNavigatorViewModel + private val viewModel: EpubNavigatorViewModel by viewModels(ownerProducer = { requireParentFragment() }) private var _binding: ViewpagerFragmentEpubBinding? = null private val binding get() = _binding!! @@ -130,7 +130,6 @@ class R2EpubPageFragment : Fragment() { _binding = ViewpagerFragmentEpubBinding.inflate(inflater, container, false) containerView = binding.root preferences = activity?.getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE)!! - viewModel = ViewModelProvider(requireParentFragment()).get(EpubNavigatorViewModel::class.java) val webView = binding.webView this.webView = webView diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt index 496323f909..d037936fd8 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt @@ -19,11 +19,13 @@ import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.webkit.WebViewClientCompat import org.readium.r2.navigator.R2BasicWebView import org.readium.r2.navigator.databinding.FragmentFxllayoutDoubleBinding import org.readium.r2.navigator.databinding.FragmentFxllayoutSingleBinding import org.readium.r2.navigator.epub.EpubNavigatorFragment +import org.readium.r2.navigator.epub.EpubNavigatorViewModel import org.readium.r2.navigator.epub.fxl.R2FXLLayout import org.readium.r2.navigator.epub.fxl.R2FXLOnDoubleTapListener @@ -46,6 +48,8 @@ class R2FXLPageFragment : Fragment() { private val navigator: EpubNavigatorFragment? get() = parentFragment as? EpubNavigatorFragment + private val viewModel: EpubNavigatorViewModel by viewModels(ownerProducer = { requireParentFragment() }) + @SuppressLint("SetJavaScriptEnabled") override fun onCreateView( inflater: LayoutInflater, @@ -123,6 +127,7 @@ class R2FXLPageFragment : Fragment() { webView.listener = it.webViewListener } + webView.useLegacySettings = viewModel.useLegacySettings webView.settings.javaScriptEnabled = true webView.isVerticalScrollBarEnabled = false webView.isHorizontalScrollBarEnabled = false From 44070ec8978331bcc2388427b517942617135b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Menu?= Date: Mon, 19 Dec 2022 18:40:06 +0100 Subject: [PATCH 29/45] Apply suggestions from code review Co-authored-by: qnga <32197639+qnga@users.noreply.github.com> --- .../adapters/pdfium/navigator/PdfiumEngineProvider.kt | 5 ++--- .../adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt | 5 ++--- .../org/readium/r2/navigator/preferences/MappedPreference.kt | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt index 33cbcb1509..9f2d1c5e1d 100644 --- a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt +++ b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumEngineProvider.kt @@ -17,9 +17,8 @@ import org.readium.r2.shared.publication.Publication /** * Main component to use the PDF navigator with the PDFium adapter. * - * Provide [PdfiumDefaults] to customize the default values that will be used by the navigator for - * some preferences and the way that preferences will be allowed to be modified through preferences - * editors created by this component. + * Provide [PdfiumDefaults] to customize the default values that will be used by + * the navigator for some preferences. */ @ExperimentalReadiumApi class PdfiumEngineProvider( diff --git a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt index 30784410fe..5fbc8d8f5c 100644 --- a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt +++ b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitEngineProvider.kt @@ -22,9 +22,8 @@ import org.readium.r2.shared.util.pdf.cachedIn /** * Main component to use the PDF navigator with PSPDFKit. * - * Provide [PsPdfKitDefaults] to customize the default values that will be used by the navigator for - * some preferences and the way that preferences will be allowed to be modified through preferences - * editors created by this component. + * Provide [PsPdfKitDefaults] to customize the default values that will be used by + * the navigator for some preferences. */ @ExperimentalReadiumApi class PsPdfKitEngineProvider( diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt index 6b33ed9c59..9613195521 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt @@ -93,8 +93,8 @@ fun > RangePreference.map( */ @ExperimentalReadiumApi fun > RangePreference.withSupportedRange( - range: ClosedRange, - progressionStrategy: ProgressionStrategy + range: ClosedRange = range, + progressionStrategy: ProgressionStrategy = progressionStrategy) ): RangePreference = map( supportedRange = { range }, From 8b7fba3deadb9e5700ff60857a86d4a03ec13353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 19 Dec 2022 18:47:00 +0100 Subject: [PATCH 30/45] Add vararg variants --- .../r2/navigator/epub/EpubNavigatorFragment.kt | 4 ++-- .../navigator/preferences/MappedPreference.kt | 18 ++++++++++++++++-- .../preferences/ProgressionStrategy.kt | 6 ++++-- .../org/readium/r2/shared/extensions/Float.kt | 2 +- .../reader/preferences/UserPreferences.kt | 2 +- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index 4e9a95dec0..e9ae8604a0 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -6,7 +6,6 @@ package org.readium.r2.navigator.epub -import org.readium.r2.shared.publication.ReadingProgression as PublicationReadingProgression import android.content.SharedPreferences import android.graphics.PointF import android.graphics.RectF @@ -77,6 +76,7 @@ import org.readium.r2.shared.fetcher.Resource import org.readium.r2.shared.publication.Link import org.readium.r2.shared.publication.Locator import org.readium.r2.shared.publication.Publication +import org.readium.r2.shared.publication.ReadingProgression as PublicationReadingProgression import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.publication.presentation.presentation import org.readium.r2.shared.publication.services.isRestricted @@ -1010,4 +1010,4 @@ class EpubNavigatorFragment internal constructor( @ExperimentalReadiumApi private val EpubSettings.effectiveBackgroundColor: Int get() = - backgroundColor?.int ?: theme.backgroundColor \ No newline at end of file + backgroundColor?.int ?: theme.backgroundColor diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt index 9613195521..163855a102 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/MappedPreference.kt @@ -10,6 +10,13 @@ import org.readium.r2.shared.ExperimentalReadiumApi fun Preference.map(from: (T) -> V, to: (V) -> T): Preference = MappedPreference(this, from, to) +/** + * Creates a new [EnumPreference] object wrapping the receiver with the provided [supportedValues]. + */ +@ExperimentalReadiumApi +fun Preference.withSupportedValues(vararg supportedValues: T): EnumPreference = + withSupportedValues(supportedValues.toList()) + /** * Creates a new [EnumPreference] object wrapping the receiver with the provided [supportedValues]. */ @@ -29,6 +36,13 @@ fun EnumPreference.map( ): EnumPreference = MappedEnumPreference(this, from, to, supportedValues) +/** + * Creates a new [EnumPreference] object wrapping the receiver with the provided [supportedValues]. + */ +@ExperimentalReadiumApi +fun EnumPreference.withSupportedValues(vararg supportedValues: T): EnumPreference = + withSupportedValues(supportedValues.toList()) + /** * Creates a new [EnumPreference] object wrapping the receiver with the provided [supportedValues]. */ @@ -93,8 +107,8 @@ fun > RangePreference.map( */ @ExperimentalReadiumApi fun > RangePreference.withSupportedRange( - range: ClosedRange = range, - progressionStrategy: ProgressionStrategy = progressionStrategy) + range: ClosedRange = supportedRange, + progressionStrategy: ProgressionStrategy ): RangePreference = map( supportedRange = { range }, diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt index 323f3f2a28..04bcc633e0 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/ProgressionStrategy.kt @@ -6,7 +6,6 @@ package org.readium.r2.navigator.preferences -import kotlin.math.abs import org.readium.r2.shared.extensions.equalsDelta /** @@ -26,7 +25,10 @@ interface ProgressionStrategy { * * @param equalsDelta Provide an equality algorithm to compare floating point numbers. */ -class StepsProgression>(private val steps: List, private val equalsDelta: (T, T) -> Boolean) : ProgressionStrategy { +class StepsProgression>( + private val steps: List, + private val equalsDelta: (T, T) -> Boolean +) : ProgressionStrategy { companion object { operator fun invoke(vararg steps: Int): StepsProgression = diff --git a/readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt b/readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt index d5101fd4bc..45e7ebbfcd 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/extensions/Float.kt @@ -15,4 +15,4 @@ fun Float.equalsDelta(other: Float, delta: Float = 0.001f) = @InternalReadiumApi fun Double.equalsDelta(other: Double, delta: Double = 0.001) = - this == other || abs(this - other) < delta \ No newline at end of file + this == other || abs(this - other) < delta diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index ee1cc27478..bdc2625aac 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -26,7 +27,6 @@ import org.readium.r2.navigator.epub.EpubPreferencesEditor import org.readium.r2.navigator.preferences.* import org.readium.r2.navigator.preferences.Color as ReadiumColor import org.readium.r2.navigator.preferences.TextAlign as ReadiumTextAlign -import androidx.compose.material.icons.filled.* import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.util.Language From ecb032b69a001b39febef15529834393cedff372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 19 Dec 2022 19:18:02 +0100 Subject: [PATCH 31/45] Make `resourceAtUrl` optional --- .../src/main/java/org/readium/r2/navigator/R2BasicWebView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt index 99206dc06f..542c85b892 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt @@ -83,7 +83,7 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte fun shouldInterceptRequest(webView: WebView, request: WebResourceRequest): WebResourceResponse? = null @InternalReadiumApi - fun resourceAtUrl(url: String): Resource? + fun resourceAtUrl(url: String): Resource? = null } lateinit var listener: Listener From 462e0faed9f6281f849a8c76e325d631a553642c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 20 Dec 2022 15:41:35 +0100 Subject: [PATCH 32/45] Remove non embedded `FontFamily` and fix `alternate` --- docs/guides/epub-custom-fonts.md | 2 +- .../readium/r2/navigator/R2BasicWebView.kt | 2 +- .../navigator/epub/EpubNavigatorFragment.kt | 64 +++++++++++-------- .../navigator/epub/EpubNavigatorViewModel.kt | 10 ++- .../readium/r2/navigator/epub/EpubSettings.kt | 16 +++-- .../epub/css/FontFamilyDeclaration.kt | 35 ++++++---- .../r2/navigator/epub/css/ReadiumCss.kt | 14 ++-- .../readium/r2/navigator/preferences/Types.kt | 32 ++-------- .../java/org/readium/r2/testapp/Readium.kt | 4 ++ .../r2/testapp/reader/EpubReaderFragment.kt | 1 + .../reader/preferences/PreferencesManagers.kt | 13 +++- .../reader/preferences/UserPreferences.kt | 1 + 12 files changed, 114 insertions(+), 80 deletions(-) diff --git a/docs/guides/epub-custom-fonts.md b/docs/guides/epub-custom-fonts.md index 8304692d14..e83e9629ea 100644 --- a/docs/guides/epub-custom-fonts.md +++ b/docs/guides/epub-custom-fonts.md @@ -3,7 +3,7 @@ The `FontFamily` type represents a font family that can be picked up by users when they are reading reflowable EPUBs. Some are predefined in the navigator (though typefaces are not necessarily available for them) and you can create new ones using a name of your choice. Providing an `alternate` font family as a fallback is a good idea in case the font file is unreachable. ```kotlin - val openDyslexic = FontFamily(name = "OpenDyslexic", alternate = FontFamily.ACCESSIBLE_DFA) + val openDyslexic = FontFamily(name = "OpenDyslexic") ``` # Adding custom typefaces to the EPUB navigator diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt index 542c85b892..d405c3cc99 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt @@ -94,7 +94,7 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte internal val scrollModeFlow = MutableStateFlow(false) -/** Indicates that a user text selection is active. */ + /** Indicates that a user text selection is active. */ internal var isSelecting = false val scrollMode: Boolean get() = scrollModeFlow.value diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index e9ae8604a0..207136b701 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -41,17 +41,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.json.JSONObject -import org.readium.r2.navigator.DecorableNavigator -import org.readium.r2.navigator.Decoration -import org.readium.r2.navigator.DecorationId -import org.readium.r2.navigator.ExperimentalDecorator -import org.readium.r2.navigator.ExperimentalDragGesture -import org.readium.r2.navigator.NavigatorDelegate -import org.readium.r2.navigator.R -import org.readium.r2.navigator.R2BasicWebView -import org.readium.r2.navigator.SelectableNavigator -import org.readium.r2.navigator.Selection -import org.readium.r2.navigator.VisualNavigator +import org.readium.r2.navigator.* import org.readium.r2.navigator.databinding.ActivityR2ViewpagerBinding import org.readium.r2.navigator.epub.EpubNavigatorViewModel.RunScriptCommand import org.readium.r2.navigator.epub.css.FontFamilyDeclaration @@ -120,7 +110,7 @@ class EpubNavigatorFragment internal constructor( } } - data class Configuration( + data class Configuration internal constructor( /** * Patterns for asset paths which will be available to EPUB resources under @@ -132,13 +122,7 @@ class EpubNavigatorFragment internal constructor( * Use .* to serve all app assets. */ @ExperimentalReadiumApi - val servedAssets: List = emptyList(), - - /** - * Font declarations to inject through Readium CSS. - */ - @ExperimentalReadiumApi - internal val fontFamilyDeclarations: MutableList = mutableListOf(), + val servedAssets: List, /** * Readium CSS reading system settings. @@ -146,26 +130,24 @@ class EpubNavigatorFragment internal constructor( * See https://readium.org/readium-css/docs/CSS19-api.html#reading-system-styles */ @ExperimentalReadiumApi - val readiumCssRsProperties: RsProperties = RsProperties(), + val readiumCssRsProperties: RsProperties, /** * Supported HTML decoration templates. */ - val decorationTemplates: HtmlDecorationTemplates = HtmlDecorationTemplates.defaultTemplates(), + val decorationTemplates: HtmlDecorationTemplates, /** * Custom [ActionMode.Callback] to be used when the user selects content. * * Provide one if you want to customize the selection context menu items. */ - var selectionActionModeCallback: ActionMode.Callback? = null, + var selectionActionModeCallback: ActionMode.Callback?, /** * Whether padding accounting for display cutouts should be applied. */ - val shouldApplyInsetsPadding: Boolean? = true, - - internal val javascriptInterfaces: MutableMap = mutableMapOf(), + val shouldApplyInsetsPadding: Boolean?, /** * Disable user selection if the publication is protected by a DRM (e.g. with LCP). @@ -176,8 +158,28 @@ class EpubNavigatorFragment internal constructor( * https://github.com/readium/kotlin-toolkit/issues/299#issuecomment-1315643577 */ @DelicateReadiumApi - val disableSelectionWhenProtected: Boolean = true + var disableSelectionWhenProtected: Boolean, + + internal val fontFamilyDeclarations: MutableList, + internal val javascriptInterfaces: MutableMap ) { + constructor( + servedAssets: List = emptyList(), + readiumCssRsProperties: RsProperties = RsProperties(), + decorationTemplates: HtmlDecorationTemplates = HtmlDecorationTemplates.defaultTemplates(), + selectionActionModeCallback: ActionMode.Callback? = null, + shouldApplyInsetsPadding: Boolean? = true, + ) : this( + servedAssets = servedAssets, + readiumCssRsProperties = readiumCssRsProperties, + decorationTemplates = decorationTemplates, + selectionActionModeCallback = selectionActionModeCallback, + shouldApplyInsetsPadding = shouldApplyInsetsPadding, + disableSelectionWhenProtected = true, + fontFamilyDeclarations = mutableListOf(), + javascriptInterfaces = mutableMapOf() + ) + /** * Registers a new factory for the [JavascriptInterface] named [name]. * @@ -190,13 +192,21 @@ class EpubNavigatorFragment internal constructor( /** * Adds a declaration for [fontFamily] using [builderAction]. + * + * @param alternates Specifies a list of alternative font families used as fallbacks when + * symbols are missing from [fontFamily]. */ @ExperimentalReadiumApi fun addFontFamilyDeclaration( fontFamily: FontFamily, + alternates: List = emptyList(), builderAction: (MutableFontFamilyDeclaration).() -> Unit ) { - val declaration = buildFontFamilyDeclaration(fontFamily.name, builderAction) + val declaration = buildFontFamilyDeclaration( + fontFamily = fontFamily.name, + alternates = alternates.map { it.name }, + builderAction = builderAction + ) fontFamilyDeclarations.add(declaration) } } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt index f81d2ad2be..06848d4905 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt @@ -4,6 +4,8 @@ * available in the top-level LICENSE file of the project. */ +@file:OptIn(ExperimentalReadiumApi::class) + package org.readium.r2.navigator.epub import android.app.Application @@ -113,7 +115,7 @@ internal class EpubNavigatorViewModel( private val css = MutableStateFlow( ReadiumCss( rsProperties = config.readiumCssRsProperties, - fontFaces = config.fontFamilyDeclarations.flatMap { it.fontFaces }, + fontFamilyDeclarations = config.fontFamilyDeclarations, googleFonts = googleFonts, assetsBaseHref = WebViewServer.assetsBaseHref ).update(settings.value) @@ -399,3 +401,9 @@ internal class EpubNavigatorViewModel( } } } + +private val FontFamily.Companion.LITERATA: FontFamily get() = FontFamily("Literata") +private val FontFamily.Companion.PT_SERIF: FontFamily get() = FontFamily("PT Serif") +private val FontFamily.Companion.ROBOTO: FontFamily get() = FontFamily("Roboto") +private val FontFamily.Companion.SOURCE_SANS_PRO: FontFamily get() = FontFamily("Source Sans Pro") +private val FontFamily.Companion.VOLLKORN: FontFamily get() = FontFamily("Vollkorn") diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt index 4f426a729f..86a9574e08 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt @@ -67,12 +67,20 @@ data class EpubSettings( @OptIn(ExperimentalReadiumApi::class) internal fun ReadiumCss.update(settings: EpubSettings): ReadiumCss { - fun FontFamily.toCss(): List = buildList { - add(name) - val alternateChain = alternate?.toCss() - alternateChain?.let { addAll(it) } + fun resolveFontStack(fontFamily: String): List = buildList { + add(fontFamily) + + val alternates = fontFamilyDeclarations + .firstOrNull { it.fontFamily == fontFamily } + ?.alternates + ?: emptyList() + + addAll(alternates.flatMap(::resolveFontStack)) } + fun FontFamily.toCss(): List = + resolveFontStack(name) + fun Color.toCss(): CssColor = CssColor.Int(int) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt index e5bba8f44d..e3e740d059 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt @@ -10,21 +10,29 @@ import org.readium.r2.shared.ExperimentalReadiumApi /** * Build a declaration for [fontFamily] using [builderAction]. + * + * @param alternates Specifies a list of alternative font families used as fallbacks when symbols + * are missing from [fontFamily]. */ @ExperimentalReadiumApi -fun buildFontFamilyDeclaration( +internal fun buildFontFamilyDeclaration( fontFamily: String, + alternates: List, builderAction: (MutableFontFamilyDeclaration).() -> Unit ) = - MutableFontFamilyDeclaration(fontFamily).apply(builderAction).toFontFamilyDeclaration() + MutableFontFamilyDeclaration(fontFamily, alternates).apply(builderAction).toFontFamilyDeclaration() /** * A font family declaration. + * + * @param alternates Specifies a list of alternative font families used as fallbacks when symbols + * are missing from [fontFamily]. */ @ExperimentalReadiumApi -data class FontFamilyDeclaration internal constructor( - internal val fontFamily: String, - internal val fontFaces: List +internal data class FontFamilyDeclaration( + val fontFamily: String, + val alternates: List, + val fontFaces: List ) /** @@ -41,21 +49,21 @@ internal fun buildFontFaceDeclaration( * An immutable font face declaration. */ @ExperimentalReadiumApi -data class FontFaceDeclaration internal constructor( - private val fontFamily: String, - private val sources: List, - private var fontStyle: FontStyle? = null, - private var fontWeight: FontWeight? = null, +internal data class FontFaceDeclaration( + val fontFamily: String, + val sources: List, + var fontStyle: FontStyle? = null, + var fontWeight: FontWeight? = null, ) { - internal fun links(urlNormalizer: (String) -> String): List = + fun links(urlNormalizer: (String) -> String): List = sources .filter { it.preload } .map { """""" } - internal fun toCss(urlNormalizer: (String) -> String): String { + fun toCss(urlNormalizer: (String) -> String): String { val descriptors = buildMap { set("font-family", """"$fontFamily"""") @@ -100,6 +108,7 @@ internal data class FontFaceSource( @ExperimentalReadiumApi data class MutableFontFamilyDeclaration internal constructor( private val fontFamily: String, + private val alternates: List, private val fontFaces: MutableList = mutableListOf() ) { @@ -110,7 +119,7 @@ data class MutableFontFamilyDeclaration internal constructor( internal fun toFontFamilyDeclaration(): FontFamilyDeclaration { check(fontFaces.isNotEmpty()) - return FontFamilyDeclaration(fontFamily, fontFaces) + return FontFamilyDeclaration(fontFamily, alternates, fontFaces) } } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt index 8a15409db1..53a829eadf 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/ReadiumCss.kt @@ -19,7 +19,7 @@ internal data class ReadiumCss( val layout: Layout = Layout(language = null, Layout.Stylesheets.Default, ReadingProgression.LTR), val rsProperties: RsProperties = RsProperties(), val userProperties: UserProperties = UserProperties(), - val fontFaces: List = emptyList(), + val fontFamilyDeclarations: List = emptyList(), val googleFonts: List = emptyList(), val assetsBaseHref: String ) { @@ -104,9 +104,11 @@ internal data class ReadiumCss( */ private val fontsInjectableCss: List by lazy { buildList { - for (fontFace in fontFaces) { - add(fontFace.toCss(::normalizeAssetUrl)) - } + addAll( + fontFamilyDeclarations + .flatMap { it.fontFaces } + .map { it.toCss(::normalizeAssetUrl) } + ) if (googleFonts.isNotEmpty()) { val families = googleFonts.joinToString("|") { it.name } @@ -124,7 +126,9 @@ internal data class ReadiumCss( } private val fontsInjectableLinks: List by lazy { - fontFaces.flatMap { it.links(::normalizeAssetUrl) } + fontFamilyDeclarations + .flatMap { it.fontFaces } + .flatMap { it.links(::normalizeAssetUrl) } } private fun normalizeAssetUrl(url: String): String = diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt index 8b5df1213d..7930b24a62 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt @@ -67,10 +67,13 @@ enum class ImageFilter { * Typeface for a publication's text. * * When not available, the Navigator should use [alternate] as a fallback. + * + * For a list of vetted font families, see https://readium.org/readium-css/docs/CSS10-libre_fonts. */ +@JvmInline @ExperimentalReadiumApi @Serializable -data class FontFamily(val name: String, val alternate: FontFamily? = null) { +value class FontFamily(val name: String) { companion object { // Generic font families @@ -81,33 +84,10 @@ data class FontFamily(val name: String, val alternate: FontFamily? = null) { val FANTASY = FontFamily("fantasy") val MONOSPACE = FontFamily("monospace") - // Vetted font families - // See https://readium.org/readium-css/docs/CSS10-libre_fonts - - // Serif - val CHARIS_SIL = FontFamily("Charis SIL", alternate = SERIF) - val FAUSTINA = FontFamily("Faustina", alternate = SERIF) - val IBM_PLEX_SERIF = FontFamily("IBM Plex Serif", alternate = SERIF) - val LITERATA = FontFamily("Literata", alternate = SERIF) - val MERRIWEATHER = FontFamily("Merriweather", alternate = SERIF) - val PT_SERIF = FontFamily("PT Serif", alternate = SERIF) - val VOLLKORN = FontFamily("Vollkorn", alternate = SERIF) - - // Sans-serif - val CLEAR_SANS = FontFamily("Clear Sans", alternate = SANS_SERIF) - val FIRA_SANS = FontFamily("Fira Sans", alternate = SANS_SERIF) - val LIBRE_FRANKLIN = FontFamily("Libre Franklin", alternate = SANS_SERIF) - val MERRIWEATHER_SANS = FontFamily("Merriweather Sans", alternate = SANS_SERIF) - val PT_SANS = FontFamily("PT Sans", alternate = SANS_SERIF) - val SOURCE_SANS_PRO = FontFamily("Source Sans Pro", alternate = SANS_SERIF) - - // Accessibility + // Accessibility fonts embedded with Readium val ACCESSIBLE_DFA = FontFamily("AccessibleDfA") - val IA_WRITER_DUOSPACE = FontFamily("IA Writer Duospace", alternate = MONOSPACE) + val IA_WRITER_DUOSPACE = FontFamily("IA Writer Duospace") val OPEN_DYSLEXIC = FontFamily("OpenDyslexic") - - // System - val ROBOTO = FontFamily("Roboto", alternate = SANS_SERIF) } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt index c15378e2c7..c0eac6b68d 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt @@ -9,6 +9,7 @@ package org.readium.r2.testapp import android.content.Context import org.readium.adapters.pdfium.document.PdfiumDocumentFactory import org.readium.r2.lcp.LcpService +import org.readium.r2.navigator.preferences.FontFamily import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.util.Try import org.readium.r2.streamer.Streamer @@ -39,3 +40,6 @@ class Readium(context: Context) { pdfFactory = PdfiumDocumentFactory(context) ) } + +@OptIn(ExperimentalReadiumApi::class) +val FontFamily.Companion.LITERATA: FontFamily get() = FontFamily("Literata") diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt index 23ac551c01..02064a2a17 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt @@ -25,6 +25,7 @@ import org.readium.r2.navigator.html.toCss import org.readium.r2.navigator.preferences.FontFamily import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.Locator +import org.readium.r2.testapp.LITERATA import org.readium.r2.testapp.R import org.readium.r2.testapp.reader.preferences.UserPreferencesViewModel import org.readium.r2.testapp.search.SearchFragment diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/PreferencesManagers.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/PreferencesManagers.kt index 31f85cbf42..e9b575e304 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/PreferencesManagers.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/PreferencesManagers.kt @@ -31,6 +31,7 @@ import org.readium.r2.navigator.preferences.Configurable import org.readium.r2.navigator.preferences.PreferencesFilter import org.readium.r2.navigator.preferences.PreferencesSerializer import org.readium.r2.shared.ExperimentalReadiumApi +import org.readium.r2.shared.extensions.tryOrNull import org.readium.r2.testapp.utils.extensions.stateInFirst class PreferencesManager

> internal constructor( @@ -81,11 +82,19 @@ sealed class PreferencesManagerFactory

>( private suspend fun getPreferences(bookId: Long, scope: CoroutineScope): StateFlow

{ val sharedPrefs = dataStore.data .map { data -> data[key(klass)] } - .map { json -> json?.let { preferencesSerializer.deserialize(it) } ?: emptyPreferences } + .map { json -> + tryOrNull { + json?.let { preferencesSerializer.deserialize(it) } + } ?: emptyPreferences + } val pubPrefs = dataStore.data .map { data -> data[key(bookId)] } - .map { json -> json?.let { preferencesSerializer.deserialize(it) } ?: emptyPreferences } + .map { json -> + tryOrNull { + json?.let { preferencesSerializer.deserialize(it) } + } ?: emptyPreferences + } return combine(sharedPrefs, pubPrefs) { shared, pub -> shared + pub } .stateInFirst(scope, SharingStarted.Eagerly) diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index bdc2625aac..54a8ba2e73 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -30,6 +30,7 @@ import org.readium.r2.navigator.preferences.TextAlign as ReadiumTextAlign import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.util.Language +import org.readium.r2.testapp.LITERATA import org.readium.r2.testapp.reader.ReaderViewModel import org.readium.r2.testapp.utils.compose.ColorPicker import org.readium.r2.testapp.utils.compose.DropdownMenuButton From 4e5fbd7f7c2fc968cd696b4f6563e3c7d7ee1638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 20 Dec 2022 19:17:31 +0100 Subject: [PATCH 33/45] Improve the configuration API --- .../navigator/epub/EpubNavigatorFragment.kt | 32 +++++++++++-------- .../r2/testapp/reader/EpubReaderFragment.kt | 15 ++++++--- .../reader/preferences/UserPreferences.kt | 12 +++---- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index 207136b701..da3e13fe6c 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -100,9 +100,9 @@ class EpubNavigatorFragment internal constructor( ) : Fragment(), VisualNavigator, SelectableNavigator, DecorableNavigator, Configurable { // Make a copy to prevent the user from modifying the configuration after initialization. - internal val config: Configuration = configuration.copy( - servedAssets = configuration.servedAssets + "readium/.*" - ).apply { + internal val config: Configuration = configuration.copy().apply { + servedAssets += "readium/.*" + addFontFamilyDeclaration(FontFamily.OPEN_DYSLEXIC) { addFontFace { addSource("readium/fonts/OpenDyslexic-Regular.otf") @@ -122,7 +122,7 @@ class EpubNavigatorFragment internal constructor( * Use .* to serve all app assets. */ @ExperimentalReadiumApi - val servedAssets: List, + var servedAssets: List, /** * Readium CSS reading system settings. @@ -130,12 +130,12 @@ class EpubNavigatorFragment internal constructor( * See https://readium.org/readium-css/docs/CSS19-api.html#reading-system-styles */ @ExperimentalReadiumApi - val readiumCssRsProperties: RsProperties, + var readiumCssRsProperties: RsProperties, /** * Supported HTML decoration templates. */ - val decorationTemplates: HtmlDecorationTemplates, + var decorationTemplates: HtmlDecorationTemplates, /** * Custom [ActionMode.Callback] to be used when the user selects content. @@ -147,7 +147,7 @@ class EpubNavigatorFragment internal constructor( /** * Whether padding accounting for display cutouts should be applied. */ - val shouldApplyInsetsPadding: Boolean?, + var shouldApplyInsetsPadding: Boolean?, /** * Disable user selection if the publication is protected by a DRM (e.g. with LCP). @@ -160,8 +160,8 @@ class EpubNavigatorFragment internal constructor( @DelicateReadiumApi var disableSelectionWhenProtected: Boolean, - internal val fontFamilyDeclarations: MutableList, - internal val javascriptInterfaces: MutableMap + internal var fontFamilyDeclarations: List, + internal var javascriptInterfaces: Map ) { constructor( servedAssets: List = emptyList(), @@ -176,8 +176,8 @@ class EpubNavigatorFragment internal constructor( selectionActionModeCallback = selectionActionModeCallback, shouldApplyInsetsPadding = shouldApplyInsetsPadding, disableSelectionWhenProtected = true, - fontFamilyDeclarations = mutableListOf(), - javascriptInterfaces = mutableMapOf() + fontFamilyDeclarations = emptyList(), + javascriptInterfaces = emptyMap() ) /** @@ -187,7 +187,7 @@ class EpubNavigatorFragment internal constructor( * resource. */ fun registerJavascriptInterface(name: String, factory: JavascriptInterfaceFactory) { - javascriptInterfaces[name] = factory + javascriptInterfaces += name to factory } /** @@ -202,12 +202,16 @@ class EpubNavigatorFragment internal constructor( alternates: List = emptyList(), builderAction: (MutableFontFamilyDeclaration).() -> Unit ) { - val declaration = buildFontFamilyDeclaration( + fontFamilyDeclarations += buildFontFamilyDeclaration( fontFamily = fontFamily.name, alternates = alternates.map { it.name }, builderAction = builderAction ) - fontFamilyDeclarations.add(declaration) + } + + companion object { + operator fun invoke(builder: Configuration.() -> Unit): Configuration = + Configuration().apply(builder) } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt index 02064a2a17..704798c41f 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt @@ -52,24 +52,29 @@ class EpubReaderFragment : VisualReaderFragment(), EpubNavigatorFragment.Listene initialLocator = readerData.initialLocation, listener = this, initialPreferences = readerData.preferencesManager.preferences.value, - configuration = EpubNavigatorFragment.Configuration( + configuration = EpubNavigatorFragment.Configuration { + // To customize the text selection menu. + selectionActionModeCallback = customSelectionActionModeCallback + // App assets which will be accessible from the EPUB resources. // You can use simple glob patterns, such as "images/.*" to allow several // assets in one go. servedAssets = listOf( - /** Icon for the annotation side mark, see [annotationMarkTemplate]. */ + // For the custom font Literata. "fonts/.*", + // Icon for the annotation side mark, see [annotationMarkTemplate]. "annotation-icon.svg" - ), - ).apply { + ) + // Register the HTML template for our custom [DecorationStyleAnnotationMark]. decorationTemplates[DecorationStyleAnnotationMark::class] = annotationMarkTemplate() - selectionActionModeCallback = customSelectionActionModeCallback + // Declare a custom font family for reflowable EPUBs. addFontFamilyDeclaration(FontFamily.LITERATA) { addFontFace { addSource("fonts/Literata-VariableFont_opsz,wght.ttf") setFontStyle(FontStyle.NORMAL) + // Literata is a variable font family, so we can provide a font weight range. setFontWeightRange() } addFontFace { diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index 54a8ba2e73..c28b6864a8 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -400,13 +400,11 @@ private fun ColumnScope.ReflowableUserPreferences( title = "Typeface", preference = fontFamily .withSupportedValues( - listOf( - FontFamily.LITERATA, - FontFamily.SANS_SERIF, - FontFamily.IA_WRITER_DUOSPACE, - FontFamily.ACCESSIBLE_DFA, - FontFamily.OPEN_DYSLEXIC - ) + FontFamily.LITERATA, + FontFamily.SANS_SERIF, + FontFamily.IA_WRITER_DUOSPACE, + FontFamily.ACCESSIBLE_DFA, + FontFamily.OPEN_DYSLEXIC ), commit = commit ) { value -> From d0b35b6eb2b3380aff89a46d117b47b562d4ae26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 20 Dec 2022 19:51:46 +0100 Subject: [PATCH 34/45] Update font guide --- CHANGELOG.md | 2 +- docs/guides/epub-custom-fonts.md | 60 --------------- docs/guides/epub-fonts.md | 125 +++++++++++++++++++++++++++++++ mkdocs.yml | 2 +- 4 files changed, 127 insertions(+), 62 deletions(-) delete mode 100644 docs/guides/epub-custom-fonts.md create mode 100644 docs/guides/epub-fonts.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 071ebf2c68..b268080091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ All notable changes to this project will be documented in this file. Take a look ``` * New [PSPDFKit](readium/adapters/pspdfkit) adapter for rendering PDF documents. [Take a look at the user guide](docs/guides/pdf.md). * [A brand new text-to-speech implementation](docs/guides/tts.md). -* [Support for custom fonts with the EPUB navigator](docs/guides/epub-custom-fonts.md). +* [Support for custom fonts with the EPUB navigator](docs/guides/epub-fonts.md). * New EPUB user settings, as part of [the revamped Settings API](docs/guides/navigator-settings.md): * `backgroundColor` - Default page background color. * `textColor` - Default page text color. diff --git a/docs/guides/epub-custom-fonts.md b/docs/guides/epub-custom-fonts.md deleted file mode 100644 index e83e9629ea..0000000000 --- a/docs/guides/epub-custom-fonts.md +++ /dev/null @@ -1,60 +0,0 @@ -# The FontFamily type - -The `FontFamily` type represents a font family that can be picked up by users when they are reading reflowable EPUBs. Some are predefined in the navigator (though typefaces are not necessarily available for them) and you can create new ones using a name of your choice. Providing an `alternate` font family as a fallback is a good idea in case the font file is unreachable. - -```kotlin - val openDyslexic = FontFamily(name = "OpenDyslexic") -``` - -# Adding custom typefaces to the EPUB navigator - -By default, Android doesn't guarantee the availability of any specific typeface on devices. Only generic font families can reliably be used: `sans-serif`, `serif`, `cursive` and `monospace`. - -Additionally, the `EpubNavigatorFragment` comes with three font families that you don't have to provide typefaces for: - -* [AccessibleDfA](https://github.com/Orange-OpenSource/font-accessible-dfa), by Orange; -* [IA Writer Duospace](https://github.com/iaolo/iA-Fonts/tree/master/iA%20Writer%20Duospace), by iA; -* [OpenDyslexic](https://opendyslexic.org/). - -To add new typefaces, you need to provide font family declarations when initializing the `EpubNavigatorFragment` factory. Make sure that one of the patterns that you provide in `servedAssets` is matching the asset path you pass to `æddSource`. - -```kotlin -EpubNavigatorFragment( - ..., - configuration = EpubNavigatorFactory.Configuration( - servedAssets = listOf( - "fonts/.*" - ) - ).apply { - addFontFamilyDeclaration(FontFamily.LITERATA) { - addFontFace { - addSource("fonts/Literata-VariableFont_opsz,wght.ttf") - setFontStyle(FontStyle.NORMAL) - } - addFontFace { - addSource("fonts/Literata-Italic-VariableFont_opsz,wght.ttf") - setFontStyle(FontStyle.ITALIC) - } - } - } -) -``` - -# Customizing the list of fonts available through the EPUB preferences editor - -If you have added custom typefaces to the EPUB navigator, you probably want to customize the list of fonts available through the preferences editor as well. - -```kotlin -EpubNavigatorFactory( - ..., - preferencesEditorConfiguration = EpubPreferencesEditor.Configuration( - fontFamilies = listOf(FontFamily.LITERATA, FontFamily.OPEN_DYSLEXIC, FontFamily.ACCESSIBLE_DFA) - ) - ) -) -``` - -## Android Downloadable Fonts - -Android [Downloadable Fonts](https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts) are not yet supported. Please open a new issue if you want to contribute this feature to Readium. - diff --git a/docs/guides/epub-fonts.md b/docs/guides/epub-fonts.md new file mode 100644 index 0000000000..c14f26d067 --- /dev/null +++ b/docs/guides/epub-fonts.md @@ -0,0 +1,125 @@ +# Font families in the EPUB navigator + +Readium allows users to customize the font family used to render a reflowable EPUB, by changing the [EPUB navigator preferences](navigator-settings.md). + +:warning: You cannot change the default font family of a fixed-layout EPUB (with zoomable pages), as it is similar to a PDF or a comic book. + +## Available font families + +Android does not ship with any font families except the generic ones: `sans-serif`, `serif`, `cursive` and `monospace`. As a result, the default selection of typefaces with the Readium Kotlin toolkit is quite limited. + +To improve readability, Readium embeds three additional font families designed for accessibility: + +* [OpenDyslexic](https://opendyslexic.org/) +* [AccessibleDfA](https://github.com/Orange-OpenSource/font-accessible-dfa), by Orange +* [iA Writer Duospace](https://github.com/iaolo/iA-Fonts/tree/master/iA%20Writer%20Duospace), by iA + +You can use all these font families out of the box with the EPUB navigator: + +```kotlin +// Set the current font family. +epubNavigator.submitPreferences(EpubPreferences( + fontFamily = FontFamily.OPEN_DYSLEXIC +)) +``` + +```kotlin +// To customize the list of available font families with the preferences editor: +epubPreferencesEditor.fontFamily + .withSupportedValues( + FontFamily.SANS_SERIF, + FontFamily.SERIF, + FontFamily.CURSIVE, + FontFamily.MONOSPACE, + FontFamily.OPEN_DYSLEXIC, + FontFamily.ACCESSIBLE_DFA, + FontFamily.IA_WRITER_DUOSPACE + ) +``` + +## How to add custom font families? + +To offer more choices to your users, you must embed and declare custom font families. Use the following steps: + +1. Get the font files in the desired format, such as .ttf and .otf. [Google Fonts](https://fonts.google.com/) is a good source of free fonts. +2. Copy the files to a subdirectory of your [app `assets`](https://developer.android.com/guide/topics/resources/providing-resources), such as `src/main/assets/fonts`. +3. Declare new extensions for your custom font families to make them first-class citizens. This is optional but convenient. + ```kotlin + val FontFamily.Companion.ATKINSON_HYPERLEGIBLE get() = FontFamily("Atkinson Hyperlegible") + val FontFamily.Companion.LITERATA: FontFamily get() = FontFamily("Literata") + ``` +4. Configure the EPUB navigator with: + * `servedAssets` - An asset path pattern to serve your font files in the navigator. + * A declaration of the font faces for all the additional font families. + ```kotlin + EpubNavigatorFactory(...).createFragmentFactory( + ..., + configuration = EpubNavigatorFactory.Configuration { + // Add the assets folder which contains the font files to authorize the navigator to access it. + servedAssets += "fonts/.*" + + // Declare a font family with a file per style. + addFontFamilyDeclaration(FontFamily.ATKINSON_HYPERLEGIBLE) { + addFontFace { + addSource("fonts/AtkinsonHyperlegible-Regular.ttf", preload = true) + setFontStyle(FontStyle.NORMAL) + setFontWeight(FontWeight.NORMAL) + } + addFontFace { + addSource("fonts/AtkinsonHyperlegible-Bold.ttf") + setFontStyle(FontStyle.NORMAL) + setFontWeight(FontWeight.BOLD) + } + addFontFace { + addSource("fonts/AtkinsonHyperlegible-Italic.ttf") + setFontStyle(FontStyle.ITALIC) + setFontWeight(FontWeight.NORMAL) + } + addFontFace { + addSource("fonts/AtkinsonHyperlegible-BoldItalic.ttf") + setFontStyle(FontStyle.ITALIC) + setFontWeight(FontWeight.BOLD) + } + } + + // Declare a variable font family. + // See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide + addFontFamilyDeclaration(FontFamily.LITERATA) { + addFontFace { + addSource("fonts/Literata-VariableFont_opsz,wght.ttf") + setFontStyle(FontStyle.NORMAL) + setFontWeightRange(200..900) + } + addFontFace { + addSource("fonts/Literata-Italic-VariableFont_opsz,wght.ttf") + setFontStyle(FontStyle.ITALIC) + setFontWeightRange(200..900) + } + } + } + ) + ``` + +You are now ready to use your custom font families. + +```kotlin +// Set the current font family. +epubNavigator.submitPreferences(EpubPreferences( + fontFamily = FontFamily.LITERATA +)) +``` + +```kotlin +// To customize the list of available font families with the preferences editor: +epubPreferencesEditor.fontFamily + .withSupportedValues( + FontFamily.ATKINSON_HYPERLEGIBLE, + FontFamily.LITERATA, + FontFamily.OPEN_DYSLEXIC, + ) +``` + +## Android Downloadable Fonts + +Android [Downloadable Fonts](https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts) are not yet supported. Please open a new issue if you want to contribute this feature to Readium. + diff --git a/mkdocs.yml b/mkdocs.yml index 1a83d96244..08f95340eb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -46,7 +46,7 @@ nav: - Migration Guide: migration-guide.md - User Guides: - Navigator settings: guides/navigator-settings.md - - Adding custom fonts: guides/epub-custom-fonts.md + - EPUB font families: guides/epub-fonts.md - PDF support: guides/pdf.md - Text-to-speech: guides/tts.md - Extracting the content of a publication: guides/content.md From 8eb980fa9e584348d6dd691ee79e6b497f243a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 21 Dec 2022 15:09:09 +0100 Subject: [PATCH 35/45] Clean up user preferences view model --- .../java/org/readium/r2/testapp/Readium.kt | 1 - .../preferences/UserPreferencesViewModel.kt | 49 ++++++------------- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt index c0eac6b68d..0abdd0406b 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt @@ -17,7 +17,6 @@ import org.readium.r2.streamer.Streamer /** * Holds the shared Readium objects and services used by the app. */ -@OptIn(ExperimentalReadiumApi::class) class Readium(context: Context) { /** diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt index 9f611477f1..95bfbbd90f 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt @@ -14,7 +14,6 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import org.readium.adapters.pdfium.navigator.PdfiumPreferences import org.readium.adapters.pdfium.navigator.PdfiumSettings -import org.readium.r2.navigator.Navigator import org.readium.r2.navigator.epub.* import org.readium.r2.navigator.preferences.* import org.readium.r2.shared.ExperimentalReadiumApi @@ -23,7 +22,7 @@ import org.readium.r2.testapp.reader.* /** * Manages user settings. * - * Note: This is not an Android [ViewModel], but it is a component of [ReaderViewModel]. + * Note: This is not an Android ViewModel, but it is a component of [ReaderViewModel]. * * @param bookId Database ID for the book. */ @@ -35,6 +34,19 @@ class UserPreferencesViewModel ) { + fun bind(configurable: Configurable, lifecycleOwner: LifecycleOwner) { + with(lifecycleOwner) { + preferencesManager.preferences + .flowWithLifecycle(lifecycle) + .onEach { configurable.submitPreferences(it) } + .launchIn(lifecycleScope) + } + } + + fun commitPreferences() = viewModelScope.launch { + preferencesManager.setPreferences(this@UserPreferencesViewModel.preferencesEditor.preferences) + } + companion object { operator fun invoke( @@ -61,37 +73,4 @@ class UserPreferencesViewModel null } } - - /** - * Current [Navigator] settings. - */ - private val _settings = MutableStateFlow(null) - - /** - * Current reader theme. - */ - val theme: StateFlow = _settings - .filterIsInstance() - .map { it.theme } - .stateIn(viewModelScope, SharingStarted.Lazily, initialValue = Theme.LIGHT) - - fun bind(configurable: Configurable, lifecycleOwner: LifecycleOwner) { - with(lifecycleOwner) { - configurable.settings - .flowWithLifecycle(lifecycle) - .onEach { - _settings.value = it - } - .launchIn(lifecycleScope) - - preferencesManager.preferences - .flowWithLifecycle(lifecycle) - .onEach { configurable.submitPreferences(it) } - .launchIn(lifecycleScope) - } - } - - fun commitPreferences() = viewModelScope.launch { - preferencesManager.setPreferences(this@UserPreferencesViewModel.preferencesEditor.preferences) - } } From a74850a14e393069ca8678d8e1dcfa7e0709409c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 21 Dec 2022 15:17:51 +0100 Subject: [PATCH 36/45] Alternate way to update the Compose UI --- .../reader/preferences/UserPreferences.kt | 11 ++++----- .../preferences/UserPreferencesViewModel.kt | 23 ++++++++++--------- .../r2/testapp/utils/extensions/Flow.kt | 23 +++++++++++-------- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index c28b6864a8..d5b1f80022 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -8,6 +8,8 @@ package org.readium.r2.testapp.reader.preferences +import org.readium.r2.navigator.preferences.Color as ReadiumColor +import org.readium.r2.navigator.preferences.TextAlign as ReadiumTextAlign import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -25,8 +27,6 @@ import java.util.* import org.readium.adapters.pdfium.navigator.PdfiumPreferencesEditor import org.readium.r2.navigator.epub.EpubPreferencesEditor import org.readium.r2.navigator.preferences.* -import org.readium.r2.navigator.preferences.Color as ReadiumColor -import org.readium.r2.navigator.preferences.TextAlign as ReadiumTextAlign import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.util.Language @@ -41,12 +41,11 @@ import org.readium.r2.testapp.utils.compose.ToggleButtonGroup */ @Composable fun UserPreferences(model: UserPreferencesViewModel<*, *>) { - val editor = remember { mutableStateOf(model.preferencesEditor, policy = neverEqualPolicy()) } - val commit: () -> Unit = { editor.value = editor.value ; model.commitPreferences() } + val editor by model.editor.collectAsState() UserPreferences( - editor = editor.value, - commit = commit + editor = editor, + commit = model::commit ) } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt index 95bfbbd90f..3494faae2c 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferencesViewModel.kt @@ -18,6 +18,7 @@ import org.readium.r2.navigator.epub.* import org.readium.r2.navigator.preferences.* import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.testapp.reader.* +import org.readium.r2.testapp.utils.extensions.mapStateIn /** * Manages user settings. @@ -31,8 +32,10 @@ class UserPreferencesViewModel, - val preferencesEditor: PreferencesEditor

+ private val createPreferencesEditor: (P) -> PreferencesEditor

) { + val editor: StateFlow> = preferencesManager.preferences + .mapStateIn(viewModelScope, createPreferencesEditor) fun bind(configurable: Configurable, lifecycleOwner: LifecycleOwner) { with(lifecycleOwner) { @@ -43,8 +46,10 @@ class UserPreferencesViewModel? = when (readerInitData) { is EpubReaderInitData -> with(readerInitData) { - val editor = navigatorFactory - .createPreferencesEditor(preferencesManager.preferences.value) - UserPreferencesViewModel( - bookId, viewModelScope, preferencesManager, editor + bookId, viewModelScope, preferencesManager, + createPreferencesEditor = navigatorFactory::createPreferencesEditor ) } is PdfReaderInitData -> with(readerInitData) { - val editor = navigatorFactory - .createPreferencesEditor(preferencesManager.preferences.value) - UserPreferencesViewModel( - bookId, viewModelScope, preferencesManager, editor + bookId, viewModelScope, preferencesManager, + createPreferencesEditor = navigatorFactory::createPreferencesEditor ) } else -> null diff --git a/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Flow.kt b/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Flow.kt index ad9f9a62c4..1d2f058aae 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Flow.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Flow.kt @@ -71,12 +71,17 @@ fun Flow.throttleLatest(period: Duration): Flow = suspend fun

Flow

.stateInFirst(scope: CoroutineScope, sharingStarted: SharingStarted) = stateIn(scope, sharingStarted, first()) -fun combine( - scope: CoroutineScope, - sharingStarted: SharingStarted, - flow: StateFlow, - flow2: StateFlow, - transform: (T1, T2) -> R -): StateFlow = - combine(flow, flow2, transform) - .stateIn(scope, sharingStarted, transform(flow.value, flow2.value)) +/** + * Transforms the value of a [StateFlow] and stores it in a new [StateFlow] using the given + * [coroutineScope]. + */ +fun StateFlow.mapStateIn( + coroutineScope: CoroutineScope, + transform: (value: T) -> M +): StateFlow = + map { transform(it) } + .stateIn( + coroutineScope, + SharingStarted.Eagerly, + transform(value) + ) From e2595445e69ce74493cbab0d5cab5293600b547f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 21 Dec 2022 16:35:39 +0100 Subject: [PATCH 37/45] Fix GitHub workflows --- .github/workflows/checks.yml | 25 ++++++++++++++----- .../r2/navigator/epub/css/ReadiumCssTest.kt | 24 ++++++++++-------- test-app/.github/workflows/build.yml | 25 ------------------- 3 files changed, 33 insertions(+), 41 deletions(-) delete mode 100644 test-app/.github/workflows/build.yml diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 142f4659f6..ea22e36a4b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -19,12 +19,26 @@ jobs: java-version: '11' distribution: 'adopt' - name: Build - run: ./gradlew clean build -x test + run: ./gradlew clean build -x test -x ktlintMainSourceSetCheck - name: Test run: ./gradlew test --continue - lint: - name: Lint + lint-kt: + name: Lint Kotlin + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + - name: Lint + run: ./gradlew ktlintCheck + + lint-js: + name: Lint JavaScript runs-on: macos-latest defaults: run: @@ -37,13 +51,12 @@ jobs: uses: actions/checkout@v2 - name: Install dependencies run: make install - - name: Lint JavaScript + - name: Lint run: yarn --cwd "$scripts" run lint - - name: Check JavaScript formatting + - name: Check formatting run: yarn --cwd "$scripts" run checkformat # FIXME: This suddenly stopped working even though the toolchain versions seem identical. # - name: Check if bundled scripts are up-to-date # run: | # make scripts # git diff --exit-code --name-only src/main/assets/readium/scripts/*.js - diff --git a/readium/navigator/src/test/java/org/readium/r2/navigator/epub/css/ReadiumCssTest.kt b/readium/navigator/src/test/java/org/readium/r2/navigator/epub/css/ReadiumCssTest.kt index f3b7a2ee23..093572d53a 100644 --- a/readium/navigator/src/test/java/org/readium/r2/navigator/epub/css/ReadiumCssTest.kt +++ b/readium/navigator/src/test/java/org/readium/r2/navigator/epub/css/ReadiumCssTest.kt @@ -593,18 +593,22 @@ class ReadiumCssTest { @Test fun `Inject font declarations`() { val sut = ReadiumCss( - fontFaces = listOf( - buildFontFaceDeclaration("Libre Franklin") { - addSource("fonts/LibreFranklin.otf") + fontFamilyDeclarations = listOf( + buildFontFamilyDeclaration("Libre Franklin", alternates = emptyList()) { + addFontFace { + addSource("fonts/LibreFranklin.otf") + } }, - buildFontFaceDeclaration("OpenDyslexic") { - addSource("fonts/OpenDyslexic.otf") + buildFontFamilyDeclaration("Open Dyslexic", alternates = emptyList()) { + addFontFace { + addSource("fonts/OpenDyslexic.otf") + } } ), googleFonts = listOf( - FontFamily.ROBOTO, - FontFamily.LITERATA, - FontFamily.PT_SERIF, + FontFamily.OPEN_DYSLEXIC, + FontFamily.SANS_SERIF, + FontFamily.SERIF, ), assetsBaseHref = "/assets/" ) @@ -625,9 +629,9 @@ class ReadiumCssTest { diff --git a/test-app/.github/workflows/build.yml b/test-app/.github/workflows/build.yml deleted file mode 100644 index bf2104fc49..0000000000 --- a/test-app/.github/workflows/build.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Build - -on: - push: - branches: [ develop ] - pull_request: - branches: [ develop ] - -jobs: - build: - name: Build - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - lfs: 'true' - - name: Set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: '11' - distribution: 'adopt' - - name: Build - run: ./gradlew clean build From d4769e96c58bb814d3e3777312b72d7c424d12bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Wed, 21 Dec 2022 18:04:35 +0100 Subject: [PATCH 38/45] Disable checks for draft PRs --- .github/workflows/checks.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ea22e36a4b..b021fe6419 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -4,6 +4,7 @@ on: push: branches: [ main, develop ] pull_request: + types: [ review_requested, ready_for_review ] jobs: build: From 265a6b5a2e8ed688e285d31775f045b0c64301d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 22 Dec 2022 13:05:40 +0100 Subject: [PATCH 39/45] Various fixes --- .../navigator/epub/EpubNavigatorFragment.kt | 4 +- .../navigator/epub/EpubPreferencesEditor.kt | 6 +-- .../readium/r2/navigator/epub/EpubSettings.kt | 4 +- .../r2/navigator/epub/EpubSettingsResolver.kt | 4 +- .../epub/css/FontFamilyDeclaration.kt | 46 +++++++++---------- .../readium/r2/navigator/preferences/Types.kt | 1 - .../r2/testapp/reader/EpubReaderFragment.kt | 4 +- .../reader/preferences/UserPreferences.kt | 4 +- 8 files changed, 35 insertions(+), 38 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index da3e13fe6c..5ebca0739f 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -495,7 +495,7 @@ class EpubNavigatorFragment internal constructor( if (viewModel.layout == EpubLayout.FIXED) return if (previous.fontSize != new.fontSize) { - r2PagerAdapter?.setFontSize(new.fontSize ?: 1.0) + r2PagerAdapter?.setFontSize(new.fontSize) } if (previous.effectiveBackgroundColor != new.effectiveBackgroundColor) { resourcePager.setBackgroundColor(new.effectiveBackgroundColor) @@ -514,7 +514,7 @@ class EpubNavigatorFragment internal constructor( return } - (fragment as? R2EpubPageFragment)?.setFontSize(settings.value.fontSize ?: 1.0) + (fragment as? R2EpubPageFragment)?.setFontSize(settings.value.fontSize) } } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt index e5dbec3da3..7e53f2b2d9 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt @@ -87,7 +87,7 @@ class EpubPreferencesEditor internal constructor( val fontSize: RangePreference = RangePreferenceDelegate( getValue = { preferences.fontSize }, - getEffectiveValue = { state.settings.fontSize ?: 1.0 }, + getEffectiveValue = { state.settings.fontSize }, getIsEffective = { layout == EpubLayout.REFLOWABLE }, updateValue = { value -> updateValues { it.copy(fontSize = value) } }, supportedRange = 0.4..5.0, @@ -103,13 +103,13 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(hyphens = value) } }, ) - val imageFilter: EnumPreference = + val imageFilter: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.imageFilter }, getEffectiveValue = { state.settings.imageFilter }, getIsEffective = { state.settings.theme == Theme.DARK }, updateValue = { value -> updateValues { it.copy(imageFilter = value) } }, - supportedValues = listOf(ImageFilter.NONE, ImageFilter.DARKEN, ImageFilter.INVERT), + supportedValues = listOf(ImageFilter.DARKEN, ImageFilter.INVERT), ) val language: Preference = diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt index 86a9574e08..1ba34459e9 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettings.kt @@ -40,10 +40,10 @@ data class EpubSettings( val backgroundColor: Color?, val columnCount: ColumnCount, val fontFamily: FontFamily?, - val fontSize: Double?, + val fontSize: Double, val fontWeight: Double?, val hyphens: Boolean?, - val imageFilter: ImageFilter, + val imageFilter: ImageFilter?, val language: Language?, val letterSpacing: Double?, val ligatures: Boolean?, diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt index b1aae60f65..dd18f81ca4 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubSettingsResolver.kt @@ -32,10 +32,10 @@ internal class EpubSettingsResolver( backgroundColor = preferences.backgroundColor, columnCount = preferences.columnCount ?: defaults.columnCount ?: ColumnCount.AUTO, fontFamily = preferences.fontFamily, - fontSize = preferences.fontSize ?: defaults.fontSize, + fontSize = preferences.fontSize ?: defaults.fontSize ?: 1.0, fontWeight = preferences.fontWeight ?: defaults.fontWeight, hyphens = preferences.hyphens ?: defaults.hyphens, - imageFilter = preferences.imageFilter ?: defaults.imageFilter ?: ImageFilter.NONE, + imageFilter = preferences.imageFilter ?: defaults.imageFilter, language = language, letterSpacing = preferences.letterSpacing ?: defaults.letterSpacing, ligatures = preferences.ligatures ?: defaults.ligatures, diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt index e3e740d059..f1fb492a07 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/css/FontFamilyDeclaration.kt @@ -7,6 +7,7 @@ package org.readium.r2.navigator.epub.css import org.readium.r2.shared.ExperimentalReadiumApi +import org.readium.r2.shared.util.Either /** * Build a declaration for [fontFamily] using [builderAction]. @@ -53,7 +54,7 @@ internal data class FontFaceDeclaration( val fontFamily: String, val sources: List, var fontStyle: FontStyle? = null, - var fontWeight: FontWeight? = null, + var fontWeight: Either>? = null, ) { fun links(urlNormalizer: (String) -> String): List = @@ -75,10 +76,10 @@ internal data class FontFaceDeclaration( fontWeight?.let { when (it) { - is FontWeight.Range -> - set("font-weight", "${it.range.start} ${it.range.endInclusive}") - is FontWeight.Value -> - set("font-weight", it.value) + is Either.Left -> + set("font-weight", it.value.value) + is Either.Right -> + set("font-weight", "${it.value.start} ${it.value.endInclusive}") } } } @@ -131,7 +132,7 @@ data class MutableFontFaceDeclaration internal constructor( private val fontFamily: String, private val sources: MutableList = mutableListOf(), private var fontStyle: FontStyle? = null, - private var fontWeight: FontWeight? = null + private var fontWeight: Either>? = null ) { /** @@ -155,14 +156,16 @@ data class MutableFontFaceDeclaration internal constructor( * Set the font weight of the font face. */ fun setFontWeight(fontWeight: FontWeight) { - this.fontWeight = fontWeight + this.fontWeight = Either(fontWeight) } /** * Set the font weight range of a variable font face. */ - fun setFontWeightRange(range: ClosedRange = 1..1000) { - this.fontWeight = FontWeight.Range(range) + fun setFontWeight(range: ClosedRange) { + require(range.start >= 1) + require(range.endInclusive <= 1000) + this.fontWeight = Either(range) } internal fun toFontFaceDeclaration() = @@ -184,19 +187,14 @@ enum class FontStyle { * See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-weight#common_weight_name_mapping */ @ExperimentalReadiumApi -sealed class FontWeight { - data class Range(val range: ClosedRange = 1..1000) : FontWeight() - data class Value(val value: Int) : FontWeight() - - companion object { - val THIN = Value(100) - val EXTRA_LIGHT = Value(200) - val LIGHT = Value(300) - val NORMAL = Value(400) - val MEDIUM = Value(500) - val SEMI_BOLD = Value(600) - val BOLD = Value(700) - val EXTRA_BOLD = Value(800) - val BLACK = Value(900) - } +enum class FontWeight(val value: Int) { + THIN(100), + EXTRA_LIGHT(200), + LIGHT(300), + NORMAL(400), + MEDIUM(500), + SEMI_BOLD(600), + BOLD(700), + EXTRA_BOLD(800), + BLACK(900); } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt index 7930b24a62..627bbe1ed6 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/preferences/Types.kt @@ -58,7 +58,6 @@ enum class ColumnCount { @ExperimentalReadiumApi @Serializable enum class ImageFilter { - @SerialName("none") NONE, @SerialName("darken") DARKEN, @SerialName("invert") INVERT; } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt index 704798c41f..9d0aef6e6f 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/EpubReaderFragment.kt @@ -75,12 +75,12 @@ class EpubReaderFragment : VisualReaderFragment(), EpubNavigatorFragment.Listene addSource("fonts/Literata-VariableFont_opsz,wght.ttf") setFontStyle(FontStyle.NORMAL) // Literata is a variable font family, so we can provide a font weight range. - setFontWeightRange() + setFontWeight(200..900) } addFontFace { addSource("fonts/Literata-Italic-VariableFont_opsz,wght.ttf") setFontStyle(FontStyle.ITALIC) - setFontWeightRange() + setFontWeight(200..900) } } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index d5b1f80022..dc9fa5935e 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -255,7 +255,7 @@ private fun ColumnScope.ReflowableUserPreferences( fontSize: RangePreference? = null, fontWeight: RangePreference? = null, hyphens: Preference? = null, - imageFilter: EnumPreference? = null, + imageFilter: EnumPreference? = null, language: Preference? = null, letterSpacing: RangePreference? = null, ligatures: Preference? = null, @@ -367,9 +367,9 @@ private fun ColumnScope.ReflowableUserPreferences( commit = commit ) { value -> when (value) { - ImageFilter.NONE -> "None" ImageFilter.DARKEN -> "Darken" ImageFilter.INVERT -> "Invert" + null -> "None" } } } From e96f4120e680802f8697ee45b2e4fc7fbc64649f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 22 Dec 2022 13:28:18 +0100 Subject: [PATCH 40/45] Edit navigator settings guide --- docs/guides/navigator-settings.md | 224 ++++++++++++++++-------------- 1 file changed, 122 insertions(+), 102 deletions(-) diff --git a/docs/guides/navigator-settings.md b/docs/guides/navigator-settings.md index 99d1111d39..05b7f32056 100644 --- a/docs/guides/navigator-settings.md +++ b/docs/guides/navigator-settings.md @@ -6,11 +6,11 @@ Take a look at the [migration guide](../migration-guide.md) if you are already u ## Overview -A few Readium components – such as the Navigator – support dynamic configuration through the `Configurable` interface. +The Readium Navigator can be configured dynamically, as it implements the `Configurable` interface. -The application cannot explicitly set the Navigator settings. Instead, you can submit a set of `Preferences` to the Navigator (`Configurable`) which will in turn recompute its settings and refresh the presentation. +You cannot directly overwrite the Navigator settings. Instead, you submit a set of `Preferences` to the Navigator, which will then recalculate its settings and update the presentation. -For a concrete example: "font size" is a **setting**, the application can submit the font size value `150%` which is a **preference**. +For instance: "font size" is a **setting**, and the application can submit the font size value `150%` as a **preference**. @@ -18,80 +18,70 @@ For a concrete example: "font size" is a **setting**, the application can submit // 1. Create a set of preferences. val preferences = EpubPreferences( fontFamily = FontFamily.SERIF, - fontSize = 2, + fontSize = 2.0, publisherStyles = false ) -// 2. Submit the preferences, the Navigator will update the presentation and its settings. +// 2. Submit the preferences, the Navigator will update its settings and the presentation. navigator.submitPreferences(preferences) ``` -In addition, Readium provides a `PreferencesEditor` for each `Configurable` navigator which enables you to easily build a user settings interface. + +### Editing preferences + +To assist you in building a preferences user interface or modifying existing preferences, navigators can offer a `PreferencesEditor`. Each implementation includes rules for adjusting preferences, such as the supported values or ranges. ```kotlin -// 1. Create a preferences editor -val preferencesEditor = navigatorFactory.createPreferencesEditor(initialPreferences) +// 1. Create a preferences editor. +val editor = EpubNavigatorFactory(publication).createPreferencesEditor(preferences) -// 2. Let the user update preferences through the preferences editor -preferencesEditor.apply { +// 2. Modify the preferences through the editor. +editor.apply { fontFamily.set(FontFamily.SERIF) fontSize.increment() publisherStyles.toggle() } -// 3. Submit the updated preferences -navigator.submitPreferences(preferencesEditor.preferences) +// 3. Submit the edited preferences +navigator.submitPreferences(editor.preferences) ``` -### Preferences Editors - -The `PreferencesEditor` object is unique for each Navigator implementation and provides helpers to deal with each specific preference. Each `Preference` property represents a single configurable property of the Navigator, such as the font size or the theme. It provides access to the current value of the preference, the value that the navigator will compute as the corresponding setting, as well as additional metadata and constraints depending on the preference type. - -Here are some of the available preference types: - -* `TogglePreference` - a simple boolean preference, e.g. whether or not the publisher styles are enabled. -* `RangePreference` - a preference for numbers constrained in a range, e.g. the page margins as a `RangeSetting` could range from 0px to 200px. -* `EnumPreference` - a preference whose value is a member of the enum `V`, e.g. the theme (`light`, `dark`, `sepia`) or the font family. - -#### `Preferences` are low-level +### Preferences are low-level -The `Preferences` objects are technical low-level properties. While some of them can be directly exposed to the user, such as the font size, other preferences should not be displayed as-is. +Preferences are low-level technical properties. While some of them can be exposed directly to the user, such as the font size, others should not be displayed as-is. -For example in EPUB, we simulate two pages side by side with `columnCount` (`auto`, `1`, `2`) for reflowable resources and `spread` (`auto`, `never`, `always`) for a fixed layout publication. Instead of showing both settings with all their possible values in the user interface, you might prefer showing a single switch button to enable a dual-page mode which will set both settings appropriately. +For instance, in EPUB, we can simulate two pages side by side using the `columnCount` (`auto`, `1`, `2`) property for reflowable resources, and the `spread` (`auto`, `never`, `always`) property for fixed-layout publications. Rather than displaying both of these settings with all of their possible values in the user interface, you might prefer to show a single switch button to enable a dual-page mode, which will set both settings appropriately. -### Preferences +### Inactive settings -The `Preferences` object holds the values which should be preferred by the Navigator when computing its `Settings`. Preferences can be combined by the app from different sources: +A setting may be inactive if its activation conditions are not met in a set of preferences. The Navigator will ignore inactive settings when updating its presentation. For example, with the EPUB navigator, the word spacing setting requires the publisher styles to be disabled in order to take effect. -* Static app defaults. -* User preferences restored from JSON. -* User preferences interface. - -#### Inactive settings - -A setting can be inactive if its activation conditions are not met in a set of preferences. The Navigator will ignore inactive settings when refreshing its presentation. For instance with the EPUB navigator, the word spacing setting requires the publisher styles to be disabled to take effect. - -You can check if a setting is effective with: +You can check if a setting is effective for a set of preferences using the `PreferencesEditor`: ```kotlin -preference.isEffective +val editor = EpubNavigatorFactory(publication) + .createPreferencesEditor(preferences) + +editor.wordSpacing.isEffective ``` ## Setting the initial Navigator preferences and app defaults -When opening a publication, you want to apply the user preferences right away. You can do that by providing them to the Navigator constructor. The API depends on each Navigator implementation, but looks like this: +When opening a publication, you can immediately apply the user preferences by providing them to the Navigator constructor. The API for doing this varies depending on the Navigator implementation, but generally looks like this: ```kotlin val navigatorFactory = EpubNavigatorFactory( publication = publication, configuration = EpubNavigatorFactory.Configuration( - defaults = EpubDefaults(language="fr") + defaults = EpubDefaults( + pageMargins = 1.5, + scroll = true + ) ) ) navigatorFactory.createFragmentFactory( - ..., initialPreferences = EpubPreferences( - scroll = true + language = "fr" ) ) ``` @@ -100,29 +90,25 @@ The `defaults` are used as fallback values when the default Navigator settings a ## Build a user settings interface -:question: The following examples are using [Jetpack Compose](https://developer.android.com/jetpack/compose), but could be implemented with regular Android views. +:question: The following examples use [Jetpack Compose](https://developer.android.com/jetpack/compose), but could be implemented using regular Android views as well. -You can use the `PreferencesEditor` API to build a user settings interface dynamically. As this API is agnostic to the type of publication (excepted the editor itself), you can reuse parts of the user settings screen across Navigator implementations or media types. +### `PreferencesEditor` -For example, you could group the user preferences per nature of publications: +Although you could create and modify `Preferences` objects directly before submitting them to the Navigator, a `PreferenceEditor` can assist you by providing helpers for dealing with each preference type when building the user interface. -* `ReflowableUserPreferences` for a visual publication with adjustable fonts and dimensions, such as a reflowable EPUB, HTML document or PDF with reflow mode enabled. -* `FixedLayoutUserPreferences` for a visual publication with a fixed layout, such as FXL EPUB, PDF or comic books. -* `PlaybackUserPreferences` for an audiobook, text-to-speech or EPUB media overlays preferences. +`PreferencesEditor` implementations are specific to each Navigator, but they all provide `Preference` properties for every setting (e.g. theme or font size). -### Stateless `UserSettings` composable +### Stateless `UserPreferences` composable -You can switch over the `PreferencesEditor` type to decide on the best kind of user preferences screen for it. +You can use the `PreferencesEditor` type to determine which (media type agnostic) view to create. ```kotlin @Composable -fun > UserSettings( - editor: E, +fun UserPreferences( + editor: PreferencesEditor

, commit: () -> Unit ) { Column { - Text("User settings") - Button( onClick = { editor.clear() @@ -132,49 +118,58 @@ fun > UserSettings( Text("Reset") } - Divider() - when (editor) { + is PsPdfKitPreferencesEditor -> + FixedLayoutUserPreferences( + commit = commit, + scroll = editor.scroll, + fit = editor.fit, + pageSpacing = editor.pageSpacing, + ... + ) is EpubPreferencesEditor -> ReflowableUserPreferences( - publisherStyles = editor.publisherStyles, - fontSize = editor.fontSize, - fontFamily = editor.fontFamily, commit = commit, + ... ) } } } ``` -The `commit` parameter is a closure used to save the edited preferences to the data store. +The `commit` parameter is a closure used to save the edited preferences to the data store, before submitting them to the Navigator. -:question: The individual `EpubPreferences`' `Preference` properties are forwarded to `ReflowableUserPreferences` to be able to reuse it with other reflowable publication types. +:question: The individual `PsPdfKitPreferencesEditor` properties are passed to `FixedLayoutUserPreferences` to that it can be reused with other fixed-layout publication types, such as FXL EPUB or comic books. -### User settings composable for reflowable publications +### User settings composable for fixed-layout publications -This stateless composable displays the actual preferences for a reflowable publication. The `Preference` parameters are nullable as they might not be available at all times or for all media types. It delegates the rendering of individual preferences to specific composables. +This stateless composable displays the actual preferences for a fixed-layout publication. The `Preference` parameters are nullable as they might not be available at all times or for all media types. It delegates the rendering of individual preferences to more specific composables. ```kotlin @Composable -fun ReflowableUserPreferences( - publisherStyles: Preference? = null, - fontSize: RangePreference? = null, - fontFamily: EnumPreference? = null, - commit: () -> Unit +fun FixedLayoutUserPreferences( + commit: () -> Unit, + scroll: Preference? = null, + fit: EnumPreference? = null, + pageSpacing: RangePreference? = null ) { - if (publisherStyles != null) { - SwitchItem("Publisher styles", publisherStyles, commit) + if (scroll != null) { + SwitchItem("Scroll mode", scroll, commit) } - if (fontSize != null) { - StepperItem("Font size", fontSize, commit) + if (fit != null) { + MenuItem("Page fit", fit, commit) { value -> + when (value) { + Fit.WIDTH -> "Width" + Fit.HEIGHT -> "Height" + Fit.CONTAIN -> "Width and height" + Fit.COVER -> "Cover" + } + } } - if (font != null) { - MenuItem("Font", fontFamily, commit) { fontFamily -> - fontFamily.name - } + if (pageSpacing != null) { + StepperItem("Page spacing", pageSpacing, commit) } } ``` @@ -210,22 +205,29 @@ fun SwitchItem( } ``` -This composable takes advantage of the helpers in `Preference` to set the preference in two different ways: +This composable uses the helpers in `Preference` to edit the preference in two different ways: -* `toggle()` will invert the current preference when tapping on the whole list item. +* `toggle()` will reverse the current preference when tapping on the entire list item. * `set(checked)` sets an explicit value provided by the `Switch`'s `onCheckedChange` callback. -:point_up: Note that the current state for `Switch` is derived from the selected preference first, and the actual setting value as a fallback (`checked = preference.value ?: preference.effectiveValue`). We deemed more important to display the user selected value first, even if it is not applied yet in the Navigator. Your opinion may differ, in which case you can use `checked = preference.effectiveValue`. +### `value` vs `effectiveValue`, which one to use? -### Composable for a `RangePreference` +In the previous example, you may have noticed the use of `preference.value ?: preference.effectiveValue` for the current value. -A `RangePreference` can be represented as a stepper component with decrement and increment buttons. +* `value` holds the user-selected preference, which may be `null`. +* `effectiveValue` is the setting value that will actually be used by the Navigator once the preferences are submitted. It may be different from the user-selected value if it is incompatible or invalid. + +This is a common pattern with this API because it is less confusing to display the user-selected value, even if it will not actually be used by the Navigator. + +### Composable for a `RangePreference` + +A `RangePreference` can be represented as a stepper component with decrement and increment buttons. ```kotlin @Composable -fun > StepperItem( +fun > StepperItem( title: String, - preference: RangePreference, + preference: RangePreference, commit: () -> Unit, ) { ListItem( @@ -238,7 +240,7 @@ fun > StepperItem( commit() } ) { - Icon(Icons.Default.Remove, contentDescription = "Less") + Icon(Icons.Default.Remove, contentDescription = "Decrease") } val currentValue = preferences.value ?: preference.effectiveValue @@ -250,7 +252,7 @@ fun > StepperItem( commit() } ) { - Icon(Icons.Default.Add, contentDescription = "More") + Icon(Icons.Default.Add, contentDescription = "Increase") } } }, @@ -258,18 +260,18 @@ fun > StepperItem( } ``` -This composable uses the `increment()` and `decrement()` range helpers of `RangePreference`, but you could also set a value manually. +This composable uses the `increment()` and `decrement()` range helpers of `RangePreference`, but it is also possible to set a value manually. -Between the two buttons, we display the current value using the `RangeSetting.formatValue()` helper. This will automatically format the value to a human-readable string, such as a percentage or a value with units (e.g. 30px). +Between the two buttons, we display the current value using the `RangeSetting.formatValue()` helper. This will automatically format the value to a human-readable string, such as a percentage or a value with units (e.g. 30px). -### Composable for an `EnumPreference` +### Composable for an `EnumPreference` -An enum can be displayed with various components, such as: +An `EnumPreference` is a preference accepting a closed set of values. It can be displayed using various UI components, such as: * a dropdown menu for a large enum * [segmented buttons](https://m3.material.io/components/segmented-buttons/overview) for a small one -In this example, we chose a dropdown menu built using the `preference.supportedValues`, which returns the allowed enum members. +In the following example, we chose a dropdown menu built with `preference.supportedValues`, which returns the allowed enum members. ```kotlin @Composable @@ -326,40 +328,58 @@ fun DropdownMenuButton( ## Save and restore the user preferences -Having a user settings screen is moot if you cannot save and restore the selected preferences for future sessions. Each navigator comes with a JSON serialization helper that you can use or not. +Having a user settings screen is not useful if you cannot save and restore the selected preferences for future sessions. Each navigator includes a JSON serialization helper that you can choose to use or not. ```kotlin -val epubPreferencesSerializer = EpubPreferencesSerializer() +val serializer = EpubPreferencesSerializer() -val jsonString = epubPreferencesSerializer.serialize(preferences) +val jsonString = serializer.serialize(preferences) ``` When you are ready to restore the user preferences, construct a new `Preferences` object from the JSON string. ```kotlin -val preferences = epubPreferencesSerializer.deserialize(jsonString) +val preferences = serializer.deserialize(jsonString) ``` In the Test App, `UserPreferencesViewModel` delegates the preferences state hoisting and persistence to a `PreferencesManager`, which acts as a single source of truth. ### Splitting and merging preferences -How you store user preferences has an impact on the available features. You could have, for example: +The way you store user preferences can affect the available features. You could have, for example: -* A different unique set of preferences for each publication. +* A unique set of preferences for each publication. * Preferences shared between publications with the same profile or media type (EPUB, PDF, etc.). * Global preferences shared with all publications (e.g. theme). -* Several user setting profiles/themes that the user can switch to and modify independently. +* Several user setting profiles/themes that the user can switch between and modify independently. * Some settings that are not stored as JSON and will need to be reconstructed (e.g. the publication language). -To help you to deal with this, the toolkit provides for each navigator suggested filters that you can use or not. -You can then combine several sets of preferences with the `+` operator. +To assist you, the toolkit provides suggested filters for each navigator. You can combine preference filters with the `+` operator. ```kotlin -val bookPrefs = EpubPublicationPreferencesFilter.filter(preferences) -val profilePrefs = EpubSharedPreferencesFilter.filter(preferences) +// The suggested filter for the preferences that should be tied to a +// publication and not shared: +val publicationFilter = EpubPublicationPreferencesFilter + +// The suggested filter for the preferences that will be shared between +// publications of the same type. +// Note that in this example, we combine it with an inline custom filter +// to remove the `theme` preference which will be stored globally. +val sharedFilter = EpubSharedPreferencesFilter + + { it.copy(theme = null) } + +// A custom filter to extract the settings which be stored globally. +val globalFilter = PreferencesFilter { + EpubPreferences(theme = it.theme) +} + +val publicationPrefs = preferencesFilter.filter(preferences) +val sharedPrefs = sharedFilter.filter(preferences) +val globalPrefs = globalFilter.filter(preferences) -val combinedPrefs = profilePrefs + bookPrefs +// You can reconstruct the original preferences by combining the filtered ones. +val combinedPrefs = publicationPrefs + sharedPrefs + globalPrefs ``` -:warning: Some preferences are really tied to a particular publication and should never be shared between several publications, such as the language. It's recommended that you store these preferences separately per book and that's what the suggested filters would make you do if you use them. +:warning: Some preferences are closely tied to a specific publication and should never be shared between multiple publications, such as the language. It is recommended that you store these preferences separately per book, which is what the suggested filters will do if you use them. + From cfccf623d743f0b910ab4b3f5b78b50f7340d6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 22 Dec 2022 16:23:54 +0100 Subject: [PATCH 41/45] Document preferences constraints --- docs/guides/navigator-settings.md | 83 +++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/docs/guides/navigator-settings.md b/docs/guides/navigator-settings.md index 05b7f32056..eff7b21b9d 100644 --- a/docs/guides/navigator-settings.md +++ b/docs/guides/navigator-settings.md @@ -383,3 +383,86 @@ val combinedPrefs = publicationPrefs + sharedPrefs + globalPrefs :warning: Some preferences are closely tied to a specific publication and should never be shared between multiple publications, such as the language. It is recommended that you store these preferences separately per book, which is what the suggested filters will do if you use them. +## Appendix: Preference constraints + +### EPUB + +#### Reflowable vs fixed-layout + +EPUB comes in two very different flavors: **reflowable** which allows a lot of customization, and **fixed-layout** which is similar to a PDF or a comic book. Depending on the EPUB being rendered, the Navigator will ignore some of the preferences. + +| Setting | Reflowable | Fixed Layout | +|----------------------|--------------------|--------------------| +| `backgroundColor` | :white_check_mark: | :white_check_mark: | +| `columnCount` | :white_check_mark: | | +| `fontFamily` | :white_check_mark: | | +| `fontSize` | :white_check_mark: | | +| `fontWeight` | :white_check_mark: | | +| `hyphens` | :white_check_mark: | | +| `imageFilter` | :white_check_mark: | | +| `language` | :white_check_mark: | :white_check_mark: | +| `letterSpacing` | :white_check_mark: | | +| `ligatures` | :white_check_mark: | | +| `lineHeight` | :white_check_mark: | | +| `pageMargins` | :white_check_mark: | | +| `paragraphIndent` | :white_check_mark: | | +| `paragraphSpacing` | :white_check_mark: | | +| `publisherStyles` | :white_check_mark: | | +| `readingProgression` | :white_check_mark: | :white_check_mark: | +| `scroll` | :white_check_mark: | | +| `spread` | | :white_check_mark: | +| `textAlign` | :white_check_mark: | | +| `textColor` | :white_check_mark: | | +| `textNormalization` | :white_check_mark: | | +| `theme` | :white_check_mark: | | +| `typeScale` | :white_check_mark: | | +| `verticalText` | :white_check_mark: | | +| `wordSpacing` | :white_check_mark: | | + +#### Publisher styles + +The following advanced preferences require `publisherStyles` to be explicitly set to `false`. Make sure you convey this in your user interface. + +* `hyphens` +* `letterSpacing` +* `ligatures` +* `lineHeight` +* `paragraphIndent` +* `paragraphSpacing` +* `textAlign` +* `typeScale` +* `wordSpacing` + +#### Scroll vs paginated + +The `columnCount` preference is available only when in paginated mode (`scroll = false`). + +#### Dark theme specific preferences + +The `imageFilter` preference is available only in dark mode (`theme = Theme.DARK`). + +#### Language specific preferences + +Some preferences are not available for all languages and reading progression. + +| Preference | LTR | RTL | CJK | +|-------------------|--------------------|--------------------|-----| +| `paragraphIndent` | :white_check_mark: | :white_check_mark: | | +| `textAlign` | :white_check_mark: | :white_check_mark: | | +| `letterSpacing` | :white_check_mark: | | | +| `wordSpacing | :white_check_mark: | | | +| `hyphens` | :white_check_mark: | | | +| `ligatures` | | :white_check_mark: | | + +### PDF (PSPDFKit) + +#### Scroll vs paginated + +Some preferences are available only in scroll or paginated mode (`scroll = false`). + +| Preference | Scroll | Paginated | +|-------------------|--------------------|--------------------| +| `offsetFirstPage` | | :white_check_mark: | +| `spread` | | :white_check_mark: | +| `scrollAxis` | :white_check_mark: | | + From 8fb59ab620075d31424986a76088bf5432ea3c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 22 Dec 2022 16:59:34 +0100 Subject: [PATCH 42/45] Expand documentation comments in the editors --- docs/guides/navigator-settings.md | 4 +- .../navigator/PdfiumPreferencesEditor.kt | 23 +- .../navigator/PsPdfKitPreferencesEditor.kt | 62 ++++-- .../r2/navigator/epub/EpubPreferences.kt | 2 +- .../navigator/epub/EpubPreferencesEditor.kt | 196 ++++++++++++++++-- 5 files changed, 250 insertions(+), 37 deletions(-) diff --git a/docs/guides/navigator-settings.md b/docs/guides/navigator-settings.md index eff7b21b9d..ac8ba31b67 100644 --- a/docs/guides/navigator-settings.md +++ b/docs/guides/navigator-settings.md @@ -443,14 +443,14 @@ The `imageFilter` preference is available only in dark mode (`theme = Theme.DARK #### Language specific preferences -Some preferences are not available for all languages and reading progression. +Some preferences are not available for all languages and layout. | Preference | LTR | RTL | CJK | |-------------------|--------------------|--------------------|-----| | `paragraphIndent` | :white_check_mark: | :white_check_mark: | | | `textAlign` | :white_check_mark: | :white_check_mark: | | | `letterSpacing` | :white_check_mark: | | | -| `wordSpacing | :white_check_mark: | | | +| `wordSpacing` | :white_check_mark: | | | | `hyphens` | :white_check_mark: | | | | `ligatures` | | :white_check_mark: | | diff --git a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt index 35ef686698..c4d0b2b768 100644 --- a/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt +++ b/readium/adapters/pdfium/pdfium-navigator/src/main/java/org/readium/adapters/pdfium/navigator/PdfiumPreferencesEditor.kt @@ -12,11 +12,11 @@ import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.Metadata /** - * Interactive editor of [PdfiumPreferences]. + * Editor for a set of [PdfiumPreferences]. * - * This can be used as a helper for a user preferences screen. - * - * @see PdfiumPreferences + * Use [PdfiumPreferencesEditor] to assist you in building a preferences user interface or modifying + * existing preferences. It includes rules for adjusting preferences, such as the supported values + * or ranges. */ @ExperimentalReadiumApi class PdfiumPreferencesEditor internal constructor( @@ -39,10 +39,16 @@ class PdfiumPreferencesEditor internal constructor( override val preferences: PdfiumPreferences get() = state.preferences + /** + * Reset all preferences. + */ override fun clear() { updateValues { PdfiumPreferences() } } + /** + * Indicates how pages should be laid out within the viewport. + */ val fit: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.fit }, @@ -52,6 +58,9 @@ class PdfiumPreferencesEditor internal constructor( supportedValues = listOf(Fit.CONTAIN, Fit.WIDTH), ) + /** + * Space between pages in dp. + */ val pageSpacing: RangePreference = RangePreferenceDelegate( getValue = { preferences.pageSpacing }, @@ -63,6 +72,9 @@ class PdfiumPreferencesEditor internal constructor( valueFormatter = { "${it.format(1)} dp" }, ) + /** + * Direction of the horizontal progression across pages. + */ val readingProgression: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.readingProgression }, @@ -72,6 +84,9 @@ class PdfiumPreferencesEditor internal constructor( supportedValues = listOf(ReadingProgression.LTR, ReadingProgression.RTL), ) + /** + * Indicates the axis along which pages should be laid out in scroll mode. + */ val scrollAxis: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.scrollAxis }, diff --git a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt index 8c21277ccf..5b01eca390 100644 --- a/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt +++ b/readium/adapters/pspdfkit/pspdfkit-navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/PsPdfKitPreferencesEditor.kt @@ -23,11 +23,11 @@ import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.Metadata /** - * Interactive editor of [PsPdfKitPreferences]. + * Editor for a set of [PsPdfKitPreferences]. * - * This can be used as a view model for a user preferences screen. - * - * @see PsPdfKitPreferences + * Use [PsPdfKitPreferencesEditor] to assist you in building a preferences user interface or modifying + * existing preferences. It includes rules for adjusting preferences, such as the supported values + * or ranges. */ @ExperimentalReadiumApi class PsPdfKitPreferencesEditor internal constructor( @@ -50,10 +50,16 @@ class PsPdfKitPreferencesEditor internal constructor( override val preferences: PsPdfKitPreferences get() = state.preferences + /** + * Reset all preferences. + */ override fun clear() { updateValues { PsPdfKitPreferences() } } + /** + * Indicates how pages should be laid out within the viewport. + */ val fit: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.fit }, @@ -63,6 +69,13 @@ class PsPdfKitPreferencesEditor internal constructor( supportedValues = listOf(Fit.CONTAIN, Fit.WIDTH), ) + /** + * Indicates if the first page should be displayed in its own spread. + * + * Only effective when: + * - [scroll] is off + * - [spread] are not disabled + */ val offsetFirstPage: Preference = PreferenceDelegate( getValue = { preferences.offsetFirstPage }, @@ -71,6 +84,23 @@ class PsPdfKitPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(offsetFirstPage = value) } }, ) + /** + * Space between pages in dp. + */ + val pageSpacing: RangePreference = + RangePreferenceDelegate( + getValue = { preferences.pageSpacing }, + getEffectiveValue = { state.settings.pageSpacing }, + getIsEffective = { true }, + updateValue = { value -> updateValues { it.copy(pageSpacing = value) } }, + supportedRange = 0.0..50.0, + progressionStrategy = DoubleIncrement(5.0), + valueFormatter = { "${it.format(1)} dp" }, + ) + + /** + * Direction of the horizontal progression across pages. + */ val readingProgression: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.readingProgression }, @@ -80,6 +110,9 @@ class PsPdfKitPreferencesEditor internal constructor( supportedValues = listOf(ReadingProgression.LTR, ReadingProgression.RTL), ) + /** + * Indicates if pages should be handled using scrolling instead of pagination. + */ val scroll: Preference = PreferenceDelegate( getValue = { preferences.scroll }, @@ -88,6 +121,11 @@ class PsPdfKitPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(scroll = value) } }, ) + /** + * Indicates the axis along which pages should be laid out in scroll mode. + * + * Only effective when [scroll] is on. + */ val scrollAxis: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.scrollAxis }, @@ -97,6 +135,11 @@ class PsPdfKitPreferencesEditor internal constructor( supportedValues = listOf(Axis.VERTICAL, Axis.HORIZONTAL), ) + /** + * Indicates if the publication should be rendered with a synthetic spread (dual-page). + * + * Only effective when [scroll] is off. + */ val spread: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.spread }, @@ -106,17 +149,6 @@ class PsPdfKitPreferencesEditor internal constructor( supportedValues = listOf(Spread.AUTO, Spread.NEVER, Spread.ALWAYS), ) - val pageSpacing: RangePreference = - RangePreferenceDelegate( - getValue = { preferences.pageSpacing }, - getEffectiveValue = { state.settings.pageSpacing }, - getIsEffective = { true }, - updateValue = { value -> updateValues { it.copy(pageSpacing = value) } }, - supportedRange = 0.0..50.0, - progressionStrategy = DoubleIncrement(5.0), - valueFormatter = { "${it.format(1)} dp" }, - ) - private fun updateValues(updater: (PsPdfKitPreferences) -> PsPdfKitPreferences) { val newPreferences = updater(preferences) state = newPreferences.toState() diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferences.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferences.kt index b5b1f0fdc8..cb739ae57f 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferences.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferences.kt @@ -19,8 +19,8 @@ import org.readium.r2.shared.util.Language * @param backgroundColor Default page background color. * @param columnCount Number of reflowable columns to display (one-page view or two-page spread). * @param fontFamily Default typeface for the text. - * @param fontWeight Default boldness for the text. * @param fontSize Base text font size. + * @param fontWeight Default boldness for the text. * @param hyphens Enable hyphenation. * @param imageFilter Filter applied to images in dark theme. * @param language Language of the publication content. diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt index 7e53f2b2d9..8ebea85b05 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubPreferencesEditor.kt @@ -15,11 +15,11 @@ import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.util.Language /** - * Interactive editor of [EpubPreferences]. + * Editor for a set of [EpubPreferences]. * - * This can be used as a view model for a user preferences screen. - * - * @see EpubPreferences + * Use [EpubPreferencesEditor] to assist you in building a preferences user interface or modifying + * existing preferences. It includes rules for adjusting preferences, such as the supported values + * or ranges. */ @ExperimentalReadiumApi class EpubPreferencesEditor internal constructor( @@ -44,10 +44,19 @@ class EpubPreferencesEditor internal constructor( override val preferences: EpubPreferences get() = state.preferences + /** + * Reset all preferences. + */ override fun clear() { updateValues { EpubPreferences() } } + /** + * Default page background color. + * + * When unset, the current [theme] background color is effective. + * Only effective with fixed-layout publications. + */ val backgroundColor: Preference = PreferenceDelegate( getValue = { preferences.backgroundColor }, @@ -56,6 +65,13 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(backgroundColor = value) } }, ) + /** + * Number of reflowable columns to display (one-page view or two-page spread). + * + * Only effective when: + * - the publication is reflowable + * - [scroll] is off + */ val columnCount: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.columnCount }, @@ -65,6 +81,11 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(ColumnCount.AUTO, ColumnCount.ONE, ColumnCount.TWO), ) + /** + * Default typeface for the text. + * + * Only effective with reflowable publications. + */ val fontFamily: Preference = PreferenceDelegate( getValue = { preferences.fontFamily }, @@ -73,17 +94,13 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(fontFamily = value) } } ) - val fontWeight: RangePreference = - RangePreferenceDelegate( - getValue = { preferences.fontWeight }, - getEffectiveValue = { state.settings.fontWeight ?: 1.0 }, - getIsEffective = { layout == EpubLayout.REFLOWABLE && preferences.fontWeight != null }, - updateValue = { value -> updateValues { it.copy(fontWeight = value) } }, - valueFormatter = percentFormatter(), - supportedRange = 0.0..2.5, - progressionStrategy = DoubleIncrement(0.25) - ) - + /** + * Base text font size as a percentage. Default to 100%. + * + * Note that allowing a font size that is too large could break the pagination. + * + * Only effective with reflowable publications. + */ val fontSize: RangePreference = RangePreferenceDelegate( getValue = { preferences.fontSize }, @@ -95,6 +112,33 @@ class EpubPreferencesEditor internal constructor( valueFormatter = percentFormatter(), ) + /** + * Default boldness for the text as a percentage. + * + * If you want to change the boldness of all text, including headers, you can use this with + * [textNormalization]. + * + * Only effective with reflowable publications. + */ + val fontWeight: RangePreference = + RangePreferenceDelegate( + getValue = { preferences.fontWeight }, + getEffectiveValue = { state.settings.fontWeight ?: 1.0 }, + getIsEffective = { layout == EpubLayout.REFLOWABLE && preferences.fontWeight != null }, + updateValue = { value -> updateValues { it.copy(fontWeight = value) } }, + valueFormatter = percentFormatter(), + supportedRange = 0.0..2.5, + progressionStrategy = DoubleIncrement(0.25) + ) + + /** + * Enable hyphenation for latin languages. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + * - the layout is LTR + */ val hyphens: Preference = PreferenceDelegate( getValue = { preferences.hyphens }, @@ -103,6 +147,13 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(hyphens = value) } }, ) + /** + * Filter applied to images in dark theme. + * + * Only effective when: + * - the publication is reflowable + * - the [theme] is set to [Theme.DARK] + */ val imageFilter: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.imageFilter }, @@ -112,6 +163,11 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(ImageFilter.DARKEN, ImageFilter.INVERT), ) + /** + * Language of the publication content. + * + * This has an impact on the resolved layout (e.g. LTR, RTL). + */ val language: Preference = PreferenceDelegate( getValue = { preferences.language }, @@ -120,6 +176,14 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(language = value) } }, ) + /** + * Space between letters. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + * - the layout is LTR + */ val letterSpacing: RangePreference = RangePreferenceDelegate( getValue = { preferences.letterSpacing }, @@ -131,6 +195,14 @@ class EpubPreferencesEditor internal constructor( valueFormatter = percentFormatter(), ) + /** + * Enable ligatures in Arabic. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + * - the layout is RTL + */ val ligatures: Preference = PreferenceDelegate( getValue = { preferences.ligatures }, @@ -139,6 +211,13 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(ligatures = value) } }, ) + /** + * Leading line height. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + */ val lineHeight: RangePreference = RangePreferenceDelegate( getValue = { preferences.lineHeight }, @@ -150,6 +229,11 @@ class EpubPreferencesEditor internal constructor( valueFormatter = { it.format(5) }, ) + /** + * Factor applied to horizontal margins. Default to 1. + * + * Only effective with reflowable publications. + */ val pageMargins: RangePreference = RangePreferenceDelegate( getValue = { preferences.pageMargins }, @@ -161,6 +245,14 @@ class EpubPreferencesEditor internal constructor( valueFormatter = { it.format(5) }, ) + /** + * Text indentation for paragraphs. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + * - the layout is LTR or RTL + */ val paragraphIndent: RangePreference = RangePreferenceDelegate( getValue = { preferences.paragraphIndent }, @@ -172,6 +264,13 @@ class EpubPreferencesEditor internal constructor( valueFormatter = percentFormatter(), ) + /** + * Vertical margins for paragraphs. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + */ val paragraphSpacing: RangePreference = RangePreferenceDelegate( getValue = { preferences.paragraphSpacing }, @@ -183,6 +282,12 @@ class EpubPreferencesEditor internal constructor( valueFormatter = percentFormatter(), ) + /** + * Indicates whether the original publisher styles should be observed. Many advanced settings + * require this to be off. + * + * Only effective with reflowable publications. + */ val publisherStyles: Preference = PreferenceDelegate( getValue = { preferences.publisherStyles }, @@ -191,6 +296,11 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(publisherStyles = value) } }, ) + /** + * Direction of the reading progression across resources. + * + * This can be changed to influence directly the layout (e.g. LTR or RTL). + */ val readingProgression: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.readingProgression }, @@ -200,6 +310,12 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(ReadingProgression.LTR, ReadingProgression.RTL), ) + /** + * Indicates if the overflow of resources should be handled using scrolling instead of synthetic + * pagination. + * + * Only effective with reflowable publications. + */ val scroll: Preference = PreferenceDelegate( getValue = { preferences.scroll }, @@ -208,6 +324,12 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(scroll = value) } }, ) + /** + * Indicates if the fixed-layout publication should be rendered with a synthetic spread + * (dual-page). + * + * Only effective with fixed-layout publications. + */ val spread: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.spread }, @@ -217,6 +339,14 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(Spread.NEVER, Spread.ALWAYS), ) + /** + * Page text alignment. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + * - the layout is LTR or RTL + */ val textAlign: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.textAlign }, @@ -226,6 +356,12 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(TextAlign.START, TextAlign.LEFT, TextAlign.RIGHT, TextAlign.JUSTIFY), ) + /** + * Default page text color. + * + * When unset, the current [theme] text color is effective. + * Only effective with reflowable publications. + */ val textColor: Preference = PreferenceDelegate( getValue = { preferences.textColor }, @@ -234,6 +370,11 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(textColor = value) } } ) + /** + * Normalize text styles to increase accessibility. + * + * Only effective with reflowable publications. + */ val textNormalization: Preference = PreferenceDelegate( getValue = { preferences.textNormalization }, @@ -242,6 +383,11 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(textNormalization = value) } } ) + /** + * Reader theme (light, dark, sepia). + * + * Only effective with reflowable publications. + */ val theme: EnumPreference = EnumPreferenceDelegate( getValue = { preferences.theme }, @@ -251,6 +397,13 @@ class EpubPreferencesEditor internal constructor( supportedValues = listOf(Theme.LIGHT, Theme.DARK, Theme.SEPIA), ) + /** + * Scale applied to all element font sizes. + * + * Only effective when: + * - the publication is reflowable + * - [publisherStyles] is off + */ val typeScale: RangePreference = RangePreferenceDelegate( getValue = { preferences.typeScale }, @@ -262,6 +415,12 @@ class EpubPreferencesEditor internal constructor( progressionStrategy = StepsProgression(1.0, 1.067, 1.125, 1.2, 1.25, 1.333, 1.414, 1.5, 1.618), ) + /** + * Indicates whether the text should be laid out vertically. This is used for example with CJK + * languages. This setting is automatically derived from the language if no preference is given. + * + * Only effective with reflowable publications. + */ val verticalText: Preference = PreferenceDelegate( getValue = { preferences.verticalText }, @@ -270,6 +429,13 @@ class EpubPreferencesEditor internal constructor( updateValue = { value -> updateValues { it.copy(verticalText = value) } }, ) + /** + * Space between words. + * + * Only effective when: + * - the publication is reflowable + * - the layout is LTR + */ val wordSpacing: RangePreference = RangePreferenceDelegate( getValue = { preferences.wordSpacing }, From 27e21ee644aa5aa51417d8e986a9aff6950e7342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 22 Dec 2022 17:56:05 +0100 Subject: [PATCH 43/45] Update migration guide --- ...r-settings.md => navigator-preferences.md} | 0 docs/migration-guide.md | 40 ++++++++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) rename docs/guides/{navigator-settings.md => navigator-preferences.md} (100%) diff --git a/docs/guides/navigator-settings.md b/docs/guides/navigator-preferences.md similarity index 100% rename from docs/guides/navigator-settings.md rename to docs/guides/navigator-preferences.md diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 143262e92a..6cc46e5e92 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -103,19 +103,19 @@ override fun onTap(point: PointF): Boolean { ### Upgrading to the new Preferences API -The 2.3.0 release introduces a brand new user preferences API to configure the EPUB Navigator. This new API is easier and safer to use, [take a look at the user guide](guides/navigator-settings.md) to learn how to integrate it in your app. +The 2.3.0 release introduces a brand new user preferences API for configuring the EPUB and PDF Navigators. This new API is easier and safer to use. To learn how to integrate it in your app, [please refer to the user guide](guides/navigator-preferences.md). If you integrated the EPUB navigator from a previous version, follow these steps to migrate: -1. Get familiar with [the concepts of this new API](guides/navigator-settings.md#overview). -2. Remove the local HTTP server from your app, [as explained in the previous section](#removing-the-http-server) +1. Get familiar with [the concepts of this new API](guides/navigator-preferences.md#overview). +2. Remove the local HTTP server from your app, [as explained in the previous section](#removing-the-http-server). 3. Remove the whole [`UserSettings.kt`](https://github.com/readium/kotlin-toolkit/blob/f132e541a1d2c290a83974fb017efb352e0f825f/test-app/src/main/java/org/readium/r2/testapp/epub/UserSettings.kt) file from your app, if you copied it from the Test App. -4. Adapt your user settings interface to the new API using preferences editors. The [Test App](https://github.com/readium/kotlin-toolkit/tree/develop/test-app/src/main/java/org/readium/r2/testapp/reader/preferences) and the [user guide](guides/navigator-settings.md#build-a-user-settings-interface) contain examples using Jetpack Compose. -5. [Handle the persistence of the user preferences](guides/navigator-settings.md#save-and-restore-the-user-preferences). The settings are not stored in the `SharedPreferences` with name `org.readium.r2.settings` anymore. Instead, you are responsible for persisting and restoring the user preferences as you see fit (e.g. as a JSON file). - * If you want to migrate the legacy `SharedPreferences` settings, you can use the helper `Preferences.fromLegacyEpubSettings()` which will create a new `Preferences` object after translating the existing user settings. -6. Make sure you [restore the stored user preferences](guides/navigator-settings.md#setting-the-initial-navigator-preferences-and-app-defaults) when initializing the EPUB navigator. +4. Adapt your user settings interface to the new API using preferences editors. The [Test App](https://github.com/readium/kotlin-toolkit/tree/develop/test-app/src/main/java/org/readium/r2/testapp/reader/preferences) and the [user guide](guides/navigator-preferences.md#build-a-user-settings-interface) contain examples using Jetpack Compose. +5. [Handle the persistence of the user preferences](guides/navigator-preferences.md#save-and-restore-the-user-preferences). The settings are not stored in the `SharedPreferences` with name `org.readium.r2.settings` anymore. Instead, you are responsible for persisting and restoring the user preferences as you see fit (e.g. as a JSON file). + * If you want to migrate the legacy `SharedPreferences` settings, you can use the helper `EpubPreferences.fromLegacyEpubSettings()` which will create a new `EpubPreferences` object after translating the existing user settings. +6. Make sure you [restore the stored user preferences](guides/navigator-preferences.md#setting-the-initial-navigator-preferences-and-app-defaults) when initializing the EPUB navigator. -Refer to the following table for the correspondence between legacy settings and new ones. +Please refer to the following table for the correspondence between legacy settings and new ones. | **Legacy** | **New** | |-------------------------|--------------------------------------------------------| @@ -132,18 +132,20 @@ Refer to the following table for the correspondence between legacy settings and | `SCROLL_REF` | `overflow` (`scrolled`) | | `TEXT_ALIGNMENT_REF` | `textAlign` | | `WORD_SPACING_REF` | `wordSpacing` | -| N/A | `language` | -| N/A | `readingProgression` (e.g. RTL) | -| N/A | `textColor` | -| N/A | `backgroundColor` | -| N/A | `imageFilter` (dark theme only) | -| N/A | `paragraphIndent` | -| N/A | `paragraphSpacing` | -| N/A | `typeScale` | -| N/A | `textNormalization` (force bold, accessibility) | -| N/A | `hyphens` | -| N/A | `ligatures` (arabic) | +#### Deprecation of `userSettingsUIPreset` + +`publication.userSettingsUIPreset` is now deprecated, but you might still have this code in your application: + +```kotlin +publication.userSettingsUIPreset[ReadiumCSSName.ref(SCROLL_REF)] = true +``` + +You can remove it, as the support for screen readers will be added directly to the navigator in a coming release. However if you want to keep it, here is the equivalent with the new API: + +```kotlin +navigator.submitPreferences(currentPreferences.copy(scroll = true)) +``` ## 2.2.1 From d776fae8e3a7432107b6eb2d33c1ea0b85a7fb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Thu, 22 Dec 2022 18:02:03 +0100 Subject: [PATCH 44/45] More changes --- CHANGELOG.md | 7 ++++--- docs/guides/epub-fonts.md | 2 +- mkdocs.yml | 2 +- .../r2/testapp/reader/preferences/UserPreferences.kt | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b268080091..83789819a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,10 +43,11 @@ All notable changes to this project will be documented in this file. Take a look * New [PSPDFKit](readium/adapters/pspdfkit) adapter for rendering PDF documents. [Take a look at the user guide](docs/guides/pdf.md). * [A brand new text-to-speech implementation](docs/guides/tts.md). * [Support for custom fonts with the EPUB navigator](docs/guides/epub-fonts.md). -* New EPUB user settings, as part of [the revamped Settings API](docs/guides/navigator-settings.md): +* New EPUB user preferences, as part of [the revamped Settings API](docs/guides/navigator-preferences.md): * `backgroundColor` - Default page background color. + * `fontWeight` - Base text font weight. * `textColor` - Default page text color. - * `textNormalization` - Normalize font style, weight and variants using a specific strategy (force bold, accessibility). + * `textNormalization` - Normalize font style, weight and variants, which improves accessibility. * `imageFilter` - Filter applied to images in dark theme (darken, invert colors) * `language` - Language of the publication content. * `readingProgression` - Direction of the reading progression across resources, e.g. RTL. @@ -66,7 +67,7 @@ All notable changes to this project will be documented in this file. Take a look #### Navigator -* The EPUB user settings API got revamped. [Take a look at the user guide](docs/guides/navigator-settings.md) and the [migration guide](docs/migration-guide.md#230) to learn how to use it. +* The EPUB and PDF user preferences API got revamped. [Take a look at the user guide](docs/guides/navigator-preferences.md) and the [migration guide](docs/migration-guide.md#230) to learn how to use it. * `Decoration.extras` is now a `Map` instead of `Bundle`. You will need to update your app if you were storing custom data in `extras`, for example: ```kotlin val decoration = Decoration(..., diff --git a/docs/guides/epub-fonts.md b/docs/guides/epub-fonts.md index c14f26d067..9d82f13556 100644 --- a/docs/guides/epub-fonts.md +++ b/docs/guides/epub-fonts.md @@ -1,6 +1,6 @@ # Font families in the EPUB navigator -Readium allows users to customize the font family used to render a reflowable EPUB, by changing the [EPUB navigator preferences](navigator-settings.md). +Readium allows users to customize the font family used to render a reflowable EPUB, by changing the [EPUB navigator preferences](navigator-preferences.md). :warning: You cannot change the default font family of a fixed-layout EPUB (with zoomable pages), as it is similar to a PDF or a comic book. diff --git a/mkdocs.yml b/mkdocs.yml index 08f95340eb..5de1a594b2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -45,7 +45,7 @@ nav: - Home: index.md - Migration Guide: migration-guide.md - User Guides: - - Navigator settings: guides/navigator-settings.md + - Navigator preferences: guides/navigator-preferences.md - EPUB font families: guides/epub-fonts.md - PDF support: guides/pdf.md - Text-to-speech: guides/tts.md diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt index dc9fa5935e..6cc167d785 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/preferences/UserPreferences.kt @@ -8,8 +8,6 @@ package org.readium.r2.testapp.reader.preferences -import org.readium.r2.navigator.preferences.Color as ReadiumColor -import org.readium.r2.navigator.preferences.TextAlign as ReadiumTextAlign import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -27,6 +25,8 @@ import java.util.* import org.readium.adapters.pdfium.navigator.PdfiumPreferencesEditor import org.readium.r2.navigator.epub.EpubPreferencesEditor import org.readium.r2.navigator.preferences.* +import org.readium.r2.navigator.preferences.Color as ReadiumColor +import org.readium.r2.navigator.preferences.TextAlign as ReadiumTextAlign import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.epub.EpubLayout import org.readium.r2.shared.util.Language From 1b197bb4959ab29faec37b425afdbf69ae033cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Sun, 25 Dec 2022 10:23:08 +0100 Subject: [PATCH 45/45] Adjust GH action triggers --- .github/workflows/checks.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b021fe6419..44c4c19d54 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -4,13 +4,12 @@ on: push: branches: [ main, develop ] pull_request: - types: [ review_requested, ready_for_review ] jobs: build: name: Build and test runs-on: ubuntu-latest - + if: ${{ !github.event.pull_request.draft }} steps: - name: Checkout uses: actions/checkout@v2 @@ -27,6 +26,7 @@ jobs: lint-kt: name: Lint Kotlin runs-on: macos-latest + if: ${{ !github.event.pull_request.draft }} steps: - name: Checkout uses: actions/checkout@v2 @@ -41,12 +41,12 @@ jobs: lint-js: name: Lint JavaScript runs-on: macos-latest + if: ${{ !github.event.pull_request.draft }} defaults: run: working-directory: readium/navigator env: scripts: ${{ 'src/main/assets/_scripts' }} - steps: - name: Checkout uses: actions/checkout@v2