Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4e39b16
Proof of concept removing the HTTP server
mickael-menu Aug 5, 2022
05c5139
Refactor assets base href
mickael-menu Aug 5, 2022
b8ba7e5
Merge branch 'feature/settings' into feature/settings+server
mickael-menu Aug 10, 2022
3c37b54
Refactor EPUB server
mickael-menu Aug 11, 2022
294b0f8
Refactor link handling
mickael-menu Aug 11, 2022
07d58d3
Customize the 404 not found page in the EPUB navigator
mickael-menu Aug 11, 2022
e6b3d56
Add an explicit allowlist to serve app assets
mickael-menu Aug 11, 2022
c46fdff
Update the migration guide
mickael-menu Aug 11, 2022
c2ccd13
Setup scroll mode using new settings API
mickael-menu Aug 9, 2022
e338539
Fix scroll mode
mickael-menu Aug 11, 2022
b4f9c49
Fix dual page in FXL and add resource pager invalidation
mickael-menu Aug 11, 2022
636da85
Fix changing the reading progression
mickael-menu Aug 11, 2022
edcbdff
Add Theme colors and remove theme settings for FXL
mickael-menu Aug 11, 2022
f132e54
Add deprecation warnings
mickael-menu Aug 11, 2022
90bbfa1
Remove legacy server and user settings from the test app
mickael-menu Aug 11, 2022
b5fc76a
Fix tests
mickael-menu Aug 12, 2022
fb34c59
Update changelog
mickael-menu Aug 12, 2022
1eee3d8
Update migration guide
mickael-menu Aug 12, 2022
9748c7e
Migrate legacy `SharedPreferences` settings
mickael-menu Aug 16, 2022
8bb89f4
Merge branch 'feature/settings' into feature/settings+server
mickael-menu Sep 1, 2022
1c68ac9
Merge branch 'feature/settings+server' into feature/settings+replace-…
mickael-menu Sep 1, 2022
63e2a65
Fix RTL injection when `dir` is already there
mickael-menu Sep 1, 2022
1d5404c
Optimize imports
mickael-menu Sep 5, 2022
ee60ed8
Reset preferences before using a preset
mickael-menu Sep 5, 2022
082936e
Remove outdated overflow setting mentions
mickael-menu Sep 5, 2022
1d7abf1
Fix `Language`
mickael-menu Sep 5, 2022
8fbfdb8
Order primary languages in the EPUB parser
mickael-menu Sep 5, 2022
39d5ac7
Use generics with `Configurable`
mickael-menu Sep 5, 2022
7a8adcc
`Setting` is not a data class anymore
mickael-menu Sep 5, 2022
a506bf5
Refactor `Setting` models
mickael-menu Sep 6, 2022
0ef1443
Extract the `WebViewServer`
mickael-menu Sep 6, 2022
4260e04
Add a helper to generate asset URLs
mickael-menu Sep 6, 2022
b1d0629
Add a generic HTML error page
mickael-menu Sep 6, 2022
510d4f5
Use value classes for `Length`
mickael-menu Sep 6, 2022
8cb58b3
Use nullable constructors to build `Preferences` from a JSON
mickael-menu Sep 6, 2022
abee620
Introduce `NavigatorKind` and remove `Publication.profile`
mickael-menu Sep 6, 2022
d52e350
Refactor `Color` and `Length`
mickael-menu Sep 7, 2022
3b5bc64
Fix unit tests
mickael-menu Sep 7, 2022
cb69b6d
Merge pull request #262 from readium/feature/settings+replace-legacy
mickael-menu Sep 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,37 @@ All notable changes to this project will be documented in this file. Take a look
val result = navigator.evaluateJavascript("customInterface.api('argument')")
```
* 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. [Take a look at the user guide](docs/guides/tts.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).
* 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.
* `textNormalization` - Normalize font style, weight and variants using a specific strategy (force bold, 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.
* `typeScale` - Scale applied to all element font sizes.
* `paragraphIndent` - Text indentation for paragraphs.
* `paragraphSpacing` - Vertical margins for paragraphs.
* `hyphens` - Enable hyphenation.
* `ligatures` - Enable ligatures in Arabic.

### Changed

#### Shared

* `TransformingResource` now caches its content by default, as it is the correct behavior in most cases. Set `cacheBytes = false` explicitly to revert to the previous behavior.
* The previous PDF navigator was extracted in its own package to support third-party PDF engines. **This is a breaking change** if your app supported PDF, take a look at [the migration guide](docs/migration-guide.md).
* The previous PDF navigator was extracted in its own package to support third-party PDF engines. **This is a breaking change** if your app supported PDF, take a look at [the migration guide](docs/migration-guide.md#230).

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

### Deprecated

#### Streamer

* The local HTTP server is not needed anymore to render EPUB publications. [Take a look at the migration guide](docs/migration-guide.md#230).

### Fixed

Expand Down
25 changes: 11 additions & 14 deletions docs/guides/navigator-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

:warning: The Navigator Setting API is still experimental and currently only available with `EpubNavigatorFragment`.

Take a look at the [migration guide](../migration-guide.md) if you are already using the legacy EPUB settings.

## Overview

A few Readium components – such as the Navigator – support dynamic configuration through the `Configurable` interface. It provides an easy way to build a user settings interface and save user preferences as a JSON object.
Expand All @@ -18,7 +20,7 @@ val settings = navigator.settings.value

// 2. Create a new set of preferences.
val preferences = Preferences {
set(settings.overflow, Overflow.PAGINATED)
set(settings.fontFamily, FontFamily.SERIF)
increment(settings.fontSize)
toggle(settings.publisherStyles)
}
Expand All @@ -44,11 +46,6 @@ The `Setting` objects are technical low-level properties. While some of them can

For example in EPUB, we simulate two pages side by side with `columnCount` (`auto`, `1`, `2`) for reflowable resources and `spread` (`auto`, `landscape`, `both`, `none`) 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.

Similarly, you might want to cluster several settings together. For example, given a scrolled mode switch in the user interface:

* when on, `overflow` is set to `scrolled` and `readingProgression` to `ttb`
* when off, `overflow` is set to `paginated` and the user can freely select the `readingProgression` between the two values `ltr` and `rtl`

### Preferences

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:
Expand Down Expand Up @@ -79,7 +76,7 @@ val updatedPreferences = preferences.copy {

```kotlin
preferences.copy {
set(settings.overflow, Overflow.PAGINATED, activate = false)
set(settings.fontFamily, FontFamily.SERIF, activate = false)
increment(settings.fontSize, activate = false)
toggle(settings.publisherStyles, activate = false)
}
Expand All @@ -94,17 +91,17 @@ EpubNavigatorFragment.createFactory(
publication = publication,
...,
config = EpubNavigatorFragment.Configuration(
preferences = preferencesStore.get(publication.profile),
preferences = preferences,
defaultPreferences = Preferences {
set(EpubSettings.OVERFLOW, Overflow.SCROLLED)
set(EpubSettings.scroll, true)
}
)
)
```

The `defaultPreferences` are used as fallback values when the default Navigator settings are not suitable for your application.

:point_up: When you don't have access to an `EpubSettings` instance, the "prototype" settings (e.g. `EpubSettings.OVERFLOW`) are helpful to modify a `Preferences` object.
:point_up: When you don't have access to an `EpubSettings` instance, the "prototype" settings (e.g. `EpubSettings.SCROLL`) are helpful to modify a `Preferences` object.

## Build a user settings interface

Expand All @@ -123,12 +120,12 @@ For example, you could group the user settings per nature of publications:
This first [stateful composable](https://developer.android.com/jetpack/compose/state#stateful-vs-stateless) binds directly with a `Configurable` object to recompose when its settings are refreshed and to apply the preferences when the user interacts with the interface. It delegates the actual interface to a stateless `UserSettings` composable.

```kotlin
if (navigator is Configurable) {
if (navigator is Configurable<*>) {
UserSettings(navigator)
}

@Composable
fun UserSettings(configurable: Configurable) {
fun UserSettings(configurable: Configurable<*>) {
val settings by configurable.settings.collectAsState()
var preferences by remember { mutableStateOf(Preferences()) }

Expand Down Expand Up @@ -384,13 +381,13 @@ fun DropdownMenuButton(
Having a user settings screen is moot if you cannot save and restore the selected preferences for future sessions. Thankfully you can serialize `Preferences` to a JSON object.

```kotlin
val json = preferences.toJSON().toString()
val json = preferences.toJsonString()
```

When you are ready to restore the user preferences, construct a new `Preferences` object from the JSON string.

```kotlin
val preferences = Preferences(json)
val preferences = Preferences.fromJson(json)
```

In the Test App, `UserSettingsViewModel` delegates the preferences state hoisting and persistence to `PreferencesStore`, which acts as a single source of truth.
Expand Down
72 changes: 72 additions & 0 deletions docs/migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,78 @@ override fun onCreate(savedInstanceState: Bundle?) {
}
```

### Removing the HTTP server

The local HTTP server is not needed anymore to render EPUB publications. You can safely drop all occurrences of `Server` from your project and remove the `baseUrl` parameter when calling `EpubNavigatorFragment.createFactory()`.

:warning: You **must** adopt the new Settings API to remove the HTTP server, as described in the next section of this migration guide.

#### Serving app assets

If you were serving `assets/` files (e.g. fonts or scripts) to the EPUB resources, you can still do so with the new API.

First, declare the `assets/` paths that will be available to EPUB resources when creating the navigator. You can use [simple glob patterns](https://developer.android.com/reference/android/os/PatternMatcher#PATTERN_SIMPLE_GLOB) to allow multiple assets in one go, e.g. `fonts/.*`.

```kotlin
EpubNavigatorFragment.createFactory(
...,
config = EpubNavigatorFragment.Configuration(
servedAssets = listOf(
"fonts/.*",
"annotation-icon.svg"
)
)
)
```

Then, use the base URL `https://readium/assets/` to fetch your app assets from the web views. For example:

* `https://readium/assets/fonts/OpenDyslexic.otf`
* `https://readium/assets/annotation-icon.svg`

### Upgrading to the new Settings API

The 2.3.0 release introduces a brand new user settings 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.

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)
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. The [Test App](https://github.com/readium/kotlin-toolkit/tree/develop/test-app/src/main/java/org/readium/r2/testapp/reader/settings) 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.

Refer to the following table for the correspondence between legacy settings and new ones.

| **Legacy** | **New** |
|-------------------------|--------------------------------------------------------|
| `APPEARANCE_REF` | `theme` |
| `COLUMN_COUNT_REF` | `columnCount` (reflowable) and `spread` (fixed-layout) |
| `FONT_FAMILY_REF` | `fontFamily` |
| `FONT_OVERRIDE_REF` | N/A (handled automatically) |
| `FONT_SIZE_REF` | `fontSize` |
| `LETTER_SPACING_REF` | `letterSpacing` |
| `LINE_HEIGHT_REF` | `lineHeight` |
| `PAGE_MARGINS_REF` | `pageMargins` |
| `PUBLISHER_DEFAULT_REF` | `publisherStyles` |
| `reader_brightness` | N/A (out of scope for Readium) |
| `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) |


## 2.1.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.readium.r2.shared.PdfSupport
import org.readium.r2.shared.extensions.md5
import org.readium.r2.shared.extensions.tryOrLog
import org.readium.r2.shared.extensions.tryOrNull
import org.readium.r2.shared.fetcher.Resource
import org.readium.r2.shared.util.pdf.PdfDocument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ import com.pspdfkit.annotations.actions.GoToAction
import com.pspdfkit.document.DocumentSource
import com.pspdfkit.document.OutlineElement
import com.pspdfkit.document.PageBinding
import com.pspdfkit.document.PdfDocument as _PsPdfKitDocument
import com.pspdfkit.document.PdfDocumentLoader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.readium.r2.shared.PdfSupport
import org.readium.r2.shared.fetcher.Resource
import org.readium.r2.shared.publication.ReadingProgression
import org.readium.r2.shared.util.pdf.PdfDocument
import org.readium.r2.shared.util.pdf.PdfDocumentFactory
import timber.log.Timber
import java.io.File
import org.readium.r2.shared.util.pdf.PdfDocument
import kotlin.reflect.KClass
import com.pspdfkit.document.PdfDocument as _PsPdfKitDocument

@PdfSupport
class PsPdfKitDocumentFactory(context: Context) : PdfDocumentFactory<PsPdfKitDocument> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
package org.readium.r2.lcp

import org.readium.r2.shared.publication.ContentProtection
import org.readium.r2.shared.publication.LocalizedString
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.services.ContentProtectionService

Expand Down
8 changes: 1 addition & 7 deletions readium/lcp/src/main/java/org/readium/r2/lcp/LcpDecryptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@ package org.readium.r2.lcp
import org.readium.r2.shared.extensions.coerceFirstNonNegative
import org.readium.r2.shared.extensions.inflate
import org.readium.r2.shared.extensions.requireLengthFitInt
import org.readium.r2.shared.fetcher.TransformingResource
import org.readium.r2.shared.fetcher.FailureResource
import org.readium.r2.shared.fetcher.Resource
import org.readium.r2.shared.fetcher.ResourceTry
import org.readium.r2.shared.fetcher.LazyResource
import org.readium.r2.shared.fetcher.flatMapCatching
import org.readium.r2.shared.fetcher.mapCatching
import org.readium.r2.shared.fetcher.*
import org.readium.r2.shared.publication.Link
import org.readium.r2.shared.publication.encryption.encryption
import org.readium.r2.shared.util.Try
Expand Down
1 change: 0 additions & 1 deletion readium/lcp/src/main/java/org/readium/r2/lcp/LcpService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import org.readium.r2.lcp.service.*
import org.readium.r2.shared.publication.ContentProtection
import org.readium.r2.shared.util.Try
import java.io.File
import java.lang.Exception

/**
* Service used to acquire and open publications protected with LCP.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

package org.readium.r2.lcp.license.model

import org.json.JSONArray
import org.json.JSONObject
import org.readium.r2.lcp.LcpException
import org.readium.r2.lcp.license.model.components.Link
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import org.json.JSONObject
import org.readium.r2.lcp.LcpException
import org.readium.r2.lcp.service.URLParameters
import org.readium.r2.shared.publication.Link
import org.readium.r2.shared.util.mediatype.MediaType
import org.readium.r2.shared.util.URITemplate
import org.readium.r2.shared.util.mediatype.MediaType
import java.net.URL

data class Link(val json: JSONObject) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@ import com.google.android.exoplayer2.audio.AudioAttributes
import com.google.android.exoplayer2.ext.media2.SessionPlayerConnector
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
import com.google.android.exoplayer2.upstream.DataSource
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.readium.navigator.media2.MediaNavigator.Companion.create
import org.readium.r2.navigator.Navigator
import org.readium.r2.shared.publication.*
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.indexOfFirstWithHref
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.flatMap
import timber.log.Timber
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import androidx.media2.common.MediaMetadata
import androidx.media2.common.SessionPlayer
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import timber.log.Timber
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import androidx.media2.common.MediaItem
import androidx.media2.common.MediaMetadata
import androidx.media2.common.SessionPlayer
import androidx.media2.session.MediaSession
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.TimeoutCancellationException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.media2.common.MediaItem
import androidx.media2.common.MediaMetadata
import androidx.media2.common.SessionPlayer
import org.readium.r2.shared.util.Try
import timber.log.Timber
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.ExperimentalTime
Expand Down
8 changes: 4 additions & 4 deletions readium/navigator/src/main/assets/_scripts/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ export function getColumnCountPerScreen() {
}

export function isScrollModeEnabled() {
const style = document.documentElement.style;
return (
document.documentElement.style
.getPropertyValue("--USER__scroll")
.toString()
.trim() == "readium-scroll-on"
style.getPropertyValue("--USER__view").trim() == "readium-scroll-on" ||
// FIXME: Will need to be removed in Readium 3.0, --USER__scroll was incorrect.
style.getPropertyValue("--USER__scroll").trim() == "readium-scroll-on"
);
}

Expand Down
13 changes: 13 additions & 0 deletions readium/navigator/src/main/assets/readium/error.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>${error}</title>
</head>
<body style="text-align: center;">
<h1 style="font-size: 5em;">${error}</h1>
<h2><pre style="white-space: pre-wrap;">${href}</pre></h2>
</body>
</html>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
package org.readium.r2.navigator

import android.graphics.PointF
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import org.readium.r2.navigator.media.MediaPlayback
Expand Down
Loading