Skip to content

Commit

Permalink
Update the documentation for Readium 3.0.0-alpha.1 (readium#441)
Browse files Browse the repository at this point in the history
  • Loading branch information
mickael-menu committed Jan 15, 2024
1 parent 4bd2893 commit 5b718ad
Show file tree
Hide file tree
Showing 41 changed files with 872 additions and 255 deletions.
54 changes: 51 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,65 @@ All notable changes to this project will be documented in this file. Take a look

## [Unreleased]

:warning: Please consult [the migration guide](docs/migration-guide.md#300-alpha1) to assist you in handling the breaking changes in this latest major release.

### Added

#### Shared

* A new `Format` type was introduced to augment `MediaType` with more precise information about the format specifications of an `Asset`.
* The `DownloadManager` interface handles HTTP downloads. Components like the `LcpService` rely on it for downloading publications. Readium v3 ships with two implementations:
* `ForegroundDownloadManager` uses an `HttpClient` to download files while the app is running.
* `AndroidDownloadManager` is built upon [Android's `DownloadManager`](https://developer.android.com/reference/android/app/DownloadManager) to manage HTTP downloads, even when the application is closed. It allows for resuming downloads after losing connection.
* The default `ZipArchiveOpener` now supports streaming ZIP archives, which enables opening a packaged publication (e.g. EPUB or LCP protected audiobook):
* served by a remote HTTP server,
* accessed through an Android `ContentProvider`, such as the shared storage.

#### Navigator

* Support for keyboard events in the EPUB, PDF and image navigators. See `VisualNavigator.addInputListener()`.

### Fixed
#### LCP

#### OPDS
* You can now stream an LCP protected publication using its LCP License Document. This is useful for example to read a large audiobook without downloading it on the device first.
* The hash of protected publications is now verified upon download.

### Changed

* :warning: To avoid conflicts when merging your app resources, all resources declared in the Readium toolkit now have the prefix `readium_`. This means that you must rename any layouts or strings you have overridden. Some resources were removed from the toolkit. Please consult [the migration guide](docs/migration-guide.md#300-alpha1).
* Most APIs now return an `Error` instance instead of an `Exception` in case of failure, as these objects are not thrown by the toolkit but returned as values

#### Shared

* :warning: To improve the interoperability with other Readium toolkits (in particular the Readium Web Toolkits, which only work in a streaming context) **Readium v3 now generates and expects valid URLs** for `Locator` and `Link`'s `href`. **You must migrate the HREFs or Locators stored in your database**, please consult [the migration guide](docs/migration-guide.md#300-alpha1).
* `Link.href` and `Locator.href` are now respectively `Href` and `Url` objects. If you still need the string value, you can call `toString()`
* `MediaType` no longer has static helpers for sniffing it from a file or URL. Instead, you can use an `AssetRetriever` to retrieve the format of a file.

#### Navigator

* Version 3 includes a new component called `DirectionalNavigationAdapter` that replaces `EdgeTapNavigation`. This helper enables users to navigate between pages using arrow and space keys on their keyboard or by tapping the edge of the screen.
* The `onTap` and `onDrag` events of `VisualNavigator.Listener` have been deprecated. You can now use multiple implementations of `InputListener` with `VisualNavigator.addInputListener()`.

#### Streamer

* The `Streamer` object has been deprecated in favor of components with smaller responsibilities: `AssetRetriever` and `PublicationOpener`.

#### LCP

* `LcpService.acquirePublication()` is deprecated in favor of `LcpService.publicationRetriever()`, which provides greater flexibility thanks to the `DownloadManager`.
* The way the host view of a `LcpDialogAuthentication` is retrieved was changed to support Android configuration changes.

### Deprecated

* Both the Fuel and Kovenant libraries have been completely removed from the toolkit. With that, several deprecated functions have also been removed.

#### Shared

* The `putPublication` and `getPublication` helpers in `Intent` are deprecated. Now, it is the application's responsibility to pass `Publication` objects between activities and reopen them when necessary.

#### Navigator

* Fixed race conditions causing `ConcurrentModificationException` to be thrown when parsing an OPDS 2 feed.
* EPUB external links are no longer handled by the navigator. You need to open the link in your own Web View or Chrome Custom Tab.


## [2.4.0]
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ A [Test App](test-app) demonstrates how to integrate the Readium Kotlin toolkit

## Minimum Requirements

| Readium | Android min SDK | Android compile SDK | Kotlin compiler | Gradle |
|---------|-----------------|---------------------|-----------------|--------|
| 3.0.0 | 21 | 34 | 1.9.0 | 8.0.0 |
| 2.3.0 | 21 | 33 | 1.7.10 | 6.9.3 |
| Readium | Android min SDK | Android compile SDK | Kotlin compiler (✻) | Gradle (✻) |
|---------|-----------------|---------------------|---------------------|------------|
| 3.0.0 | 21 | 34 | 1.9.0 | 8.0.0 |
| 2.3.0 | 21 | 33 | 1.7.10 | 6.9.3 |

✻ Only required if you integrate Readium as a submodule instead of using Maven Central.

## Setting Up Readium

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/media-navigator.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ navigator.playback

val playingItem = navigator.readingOrder.items[playback.index]

if (playback.state is MediaNavigator.State.Error) {
if (playback.state is MediaNavigator.State.Failure) {
// Alert
}
}
Expand Down
92 changes: 54 additions & 38 deletions docs/guides/open-publication.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,58 @@
# Opening a publication

:warning: The described components are is still experimental.
:warning: The APIs described here may still undergo changes before the stable 3.0 release.

Readium requires you to instantiate a few components before you can actually open a publication.
To open a publication with Readium, you need to instantiate a couple of components: an `AssetRetriever` and a `PublicationOpener`.

## Constructing an `AssetRetriever`
## `AssetRetriever`

First, you need to instantiate an `HttpClient` to provide the toolkit the ability to do HTTP requests.
You can use the Readium `DefaultHttpClient` or a custom implementation. In the former case, its callback will
enable you to perform authentication when required.
Then, you can create an `AssetRetriever` which will enable you to read content through different schemes and guess its format.
In addition to an `HttpClient`, the `AssetRetriever` constructor takes a `ContentResolver` to support data access through the `content` scheme.
The `AssetRetriever` grants access to the content of an asset located at a given URL, such as a publication package, manifest, or LCP license.

### Constructing an `AssetRetriever`

You can create an instance of `AssetRetriever` with:

* A `ContentResolver` to support data access through the `content` URL scheme.
* An `HttpClient` to enable the toolkit to perform HTTP requests and support the `http` and `https` URL schemes. You can use `DefaultHttpClient` which provides callbacks for handling authentication when needed.

```kotlin
val httpClient = DefaultHttpClient()

val assetRetriever = AssetRetriever(context.contentResolver, httpClient)
```

## Constructing a `PublicationOpener`
### Retrieving an `Asset`

The component which can parse an `Asset` giving access to a publication to build a `Publication`
object is the `PublicationOpener`. Its constructor requires you to pass in:
With your fresh instance of `AssetRetriever`, you can open an `Asset` from an `AbsoluteUrl`.

* a `PublicationParser` it delegates the parsing work to.
* a list of `ContentProtection`s which will deal with DRMs.
```kotlin
// From a `File`
val url = File("...").toUrl()
// or from a content:// `Uri`
val url = contentUri.toAbsoluteUrl()
// or from a raw URL string
val url = AbsoluteUrl("https://domain/book.epub")

val asset = assetRetriever.retrieve(url)
.getOrElse { /* Failed to retrieve the Asset */ }
```

The easiest way to get a `PublicationParser` is to use the `DefaultPublicationParser` class. As to
`ContentProtection`s, you can get one to support LCP publications through your `LcpService` if you have one.
The `AssetRetriever` will sniff the media type of the asset, which you can store in your bookshelf database to speed up the process next time you retrieve the `Asset`. This will improve performance, especially with HTTP URL schemes.

```kotlin
val mediaType = asset.format.mediaType

// Speed up the retrieval with a known media type.
val asset = assetRetriever.retrieve(url, mediaType)
```

## `PublicationOpener`

`PublicationOpener` builds a `Publication` object from an `Asset` using:

* A `PublicationParser` to parse the asset structure and publication metadata.
* The `DefaultPublicationParser` handles all the formats supported by Readium out of the box.
* An optional list of `ContentProtection` to decrypt DRM-protected publications.
* If you support Readium LCP, you can get one from the `LcpService`.

```kotlin
val contentProtections = listOf(lcpService.contentProtection(authentication))
Expand All @@ -37,34 +62,25 @@ val publicationParser = DefaultPublicationParser(context, httpClient, assetRetri
val publicationOpener = PublicationOpener(publicationParser, contentProtections)
```

## Bringing the pieces together
### Opening a `Publication`

Once you have got an `AssetRetriever` and a `PublicationOpener`, you can eventually open a publication as follows:
```kotlin
val asset = assetRetriever.open(url, mediaType)
.getOrElse { return error }
Now that you have a `PublicationOpener` ready, you can use it to create a `Publication` from an `Asset` that was previously obtained using the `AssetRetriever`.

val publication = publicationOpener.open(asset)
.getOrElse { return error }
```
The `allowUserInteraction` parameter is useful when supporting Readium LCP. When enabled and using a `LcpDialogAuthentication`, the toolkit will prompt the user if the passphrase is missing.

Persisting the asset media type on the device can significantly improve performance as it is strong hint
for the content format, especially in case of remote publications.
```kotlin
val publication = publicationOpener.open(asset, allowUserInteraction = true)
.getOrElse { /* Failed to access or parse the publication */ }
```

## Supporting additional formats or URL schemes

`DefaultPublicationParser` accepts additional parsers. You can also use your own parser list
with `CompositePublicationParser` or implement [PublicationParser] in the way you like.

`AssetRetriever` offers an alternative constructor providing better extensibility in a similar way.
This constructor takes several parameters with different responsibilities.

* `ResourceFactory` determines which schemes you will be able to access content through.
* `ArchiveOpener` which kinds of archives your `AssetRetriever` will be able to open.
* `FormatSniffer` which file formats your `AssetRetriever` will be able to identify.
`DefaultPublicationParser` accepts additional parsers. You also have the option to use your own parser list by using `CompositePublicationParser` or create your own `PublicationParser` for a fully customized parsing resolution strategy.

For each of these components, you can either use the default implementations or implement yours
with the composite pattern. `CompositeResourceFactory`, `CompositeArchiveOpener` and `CompositeFormatSniffer`
provide simple implementations trying every item of a list in turns.
The `AssetRetriever` offers an additional constructor that provides greater extensibility options, using:

* `ResourceFactory` which handles the URL schemes through which you can access content.
* `ArchiveOpener` which determines the types of archives (ZIP, RAR, etc.) that can be opened by the `AssetRetriever`.
* `FormatSniffer` which identifies the file formats that `AssetRetriever` can recognize.

You can use either the default implementations or implement your own for each of these components using the composite pattern. The toolkit's `CompositeResourceFactory`, `CompositeArchiveOpener`, and `CompositeFormatSniffer` provide a simple resolution strategy.
2 changes: 1 addition & 1 deletion docs/guides/tts.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ If the device lacks the data necessary for the chosen voice, the user needs to m
```kotlin
navigator.playback
.onEach { playback ->
(playback?.state as? TtsNavigator.State.Error.EngineError<*>)
(playback?.state as? TtsNavigator.State.Failure.EngineError<*>)
?.let { it.error as? AndroidTtsEngine.Error.LanguageMissingData }
?.let { error ->
Timber.e("Missing data for language ${error.language}")
Expand Down
Loading

0 comments on commit 5b718ad

Please sign in to comment.