diff --git a/CHANGELOG.md b/CHANGELOG.md index d8eb227200..9a35bded3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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] diff --git a/README.md b/README.md index 8cd02b6f1d..ee5394019b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/guides/media-navigator.md b/docs/guides/media-navigator.md index 254dd6f688..4ee2f3fe19 100644 --- a/docs/guides/media-navigator.md +++ b/docs/guides/media-navigator.md @@ -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 } } diff --git a/docs/guides/open-publication.md b/docs/guides/open-publication.md index e799043423..6a2a1daa9a 100644 --- a/docs/guides/open-publication.md +++ b/docs/guides/open-publication.md @@ -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)) @@ -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. diff --git a/docs/guides/tts.md b/docs/guides/tts.md index 31b7a81928..1271332f22 100644 --- a/docs/guides/tts.md +++ b/docs/guides/tts.md @@ -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}") diff --git a/docs/migration-guide.md b/docs/migration-guide.md index a6423afacc..0ddcaefe94 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -2,36 +2,61 @@ All migration steps necessary in reading apps to upgrade to major versions of the Kotlin Readium toolkit will be documented in this file. - +## 3.0.0-alpha.1 -## 2.4.0 +First of all, upgrade to version 2.4.0 and resolve any deprecation notices. This will help you avoid troubles, as the APIs that were deprecated in version 2.x have been removed in version 3.0. -### Maven Central +### Minimum requirements -Readium is now distributed with [Maven Central](https://search.maven.org/search?q=g:org.readium.kotlin-toolkit). You must update your Gradle configuration. +If you integrate Readium 3.0 as a submodule, it requires Kotlin 1.9.0 and Gradle 8.0.0. You should start by updating these dependencies in your application. -```diff -allprojects { - repositories { -- maven { url 'https://jitpack.io' } -+ mavenCentral() - } -} -``` +#### Targeting Android SDK 34 -The group ID of the Readium modules is now `org.readium.kotlin-toolkit`, for instance: +The modules now target Android SDK 34. If your app also targets it, you will need the `FOREGROUND_SERVICE_MEDIA_PLAYBACK` permission in your `AndroidManifest.xml` file to use TTS and audiobook playback. -```groovy -dependencies { - implementation "org.readium.kotlin-toolkit:readium-shared:$readium_version" - implementation "org.readium.kotlin-toolkit:readium-streamer:$readium_version" - implementation "org.readium.kotlin-toolkit:readium-navigator:$readium_version" - implementation "org.readium.kotlin-toolkit:readium-opds:$readium_version" - implementation "org.readium.kotlin-toolkit:readium-lcp:$readium_version" -} +### `Publication` + +#### Opening a `Publication` + +The `Streamer` object has been deprecated in favor of components with smaller responsibilities: + +* `AssetRetriever` grants access to the content of an asset located at a given URL, such as a publication package, manifest, or LCP license +* `PublicationOpener` uses a publication parser and a set of content protections to create a `Publication` object from an `Asset`. + +[See the user guide for a detailed explanation on how to use these new APIs](guides/open-publication.md). + +#### Sharing `Publication` across Android activities + +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. + +You can take a look at the [`ReaderRepository` in the Test App](https://github.com/readium/kotlin-toolkit/blob/09e338b0f3acc8d59282280bded6c4bf93de6281/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderRepository.kt#L46) for inspiration. + +Alternatively, you can copy [the deprecated helpers](https://github.com/readium/kotlin-toolkit/blob/7649378a0a6b924abedf8b72372c3808fe9b992f/readium/shared/src/main/java/org/readium/r2/shared/extensions/Intent.kt#L26) and add them to your codebase. However, please note that this approach is discouraged because it will not handle configuration changes smoothly. + +### `MediaType` + +#### Sniffing a `MediaType` + +`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. + +```kotlin +val httpClient = DefaultHttpClient() +val assetRetriever = AssetRetriever(context.contentResolver, httpClient) + +val mediaType = assetRetriever.sniffFormat(File(...)) + .getOrElse { /* Failed to access the asset or recognize its format */ } + .mediaType ``` -### Migration of HREFs and Locators (bookmarks, annotations, etc.) +### HREFs + +#### `Link.href` and `Locator.href` are not strings anymore + +`Link.href` and `Locator.href` are now respectively `Href` and `Url` objects. If you still need the string value, you can call `toString()`, but you may find the `Url` objects more useful in practice. + +Use `link.url()` to get a `Url` from a `Link` object. + +#### Migration of HREFs and Locators (bookmarks, annotations, etc.) :warning: This requires a database migration in your application, if you were persisting `Locator` objects. @@ -80,29 +105,235 @@ val MIGRATION_HREF = object : Migration(1, 2) { } ``` -### LcpDialogAuthentication now supports configuration changes. +### Error management -You no longer need to pass an activity, fragment or view as `sender` to `retrievePassphrase`. -Instead, call `onParentViewAttachedToWindow` every time you have a -view attached to a window available as anchor and `onParentViewDetachedFromWindow` every time it -gets detached. You can monitor these events by setting a `View.OnAttachStateChangeListener` on your -view. +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. -See the [test-app](https://github.com/readium/kotlin-toolkit/blob/01d6c7936accea2d6b953d435e669260676e8c99/test-app/src/main/java/org/readium/r2/testapp/bookshelf/BookshelfFragment.kt#L68) -for an example. +It is recommended to handle `Error` objects using a `when` statement. However, if you still need an `Exception`, you may wrap an `Error` with `ErrorException`, for example: -### All resources now have the prefix `readium_`. +```kotlin +assetRetriever.sniffFormat(...) + .getOrElse { throw ErrorException(it) } +``` +`UserException` is also deprecated. The application now needs to provide localized error messages for toolkit errors. -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. Here is a comprehensive list of the changes. +### Navigator -### Pdfium and PSPDFKit adapters' namespaces slightly changed +#### Click on external links in the EPUB navigator -`org.readium.adapters.pspdfkit.document` moved to `org.readium.adapter.pdspdfKit.document` -`org.readium.adapters.pspdfkit.navigator` moved to `org.readium.adapter.pdspdfKit.navigator` -`org.readium.adapters.pdfium.document` moved to `org.readium.adapter.pdfium.document` -`org.readium.adapters.pdfium.navigator` moved to `org.readium.adapter.pdfium.navigator` +Clicking on external links is no longer managed by the EPUB navigator. To open the link yourself, override `HyperlinkNavigator.Listener.onExternalLinkActivated`, for example: + +```kotlin +override fun onExternalLinkActivated(url: AbsoluteUrl) { + if (!url.isHttp) return + val context = requireActivity() + val uri = url.toUri() + try { + CustomTabsIntent.Builder() + .build() + .launchUrl(context, uri) + } catch (e: ActivityNotFoundException) { + context.startActivity(Intent(Intent.ACTION_VIEW, uri)) + } +} +``` -#### Layouts +##### Edge tap and keyboard navigation + +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. + +As it implements `InputListener`, you can attach it to any `OverflowableNavigator`. + +```kotlin +navigator.addInputListener( + DirectionalNavigationAdapter( + navigator, + animatedTransition = true + ) +) +``` + +The `DirectionalNavigationAdapter` provides plenty of customization options. Please refer to its API for more details. + +#### Tap and drag events + +The `onTap` and `onDrag` events of `VisualNavigator.Listener` have been deprecated. You can now use multiple implementations of `InputListener`. The order is important when events are consumed. + +```kotlin +navigator.addInputListener(DirectionalNavigationAdapter(navigator)) + +navigator.addInputListener(object : InputListener { + override fun onTap(event: TapEvent): Boolean { + toggleUi() + return true + } +}) +``` + +### LCP + +#### Creating an `LcpService` + +The `LcpService` now requires an instance of `AssetRetriever` and `DownloadManager` during construction. To get the same behavior as before, you can use a `ForegroundDownloadManager`. If you want to support downloads in the background instead, take a look at `AndroidDownloadManager`. + +```kotlin +val lcpService = LcpService( + context, + assetRetriever = assetRetriever, + downloadManager = ForegroundDownloadManager( + httpClient = httpClient, + downloadsDirectory = File(context.cacheDir, "lcp") + ) +) +``` + +#### Downloading an LCP protected publication from a license + +`LcpService.acquirePublication()` is deprecated in favor of `LcpService.publicationRetriever()`, which provides greater flexibility thanks to the `DownloadManager`. + +```kotlin +// 1. Open an `Asset` from a `File`. +val asset = assetRetriever.retrieve(file) + .getOrElse { /* Failed to open the file or sniff its format */ } + +// 2. Verify that it is an LCP License Document. +if (asset is ResourceAsset && asset.format.conformsTo(LcpLicenseSpecification)) { + // 3. Parse the LCP License Document from its JSON representation. + val license = lcplAsset.resource.read() + .getOrElse { /* Failed to read the content of the LCPL asset */ } + .let { LicenseDocument.fromBytes(it) } + .getOrElse { /* Failed to parse a valid LCP License Document from the the raw bytes */ } + + // 4. Download the publication using the `LcpPublicationRetriever`. + // The returned `requestId` can be used to cancel an on-going download, or to resume a download + // with `LcpPublicationRetriever.register()`, if it was downloaded in the background. + val requestId = lcpService.publicationRetriever() + .retrieve(license, listener = object : LcpPublicationRetriever.Listener { + override fun onAcquisitionCompleted( + requestId: LcpPublicationRetriever.RequestId, + acquiredPublication: LcpService.AcquiredPublication + ) { + } + + override fun onAcquisitionProgressed( + requestId: LcpPublicationRetriever.RequestId, + downloaded: Long, + expected: Long? + ) { + // Report progress. + } + + override fun onAcquisitionFailed( + requestId: LcpPublicationRetriever.RequestId, + error: LcpError + ) { + // Report error. + } + + override fun onAcquisitionCancelled(requestId: LcpPublicationRetriever.RequestId) { + // Handle cancellation. + } + }) +} +``` + +If you are using a `ForegroundDownloadManager` and **not supporting background downloads**, you can use this helper to have a similar API as Readium 2.x with coroutines. + +```kotlin +suspend fun LcpService.acquirePublication( + lcplAsset: ResourceAsset, + onProgress: (Double) -> Unit +): Try { + require(lcplAsset.format.conformsTo(LcpLicenseSpecification)) + + val license = lcplAsset.resource.read() + .flatMap { LicenseDocument.fromBytes(it) } + .getOrElse { return Try.failure(it) } + + return suspendCancellableCoroutine { cont -> + publicationRetriever().retrieve(license, object : LcpPublicationRetriever.Listener { + override fun onAcquisitionCompleted( + requestId: LcpPublicationRetriever.RequestId, + acquiredPublication: LcpService.AcquiredPublication + ) { + cont.resume(Try.success(acquiredPublication)) + } + + override fun onAcquisitionProgressed( + requestId: LcpPublicationRetriever.RequestId, + downloaded: Long, + expected: Long? + ) { + expected ?: return + onProgress(downloaded.toDouble() / expected.toDouble()) + } + + override fun onAcquisitionFailed( + requestId: LcpPublicationRetriever.RequestId, + error: LcpError + ) { + cont.resume(Try.failure(error)) + } + + override fun onAcquisitionCancelled(requestId: LcpPublicationRetriever.RequestId) { + cont.cancel() + } + }) + } +} +``` + +#### `LcpDialogAuthentication` updated to support configuration changes + +The way the host view of a `LcpDialogAuthentication` is retrieved was changed to support Android configuration changes. You no longer need to pass an activity, fragment or view as `sender` parameter. + +Instead, call on your instance of `LcpDialogAuthentication`: +* `onParentViewAttachedToWindow` every time you have a view attached to a window available as anchor +* `onParentViewDetachedFromWindow` every time it gets detached + +You can monitor these events by setting a `View.OnAttachStateChangeListener` on your view. [See the Test App for an example](https://github.com/readium/kotlin-toolkit/blob/01d6c7936accea2d6b953d435e669260676e8c99/test-app/src/main/java/org/readium/r2/testapp/bookshelf/BookshelfFragment.kt#L68). + +### Removal of Fuel and Kovenant + +Both the Fuel and Kovenant libraries have been completely removed from the toolkit. With that, several deprecated functions have also been removed. + +### Resources + +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. + +#### Deleted resources + +If you referenced these resources, you need to remove them from your application or copy them to your own resources. + +##### Deleted colors + +| Name | +|-----------------------------| +| `colorPrimary` | +| `colorPrimaryDark` | +| `colorAccent` | +| `colorAccentPrefs` | +| `snackbar_background_color` | +| `snackbar_text_color` | + +##### Deleted strings + +| Name | +|----------------------------| +| `end_of_chapter` | +| `end_of_chapter_indicator` | +| `zero` | +| `epub_navigator_tag` | +| `image_navigator_tag` | +| `snackbar_text_color` | + +All the localized error messages are also removed. + +#### Renamed resources + +If you used the resources listed below, you must rename the references to reflect the new names. You can use a global search to help you find the references in your project. + +##### Renamed layouts | Deprecated | New | |-----------------------------|-----------------------------------------------| @@ -114,13 +345,13 @@ To avoid conflicts when merging your app resources, all resources declared in th | `viewpager_fragment_cbz` | `readium_navigator_viewpager_fragment_cbz` | | `viewpager_fragment_epub` | `readium_navigator_viewpager_fragment_epub` | -#### Dimensions +##### Renamed dimensions | Deprecated | New | |--------------------------------------|-------------------------------------------| | `r2_navigator_epub_vertical_padding` | `readium_navigator_epub_vertical_padding` | -#### Strings +##### Renamed strings | Deprecated | New | |---------------------------------------------|--------------------------------------------------| @@ -137,94 +368,41 @@ To avoid conflicts when merging your app resources, all resources declared in th | `r2_media_notification_channel_description` | `readium_media_notification_channel_description` | | `r2_media_notification_channel_name` | `readium_media_notification_channel_name` | -#### Drawables +##### Renamed drawables | Deprecated | New | |-----------------------------------------|----------------------------------------------| | `r2_media_notification_fastforward.xml` | `readium_media_notification_fastforward.xml` | | `r2_media_notification_rewind.xml` | `readium_media_notification_rewind.xml` | -### Publication assets - -In most cases, you no longer need to manually create a `PublicationAsset` to open a publication with -the streamer. You can use the overloaded open method taking a `Url` as argument instead. -```kotlin -streamer.open(file.toUrl(), ...) -``` - -### Tap and drag events +## 2.4.0 -The `VisualNavigator.Listener`'s `onTap` and `onDrag` events are deprecated. You can now provide multiple implementations of `InputListener`, and the order matters if events are consumed. +### Maven Central -```kotlin -navigator.addInputListener(DirectionalNavigationAdapter()) +Readium is now distributed with [Maven Central](https://search.maven.org/search?q=g:org.readium.kotlin-toolkit). You must update your Gradle configuration. -navigator.addInputListener(object : InputListener { - override fun onTap(navigator: VisualNavigator, event: TapEvent): Boolean { - toggleUi() - return true +```diff +allprojects { + repositories { +- maven { url 'https://jitpack.io' } ++ mavenCentral() } -}) +} ``` -### New navigator interfaces - -An important part of `VisualNavigator` was moved to `DirectionalNavigator`. A new `HyperlinkNavigator` -interface implemented by the `EpubFragmentNavigator` provides the ability to intercept clicks on -links to both internal and external URLs. Opening external URLs is no longer handled by the navigator, -that's something you should do by yourself in the way you like. - -### Edge tap and keyboard navigation - -Version 3.0.0 ships with a new `DirectionalNavigationAdapter` component replacing `EdgeTapNavigation`. This helper allows users to turn pages with arrow and space keys on their keyboard or by tapping the edge of the screen. - -It's easy to set it up with an implementation of `DirectionalNavigator`, as it implements `InputListener`. +The group ID of the Readium modules is now `org.readium.kotlin-toolkit`, for instance: -```kotlin -navigator.addInputListener( - DirectionalNavigationAdapter( - navigator, - animatedTransition = true - ) -) +```groovy +dependencies { + implementation "org.readium.kotlin-toolkit:readium-shared:$readium_version" + implementation "org.readium.kotlin-toolkit:readium-streamer:$readium_version" + implementation "org.readium.kotlin-toolkit:readium-navigator:$readium_version" + implementation "org.readium.kotlin-toolkit:readium-opds:$readium_version" + implementation "org.readium.kotlin-toolkit:readium-lcp:$readium_version" +} ``` -`DirectionalNavigationAdapter` offers a lot of customization options. Take a look at its API. - -### Removal of Fuel and Kovenant - -Both the Fuel and Kovenant libraries have been completely removed from the toolkit. With that, several deprecated functions have also been removed. - -#### opds/OPDS1Parser - -* Both `parseURL(url: URL)` and `parseURL(headers: MutableMap, url: URL)` have been replaced with `parseUrlString(url: String, client: HttpClient = DefaultHttpClient())`. -* `fetchOpenSearchTemplate(feed: Feed)` has been replaced with `retrieveOpenSearchTemplate(feed: Feed)`. - -#### opds/OPDS2Parser - -* Both `parseURL(url: URL)` and `parseURL(headers: MutableMap, url: URL)` have been replaced with `parseUrlString(url: String, client: HttpClient = DefaultHttpClient())`. - -#### shared/FuelPromiseExtension - -* `Request.promise()` - -#### shared/format/Deprecated - -* `Response.sniffFormat` has been replaced with `org.readium.r2.shared.util.mediatype.sniffMediaType` - -#### shared/util/mediatype/Extensions - -* `Response.sniffMediaType(...)` has been replaced with `org.readium.r2.shared.util.mediatype.sniffMediaType` - -### Targeting Android SDK 34 - -The modules now target Android SDK 34. If your app also targets that, you will need the `FOREGROUND_SERVICE_MEDIA_PLAYBACK` permission in you AndroidManifest.xml file in order to use TTS and audiobook playback. - -### Gradle 8.0 - -The minimum required Gradle version is now 8.0. - ## 2.3.0 ### `Decoration.extras` diff --git a/readium/adapters/exoplayer/audio/src/main/java/org/readium/adapter/exoplayer/audio/ExoPlayerEngine.kt b/readium/adapters/exoplayer/audio/src/main/java/org/readium/adapter/exoplayer/audio/ExoPlayerEngine.kt index 990c510798..d7dfcda84b 100644 --- a/readium/adapters/exoplayer/audio/src/main/java/org/readium/adapter/exoplayer/audio/ExoPlayerEngine.kt +++ b/readium/adapters/exoplayer/audio/src/main/java/org/readium/adapter/exoplayer/audio/ExoPlayerEngine.kt @@ -252,6 +252,6 @@ public class ExoPlayerEngine private constructor( Player.STATE_READY -> AudioEngine.State.Ready Player.STATE_BUFFERING -> AudioEngine.State.Buffering Player.STATE_ENDED -> AudioEngine.State.Ended - else -> AudioEngine.State.Error(Error(playerError!!)) + else -> AudioEngine.State.Failure(Error(playerError!!)) } } diff --git a/readium/adapters/pdfium/document/src/main/java/org/readium/adapters/pdfium/document/Deprecated.kt b/readium/adapters/pdfium/document/src/main/java/org/readium/adapters/pdfium/document/Deprecated.kt new file mode 100644 index 0000000000..fbbe24936d --- /dev/null +++ b/readium/adapters/pdfium/document/src/main/java/org/readium/adapters/pdfium/document/Deprecated.kt @@ -0,0 +1,15 @@ +package org.readium.adapters.pdfium.document + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.document.PdfiumDocument"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumDocument = org.readium.adapter.pdfium.document.PdfiumDocument + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.document.PdfiumDocumentFactory"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumDocumentFactory = org.readium.adapter.pdfium.document.PdfiumDocumentFactory diff --git a/readium/adapters/pdfium/navigator/src/main/java/org/readium/adapters/pdfium/navigator/Deprecated.kt b/readium/adapters/pdfium/navigator/src/main/java/org/readium/adapters/pdfium/navigator/Deprecated.kt new file mode 100644 index 0000000000..4c2109f269 --- /dev/null +++ b/readium/adapters/pdfium/navigator/src/main/java/org/readium/adapters/pdfium/navigator/Deprecated.kt @@ -0,0 +1,82 @@ +@file:OptIn(ExperimentalReadiumApi::class) + +package org.readium.adapters.pdfium.navigator + +import org.readium.r2.shared.ExperimentalReadiumApi + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumEngineProvider"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumEngineProvider = org.readium.adapter.pdfium.navigator.PdfiumEngineProvider + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumDefaults"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumDefaults = org.readium.adapter.pdfium.navigator.PdfiumDefaults + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumPreferences"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumPreferences = org.readium.adapter.pdfium.navigator.PdfiumPreferences + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumPreferencesEditor"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumPreferencesEditor = org.readium.adapter.pdfium.navigator.PdfiumPreferencesEditor + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumSettings"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumSettings = org.readium.adapter.pdfium.navigator.PdfiumSettings + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumDocumentFragment"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumDocumentFragment = org.readium.adapter.pdfium.navigator.PdfiumDocumentFragment + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumNavigatorFactory"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumNavigatorFactory = org.readium.adapter.pdfium.navigator.PdfiumNavigatorFactory + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumNavigatorFragment"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumNavigatorFragment = org.readium.adapter.pdfium.navigator.PdfiumNavigatorFragment + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumPreferencesSerializer"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumPreferencesSerializer = org.readium.adapter.pdfium.navigator.PdfiumPreferencesSerializer + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumPublicationPreferencesFilter"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumPublicationPreferencesFilter = org.readium.adapter.pdfium.navigator.PdfiumPublicationPreferencesFilter + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pdfium.navigator.PdfiumSharedPreferencesFilter"), + level = DeprecationLevel.ERROR +) +public typealias PdfiumSharedPreferencesFilter = org.readium.adapter.pdfium.navigator.PdfiumSharedPreferencesFilter diff --git a/readium/adapters/pspdfkit/document/src/main/java/org/readium/adapters/pspdfkit/document/Deprecated.kt b/readium/adapters/pspdfkit/document/src/main/java/org/readium/adapters/pspdfkit/document/Deprecated.kt new file mode 100644 index 0000000000..d66a1b220b --- /dev/null +++ b/readium/adapters/pspdfkit/document/src/main/java/org/readium/adapters/pspdfkit/document/Deprecated.kt @@ -0,0 +1,15 @@ +package org.readium.adapters.pspdfkit.document + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.document.PsPdfKitDocument"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitDocument = org.readium.adapter.pspdfkit.document.PsPdfKitDocument + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.document.PsPdfKitDocumentFactory"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitDocumentFactory = org.readium.adapter.pspdfkit.document.PsPdfKitDocumentFactory diff --git a/readium/adapters/pspdfkit/navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/Deprecated.kt b/readium/adapters/pspdfkit/navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/Deprecated.kt new file mode 100644 index 0000000000..bbdd9efb49 --- /dev/null +++ b/readium/adapters/pspdfkit/navigator/src/main/java/org/readium/adapters/pspdfkit/navigator/Deprecated.kt @@ -0,0 +1,82 @@ +@file:OptIn(ExperimentalReadiumApi::class) + +package org.readium.adapters.pspdfkit.navigator + +import org.readium.r2.shared.ExperimentalReadiumApi + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitEngineProvider"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitEngineProvider = org.readium.adapter.pspdfkit.navigator.PsPdfKitEngineProvider + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitDefaults"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitDefaults = org.readium.adapter.pspdfkit.navigator.PsPdfKitDefaults + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitPreferences"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitPreferences = org.readium.adapter.pspdfkit.navigator.PsPdfKitPreferences + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitPreferencesEditor"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitPreferencesEditor = org.readium.adapter.pspdfkit.navigator.PsPdfKitPreferencesEditor + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitSettings"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitSettings = org.readium.adapter.pspdfkit.navigator.PsPdfKitSettings + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitDocumentFragment"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitDocumentFragment = org.readium.adapter.pspdfkit.navigator.PsPdfKitDocumentFragment + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitNavigatorFactory"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitNavigatorFactory = org.readium.adapter.pspdfkit.navigator.PsPdfKitNavigatorFactory + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitNavigatorFragment"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitNavigatorFragment = org.readium.adapter.pspdfkit.navigator.PsPdfKitNavigatorFragment + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitPreferencesSerializer"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitPreferencesSerializer = org.readium.adapter.pspdfkit.navigator.PsPdfKitPreferencesSerializer + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitPublicationPreferencesFilter"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitPublicationPreferencesFilter = org.readium.adapter.pspdfkit.navigator.PsPdfKitPublicationPreferencesFilter + +@Deprecated( + "Moved to another package", + ReplaceWith("org.readium.adapter.pspdfkit.navigator.PsPdfKitSharedPreferencesFilter"), + level = DeprecationLevel.ERROR +) +public typealias PsPdfKitSharedPreferencesFilter = org.readium.adapter.pspdfkit.navigator.PsPdfKitSharedPreferencesFilter diff --git a/readium/navigator-media2/src/main/java/org/readium/navigator/media2/MediaNavigator.kt b/readium/navigator-media2/src/main/java/org/readium/navigator/media2/MediaNavigator.kt index 8fa001185d..9534a6eaf5 100644 --- a/readium/navigator-media2/src/main/java/org/readium/navigator/media2/MediaNavigator.kt +++ b/readium/navigator-media2/src/main/java/org/readium/navigator/media2/MediaNavigator.kt @@ -159,8 +159,8 @@ public class MediaNavigator private constructor( val state = when (currentState) { SessionPlayerState.Playing -> Playback.State.Playing - SessionPlayerState.Idle, SessionPlayerState.Error -> - Playback.State.Error + SessionPlayerState.Idle, SessionPlayerState.Failure -> + Playback.State.Failure SessionPlayerState.Paused -> if (playerCallback.playbackCompleted) { Playback.State.Finished @@ -341,7 +341,7 @@ public class MediaNavigator private constructor( Playing, Paused, Finished, - Error + Failure } public data class Resource( diff --git a/readium/navigator-media2/src/main/java/org/readium/navigator/media2/SessionPlayerHelpers.kt b/readium/navigator-media2/src/main/java/org/readium/navigator/media2/SessionPlayerHelpers.kt index c2b76af0fe..b8750c2fbe 100644 --- a/readium/navigator-media2/src/main/java/org/readium/navigator/media2/SessionPlayerHelpers.kt +++ b/readium/navigator-media2/src/main/java/org/readium/navigator/media2/SessionPlayerHelpers.kt @@ -18,14 +18,14 @@ internal enum class SessionPlayerState { Idle, Paused, Playing, - Error; + Failure; companion object { fun fromCode(sessionPlayerState: Int) = when (sessionPlayerState) { SessionPlayer.PLAYER_STATE_IDLE -> Idle SessionPlayer.PLAYER_STATE_PAUSED -> Paused SessionPlayer.PLAYER_STATE_PLAYING -> Playing - else -> Error // SessionPlayer.PLAYER_STATE_ERROR + else -> Failure // SessionPlayer.PLAYER_STATE_ERROR } } } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/HyperlinkNavigator.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/HyperlinkNavigator.kt index 7fc8e73667..14b5c23f19 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/HyperlinkNavigator.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/HyperlinkNavigator.kt @@ -33,6 +33,26 @@ public interface HyperlinkNavigator : Navigator { /** * Called when a link to an external URL was activated in the navigator. + * + * If it is an HTTP URL, you should open it with a `CustomTabsIntent` or `WebView`, for + * example: + * + * ```kotlin + * override fun onExternalLinkActivated(url: AbsoluteUrl) { + * if (!url.isHttp) return + * + * val context = requireActivity() + * val uri = url.toUri() + * + * try { + * CustomTabsIntent.Builder() + * .build() + * .launchUrl(context, uri) + * } catch (e: ActivityNotFoundException) { + * context.startActivity(Intent(Intent.ACTION_VIEW, uri)) + * } + * } + * ``` */ @ExperimentalReadiumApi public fun onExternalLinkActivated(url: AbsoluteUrl) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/util/EdgeTapNavigation.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/util/EdgeTapNavigation.kt index 1e32ad90d9..63514b88a0 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/util/EdgeTapNavigation.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/util/EdgeTapNavigation.kt @@ -1,7 +1,7 @@ package org.readium.r2.navigator.util @Deprecated( - "Replaced by [DirectionalNavigationAdapter].", + "Replaced by `DirectionalNavigationAdapter`. See the migration guide.", replaceWith = ReplaceWith("DirectionalNavigationAdapter"), level = DeprecationLevel.ERROR ) diff --git a/readium/navigators/media/audio/src/main/java/org/readium/navigator/media/audio/AudioEngine.kt b/readium/navigators/media/audio/src/main/java/org/readium/navigator/media/audio/AudioEngine.kt index 829fa734c5..67be3ebec8 100644 --- a/readium/navigators/media/audio/src/main/java/org/readium/navigator/media/audio/AudioEngine.kt +++ b/readium/navigators/media/audio/src/main/java/org/readium/navigator/media/audio/AudioEngine.kt @@ -47,7 +47,7 @@ public interface AudioEngine (val error: E) : MediaNavigator.State.Error + public data class Failure (val error: E) : MediaNavigator.State.Failure } private val coroutineScope: CoroutineScope = @@ -173,6 +173,6 @@ public class AudioNavigator State.Ready is AudioEngine.State.Ended -> State.Ended is AudioEngine.State.Buffering -> State.Buffering - is AudioEngine.State.Error -> State.Error(error) + is AudioEngine.State.Failure -> State.Failure(error) } } diff --git a/readium/navigators/media/common/src/main/java/org/readium/navigator/media/common/MediaNavigator.kt b/readium/navigators/media/common/src/main/java/org/readium/navigator/media/common/MediaNavigator.kt index 7c0114fd95..aa198291cf 100644 --- a/readium/navigators/media/common/src/main/java/org/readium/navigator/media/common/MediaNavigator.kt +++ b/readium/navigators/media/common/src/main/java/org/readium/navigator/media/common/MediaNavigator.kt @@ -53,7 +53,7 @@ public interface MediaNavigator< /** * The navigator cannot play because an error occurred. */ - public interface Error : State + public interface Failure : State } /** diff --git a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsNavigator.kt b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsNavigator.kt index 2ae114ed3f..f0a21bdb09 100644 --- a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsNavigator.kt +++ b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsNavigator.kt @@ -79,7 +79,7 @@ public class TtsNavigator, public object Ended : MediaNavigator.State.Ended - public data class Error(val error: TtsNavigator.Error) : MediaNavigator.State.Error + public data class Failure(val error: Error) : MediaNavigator.State.Failure } public sealed class Error( @@ -183,13 +183,13 @@ public class TtsNavigator, when (this) { TtsPlayer.State.Ready -> State.Ready TtsPlayer.State.Ended -> State.Ended - is TtsPlayer.State.Error -> this.toError() + is TtsPlayer.State.Failure -> this.toError() } - private fun TtsPlayer.State.Error.toError(): State.Error = + private fun TtsPlayer.State.Failure.toError(): State.Failure = when (this) { - is TtsPlayer.State.Error.ContentError -> State.Error(Error.ContentError(error)) - is TtsPlayer.State.Error.EngineError<*> -> State.Error(Error.EngineError(error)) + is TtsPlayer.State.Failure.Content -> State.Failure(Error.ContentError(error)) + is TtsPlayer.State.Failure.Engine<*> -> State.Failure(Error.EngineError(error)) } private fun TtsPlayer.Utterance.toPosition(): Location { diff --git a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsPlayer.kt b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsPlayer.kt index 3d56b5c122..b934be68bb 100644 --- a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsPlayer.kt +++ b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsPlayer.kt @@ -117,11 +117,11 @@ internal class TtsPlayer, /** * The player cannot play because an error occurred. */ - sealed class Error : State { + sealed class Failure : State { - data class EngineError (val error: E) : Error() + data class Engine (val error: E) : Failure() - data class ContentError(val error: org.readium.r2.shared.util.Error) : Error() + data class Content(val error: Error) : Failure() } } @@ -521,7 +521,7 @@ internal class TtsPlayer, private fun onEngineError(error: E) { playbackMutable.value = playbackMutable.value.copy( - state = State.Error.EngineError(error) + state = State.Failure.Engine(error) ) playbackJob?.cancel() } @@ -538,7 +538,7 @@ internal class TtsPlayer, private fun onContentError(error: Error) { playbackMutable.value = playbackMutable.value.copy( - state = State.Error.ContentError(error) + state = State.Failure.Content(error) ) playbackJob?.cancel() } diff --git a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/android/AndroidTtsEngine.kt b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/android/AndroidTtsEngine.kt index ca6194aa5c..0639f30306 100644 --- a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/android/AndroidTtsEngine.kt +++ b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/android/AndroidTtsEngine.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.flow.asStateFlow import org.readium.navigator.media.tts.TtsEngine import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.extensions.tryOrNull -import org.readium.r2.shared.util.Error import org.readium.r2.shared.util.Language /* @@ -239,7 +238,7 @@ public class AndroidTtsEngine private constructor( val pendingRequests: MutableList = mutableListOf() ) : State() - data class Error( + data class Failure( val error: AndroidTtsEngine.Error ) : State() } @@ -290,7 +289,7 @@ public class AndroidTtsEngine private constructor( is State.WaitingForService -> { stateNow.pendingRequests.add(request) } - is State.Error -> { + is State.Failure -> { tryReconnect(request) } is State.EngineAvailable -> { @@ -307,7 +306,7 @@ public class AndroidTtsEngine private constructor( is State.EngineAvailable -> { stateNow.engine.stop() } - is State.Error -> { + is State.Failure -> { // Do nothing } is State.WaitingForService -> { @@ -331,7 +330,7 @@ public class AndroidTtsEngine private constructor( is State.EngineAvailable -> { cleanEngine(stateNow.engine) } - is State.Error -> { + is State.Failure -> { // Do nothing } is State.WaitingForService -> { @@ -373,7 +372,7 @@ public class AndroidTtsEngine private constructor( private fun onReconnectionFailed() { val previousState = state as State.WaitingForService val error = Error.Service - state = State.Error(error) + state = State.Failure(error) for (request in previousState.pendingRequests) { utteranceListener?.onError(request.id, error) diff --git a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsSessionAdapter.kt b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsSessionAdapter.kt index 2248ac079a..46bdb2bcc7 100644 --- a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsSessionAdapter.kt +++ b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsSessionAdapter.kt @@ -265,7 +265,7 @@ internal class TtsSessionAdapter( } override fun getPlayerError(): PlaybackException? { - return (lastPlayback.state as? TtsPlayer.State.Error)?.toPlaybackException() + return (lastPlayback.state as? TtsPlayer.State.Failure)?.toPlaybackException() } override fun play() { @@ -777,15 +777,15 @@ internal class TtsSessionAdapter( playbackInfo: TtsPlayer.Playback // playWhenReadyChangeReason: @Player.PlayWhenReadyChangeReason Int, ) { - if (previousPlaybackInfo.state as? TtsPlayer.State.Error != playbackInfo.state as? Error) { + if (previousPlaybackInfo.state as? TtsPlayer.State.Failure != playbackInfo.state as? Error) { listeners.queueEvent( EVENT_PLAYER_ERROR ) { listener: Listener -> listener.onPlayerErrorChanged( - (playbackInfo.state as? TtsPlayer.State.Error)?.toPlaybackException() + (playbackInfo.state as? TtsPlayer.State.Failure)?.toPlaybackException() ) } - if (playbackInfo.state is TtsPlayer.State.Error) { + if (playbackInfo.state is TtsPlayer.State.Failure) { listeners.queueEvent( EVENT_PLAYER_ERROR ) { listener: Listener -> @@ -915,15 +915,15 @@ internal class TtsSessionAdapter( private val TtsPlayer.State.playerCode get() = when (this) { TtsPlayer.State.Ready -> STATE_READY TtsPlayer.State.Ended -> STATE_ENDED - is TtsPlayer.State.Error -> STATE_IDLE + is TtsPlayer.State.Failure -> STATE_IDLE } @Suppress("Unchecked_cast") - private fun TtsPlayer.State.Error.toPlaybackException(): PlaybackException = when (this) { - is TtsPlayer.State.Error.EngineError<*> -> { + private fun TtsPlayer.State.Failure.toPlaybackException(): PlaybackException = when (this) { + is TtsPlayer.State.Failure.Engine<*> -> { mapEngineError(error as E) } - is TtsPlayer.State.Error.ContentError -> { + is TtsPlayer.State.Failure.Content -> { val errorCode = when (error) { is ReadError.Access -> when (error.cause) { diff --git a/readium/opds/src/main/java/org/readium/r2/opds/OPDS1Parser.kt b/readium/opds/src/main/java/org/readium/r2/opds/OPDS1Parser.kt index 3660d73264..d7eb47cd2f 100644 --- a/readium/opds/src/main/java/org/readium/r2/opds/OPDS1Parser.kt +++ b/readium/opds/src/main/java/org/readium/r2/opds/OPDS1Parser.kt @@ -9,6 +9,7 @@ package org.readium.r2.opds +import java.net.URL import org.joda.time.DateTime import org.readium.r2.shared.extensions.toList import org.readium.r2.shared.extensions.toMap @@ -74,6 +75,15 @@ public class OPDS1Parser { } } + @Suppress("UNUSED_PARAMETER") + @Deprecated( + "Provide an instance of `Url` instead", + ReplaceWith("parse(jsonData, url.toUrl()!!)"), + DeprecationLevel.ERROR + ) + public fun parse(jsonData: ByteArray, url: URL): ParseData = + throw NotImplementedError() + private fun parseFeed(root: ElementNode, url: Url): Feed { val feedTitle = root.getFirst("title", Namespaces.Atom)?.text ?: throw Exception(OPDSParserError.MissingTitle.name) diff --git a/readium/opds/src/main/java/org/readium/r2/opds/OPDS2Parser.kt b/readium/opds/src/main/java/org/readium/r2/opds/OPDS2Parser.kt index 5d1c35e0e2..d287a2c19e 100644 --- a/readium/opds/src/main/java/org/readium/r2/opds/OPDS2Parser.kt +++ b/readium/opds/src/main/java/org/readium/r2/opds/OPDS2Parser.kt @@ -11,6 +11,7 @@ package org.readium.r2.opds +import java.net.URL import org.joda.time.DateTime import org.json.JSONArray import org.json.JSONObject @@ -77,6 +78,15 @@ public class OPDS2Parser { } } + @Deprecated( + "Provide an instance of `Url` instead", + ReplaceWith("parse(jsonData, url.toUrl()!!)"), + DeprecationLevel.ERROR + ) + @Suppress("UNUSED_PARAMETER") + public fun parse(jsonData: ByteArray, url: URL): ParseData = + throw NotImplementedError() + private fun isFeed(jsonData: ByteArray) = JSONObject(String(jsonData)).let { ( diff --git a/readium/shared/src/main/java/org/readium/r2/shared/Deprecated.kt b/readium/shared/src/main/java/org/readium/r2/shared/Deprecated.kt index 3d2afcded9..eaafca90a1 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/Deprecated.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/Deprecated.kt @@ -11,6 +11,7 @@ package org.readium.r2.shared +import android.content.Context import java.net.URL import org.json.JSONObject import org.readium.r2.shared.extensions.removeLastComponent @@ -198,3 +199,17 @@ public fun URL.removeLastComponent(): URL = removeLastComponent() public fun normalize(base: String, href: String?): String { throw NotImplementedError() } + +@Deprecated( + "Readium does not provide user error messages anymore. You need to map error cases to your own custom messages.", + level = DeprecationLevel.ERROR +) +public class UserException : Exception() { + + @Deprecated( + "Readium does not provide user error messages anymore. You need to map error cases to your own custom messages.", + level = DeprecationLevel.ERROR + ) + @Suppress("UNUSED_PARAMETER") + public fun getUserMessage(context: Context): String = throw NotImplementedError() +} diff --git a/readium/shared/src/main/java/org/readium/r2/shared/extensions/File.kt b/readium/shared/src/main/java/org/readium/r2/shared/extensions/File.kt index bc52a94ead..112f71e0d3 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/extensions/File.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/extensions/File.kt @@ -68,6 +68,9 @@ public fun File.isParentOf(other: File): Boolean { * If unknown, fallback on `MediaType.BINARY`. */ @Suppress("UnusedReceiverParameter", "RedundantSuspendModifier", "UNUSED_PARAMETER") -@Deprecated("Explicitly use MediaTypeRetriever", level = DeprecationLevel.ERROR) +@Deprecated( + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", + level = DeprecationLevel.ERROR +) public suspend fun File.mediaType(mediaTypeHint: String? = null): MediaType = throw NotImplementedError() diff --git a/readium/shared/src/main/java/org/readium/r2/shared/extensions/Intent.kt b/readium/shared/src/main/java/org/readium/r2/shared/extensions/Intent.kt index cc050e1a47..c0041d989c 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/extensions/Intent.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/extensions/Intent.kt @@ -25,7 +25,7 @@ private val deprecationException = IllegalArgumentException( ) @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Intent.putPublication(publication: Publication) { @@ -34,7 +34,7 @@ public fun Intent.putPublication(publication: Publication) { } @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Intent.putPublicationFrom(activity: Activity) { @@ -42,7 +42,7 @@ public fun Intent.putPublicationFrom(activity: Activity) { } @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Intent.getPublication(activity: Activity?): Publication { @@ -67,7 +67,7 @@ public fun Intent.getPublication(activity: Activity?): Publication { } @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Intent.getPublicationOrNull(): Publication? { @@ -89,7 +89,7 @@ public fun Intent.getPublicationOrNull(activity: Activity): Publication? { } @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Intent.destroyPublication(activity: Activity?) { @@ -101,7 +101,7 @@ public fun Intent.destroyPublication(activity: Activity?) { } @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Bundle.putPublication(publication: Publication) { @@ -110,7 +110,7 @@ public fun Bundle.putPublication(publication: Publication) { } @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Bundle.putPublicationFrom(activity: Activity) { @@ -118,7 +118,7 @@ public fun Bundle.putPublicationFrom(activity: Activity) { } @Deprecated( - "Use a repository to share publications between components", + "Use your own repository to share publications between activities. See the migration guide.", level = DeprecationLevel.ERROR ) public fun Bundle.getPublicationOrNull(): Publication? { diff --git a/readium/shared/src/main/java/org/readium/r2/shared/fetcher/Resource.kt b/readium/shared/src/main/java/org/readium/r2/shared/fetcher/Resource.kt new file mode 100644 index 0000000000..9de181d6e9 --- /dev/null +++ b/readium/shared/src/main/java/org/readium/r2/shared/fetcher/Resource.kt @@ -0,0 +1,16 @@ +package org.readium.r2.shared.fetcher + +@Deprecated( + "Moved to a different package.", + ReplaceWith("org.readium.r2.shared.util.resource.Resource"), + DeprecationLevel.ERROR +) +public class Resource { + + @Deprecated( + "`Resource.Exception` was split into several `Error` classes. You probably need `ReadError`.", + ReplaceWith("org.readium.r2.shared.util.data.ReadError"), + DeprecationLevel.ERROR + ) + public class Exception +} diff --git a/readium/shared/src/main/java/org/readium/r2/shared/publication/Locator.kt b/readium/shared/src/main/java/org/readium/r2/shared/publication/Locator.kt index c9605fec5d..b32bb5dbee 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/publication/Locator.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/publication/Locator.kt @@ -188,6 +188,20 @@ public data class Locator( putIfNotEmpty("text", text) } + @Suppress("UNUSED_PARAMETER") + @Deprecated( + "Provide a `Url` and `MediaType` instances.", + ReplaceWith("Locator(href = Url(href)!!, mediaType = MediaType(type)!!"), + level = DeprecationLevel.ERROR + ) + public constructor( + href: String, + type: String, + title: String? = null, + locations: Locations = Locations(), + text: Text = Text() + ) : this(Url("#")!!, MediaType.BINARY) + @Deprecated( "Use [mediaType.toString()] instead", ReplaceWith("mediaType.toString()"), diff --git a/readium/shared/src/main/java/org/readium/r2/shared/publication/asset/FileAsset.kt b/readium/shared/src/main/java/org/readium/r2/shared/publication/asset/FileAsset.kt new file mode 100644 index 0000000000..f95432898b --- /dev/null +++ b/readium/shared/src/main/java/org/readium/r2/shared/publication/asset/FileAsset.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2024 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.publication.asset + +import java.io.File +import org.readium.r2.shared.util.mediatype.MediaType + +@Deprecated( + "Use an `AssetRetriever` to create an `Asset`.", + ReplaceWith("AssetRetriever().retrieve(file)"), + DeprecationLevel.ERROR +) +public data class FileAsset( + public val file: File, + public val mediaType: MediaType? = null +) diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/Try.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/Try.kt index 9739da74d5..0f6c192b26 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/Try.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/Try.kt @@ -113,6 +113,20 @@ public fun Try.getOrThrow(): S = is Failure -> throw value } +/** + * Returns the encapsulated value if this instance represents success + * or throws the encapsulated ThrowableError exception if it is failure. + */ +@Suppress("UnusedReceiverParameter") +@Deprecated( + "An `Error` is not a throwable object. Refactor or wrap in an `ErrorException`.", + ReplaceWith("getOrElse { throw ErrorException(it) }"), + DeprecationLevel.ERROR +) +@JvmName("getOrThrowError") +public fun Try.getOrThrow(): S = + throw NotImplementedError() + /** * Returns the encapsulated value if this instance represents success or the [defaultValue] if it is failure. */ diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/Url.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/Url.kt index 85e9744ce3..6d880631fe 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/Url.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/Url.kt @@ -359,7 +359,9 @@ private fun String.isValidUrl(): Boolean = @JvmInline public value class FileExtension( public val value: String -) +) { + override fun toString(): String = value +} /** * Appends this file extension to [filename]. diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/http/DefaultHttpClient.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/http/DefaultHttpClient.kt index 8f384052aa..f655062d59 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/http/DefaultHttpClient.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/http/DefaultHttpClient.kt @@ -53,8 +53,7 @@ public class DefaultHttpClient( @Suppress("UNUSED_PARAMETER") @Deprecated( "If you used [additionalHeaders], pass all headers when building your request or modify it in Callback.onStartRequest instead.", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("DefaultHttpClient(mediaTypeRetriever = MediaTypeRetriever())") + level = DeprecationLevel.ERROR ) public constructor( userAgent: String? = null, diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpError.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpError.kt index 7015486816..a40c1733f5 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpError.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpError.kt @@ -70,4 +70,14 @@ public sealed class HttpError( tryOrLog { ProblemDetails.fromJSON(JSONObject(String(body))) } } } + + public companion object { + @Suppress("UNUSED_PARAMETER") + @Deprecated("Not publicly available anymore.", level = DeprecationLevel.ERROR) + public fun wrap(exception: Exception): HttpError = + throw NotImplementedError() + } } + +@Deprecated("Renamed to `HttpError`", ReplaceWith("HttpError"), DeprecationLevel.ERROR) +public typealias HttpException = HttpError diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpRequest.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpRequest.kt index 1088b8ca44..d30d3ca22d 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpRequest.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpRequest.kt @@ -42,6 +42,31 @@ public class HttpRequest( public val allowUserInteraction: Boolean = false ) : Serializable { + @Deprecated( + message = "Provide an instance of `AbsoluteUrl` instead of a string.", + replaceWith = ReplaceWith("HttpRequest(AbsoluteUrl(url)!!)"), + level = DeprecationLevel.ERROR + ) + public constructor( + url: String, + method: Method = Method.GET, + headers: Map = mapOf(), + body: Body? = null, + extras: Bundle = Bundle(), + connectTimeout: Duration? = null, + readTimeout: Duration? = null, + allowUserInteraction: Boolean = false + ) : this( + url = AbsoluteUrl(url)!!, + method = method, + headers = headers.mapValues { (_, value) -> listOf(value) }, + body = body, + extras = extras, + connectTimeout = connectTimeout, + readTimeout = readTimeout, + allowUserInteraction = allowUserInteraction + ) + /** Supported HTTP methods. */ public enum class Method : Serializable { DELETE, GET, HEAD, PATCH, POST, PUT; diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/mediatype/MediaType.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/mediatype/MediaType.kt index a5aa6f58b4..4a98bb7b3d 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/mediatype/MediaType.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/mediatype/MediaType.kt @@ -209,13 +209,13 @@ public class MediaType private constructor( public val isPublication: Boolean get() = matchesAny(CBZ, EPUB, LPF, PDF, W3C_WPUB_MANIFEST, ZAB) || isRwpm || isRpf + @Suppress("RedundantNullableReturnType") @Deprecated( - "Format and MediaType got merged together", - replaceWith = ReplaceWith(""), + message = "The file extension is now in `Format`, which you can sniff using an `AssetRetriever`", level = DeprecationLevel.ERROR ) - public val mediaType: MediaType - get() = this + public val fileExtension: String? get() = + throw NotImplementedError() public companion object { @@ -371,15 +371,19 @@ public class MediaType private constructor( @Deprecated(message = "Use FormatRegistry instead", level = DeprecationLevel.ERROR) public val sniffers: MutableList = mutableListOf() + @Deprecated( + message = "Create the `MediaType` directly instead", + replaceWith = ReplaceWith("MediaType(mediaType)"), + level = DeprecationLevel.ERROR + ) + public fun of(mediaType: String): MediaType? = MediaType(mediaType) + /** * Resolves a format from a single file extension and media type hint, without checking the actual * content. */ @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith( - "MediaTypeRetriever().retrieve(mediaType = mediaType, fileExtension = fileExtension)" - ), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) @Suppress("UNUSED_PARAMETER") @@ -395,10 +399,7 @@ public class MediaType private constructor( * content. */ @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith( - "MediaTypeRetriever().retrieve(mediaTypes = mediaTypes, fileExtensions = fileExtensions)" - ), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) @Suppress("UNUSED_PARAMETER") @@ -414,8 +415,7 @@ public class MediaType private constructor( */ @Suppress("UNUSED_PARAMETER") @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith("MediaTypeRetriever().retrieve(file)"), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) public fun ofFile( @@ -430,10 +430,7 @@ public class MediaType private constructor( * Resolves a format from a local file path. */ @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith( - "MediaTypeRetriever().retrieve(file, mediaTypes = mediaTypes, fileExtensions = fileExtensions)" - ), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) @Suppress("UNUSED_PARAMETER") @@ -450,8 +447,7 @@ public class MediaType private constructor( */ @Suppress("UNUSED_PARAMETER") @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith("MediaTypeRetriever().retrieve(File(path))"), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) public fun ofFile( @@ -467,10 +463,7 @@ public class MediaType private constructor( */ @Suppress("UNUSED_PARAMETER") @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith( - "MediaTypeRetriever().retrieve(File(path), mediaTypes = mediaTypes, fileExtensions = fileExtensions)" - ), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) public fun ofFile( @@ -486,8 +479,7 @@ public class MediaType private constructor( */ @Suppress("UNUSED_PARAMETER") @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith("MediaTypeRetriever().retrieve(bytes)"), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) public fun ofBytes( @@ -503,10 +495,7 @@ public class MediaType private constructor( */ @Suppress("UNUSED_PARAMETER") @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith( - "MediaTypeRetriever().retrieve(bytes, mediaTypes = mediaTypes, fileExtensions = fileExtensions)" - ), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) public fun ofBytes( @@ -523,10 +512,7 @@ public class MediaType private constructor( */ @Suppress("UNUSED_PARAMETER") @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith( - "MediaTypeRetriever(contentResolver = contentResolver).retrieve(uri)" - ), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) public fun ofUri( @@ -544,10 +530,7 @@ public class MediaType private constructor( */ @Suppress("UNUSED_PARAMETER") @Deprecated( - message = "Use MediaTypeRetriever instead", - replaceWith = ReplaceWith( - "MediaTypeRetriever(contentResolver = contentResolver).retrieve(uri, mediaTypes = mediaTypes, fileExtensions = fileExtensions)" - ), + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", level = DeprecationLevel.ERROR ) public fun ofUri( @@ -611,7 +594,10 @@ public class MediaType private constructor( public val LCP_LICENSE: MediaType get() = LCP_LICENSE_DOCUMENT @Suppress("UNUSED_PARAMETER") - @Deprecated("Use `MediaTypeRetriever` instead", level = DeprecationLevel.ERROR) + @Deprecated( + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", + level = DeprecationLevel.ERROR + ) public fun of( file: File, mediaType: String? = null, @@ -619,7 +605,10 @@ public class MediaType private constructor( ): MediaType? = null @Suppress("UNUSED_PARAMETER") - @Deprecated("Use `MediaTypeRetriever` instead", level = DeprecationLevel.ERROR) + @Deprecated( + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", + level = DeprecationLevel.ERROR + ) public fun of( file: File, mediaTypes: List, @@ -627,7 +616,10 @@ public class MediaType private constructor( ): MediaType? = null @Suppress("UNUSED_PARAMETER") - @Deprecated("Use `MediaTypeRetriever` instead", level = DeprecationLevel.ERROR) + @Deprecated( + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", + level = DeprecationLevel.ERROR + ) public fun of( bytes: () -> ByteArray, mediaType: String? = null, @@ -635,7 +627,10 @@ public class MediaType private constructor( ): MediaType? = null @Suppress("UNUSED_PARAMETER") - @Deprecated("Use `MediaTypeRetriever` instead", level = DeprecationLevel.ERROR) + @Deprecated( + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", + level = DeprecationLevel.ERROR + ) public fun of( bytes: () -> ByteArray, mediaTypes: List, @@ -643,7 +638,10 @@ public class MediaType private constructor( ): MediaType? = null @Suppress("UNUSED_PARAMETER") - @Deprecated("Use `MediaTypeRetriever` instead", level = DeprecationLevel.ERROR) + @Deprecated( + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", + level = DeprecationLevel.ERROR + ) public fun of( uri: Uri, contentResolver: ContentResolver, @@ -652,7 +650,10 @@ public class MediaType private constructor( ): MediaType? = null @Suppress("UNUSED_PARAMETER") - @Deprecated("Use `MediaTypeRetriever` instead", level = DeprecationLevel.ERROR) + @Deprecated( + message = "Use an `AssetRetriever` instead to retrieve the format of a file. See the migration guide.", + level = DeprecationLevel.ERROR + ) public fun of( uri: Uri, contentResolver: ContentResolver, diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/resource/Resource.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/resource/Resource.kt index 61638d68df..7324ebe72d 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/resource/Resource.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/resource/Resource.kt @@ -44,6 +44,13 @@ public interface Resource : Readable { public class Builder(properties: Map = emptyMap()) : MutableMap by properties.toMutableMap() } + + @Deprecated( + "`Resource.Exception` was split into several `Error` classes. You probably need `ReadError`.", + ReplaceWith("org.readium.r2.shared.util.data.ReadError"), + DeprecationLevel.ERROR + ) + public class Exception } /** Creates a Resource that will always return the given [error]. */ diff --git a/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt b/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt index 98b04b52a0..dd3e4b0c26 100644 --- a/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt +++ b/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt @@ -28,7 +28,7 @@ import org.readium.r2.streamer.parser.PublicationParser */ public class PublicationOpener( private val publicationParser: PublicationParser, - contentProtections: List, + contentProtections: List = emptyList(), private val onCreatePublication: Publication.Builder.() -> Unit = {} ) { public sealed class OpenError( diff --git a/readium/streamer/src/main/java/org/readium/r2/streamer/Streamer.kt b/readium/streamer/src/main/java/org/readium/r2/streamer/Streamer.kt new file mode 100644 index 0000000000..5232921c1c --- /dev/null +++ b/readium/streamer/src/main/java/org/readium/r2/streamer/Streamer.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2023 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.streamer + +@Deprecated( + "Use a `PublicationOpener` instead. See the migration guide.", + ReplaceWith("PublicationOpener"), + level = DeprecationLevel.ERROR +) +public class Streamer diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/AudioReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/AudioReaderFragment.kt index 5eb3368c63..c22b40414b 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/AudioReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/AudioReaderFragment.kt @@ -92,7 +92,7 @@ class AudioReaderFragment : BaseReaderFragment(), SeekBar.OnSeekBarChangeListene playback: TimeBasedMediaNavigator.Playback ) { Timber.v("onPlaybackChanged $playback") - if (playback.state is MediaNavigator.State.Error) { + if (playback.state is MediaNavigator.State.Failure) { onPlayerError() return } @@ -190,7 +190,7 @@ class AudioReaderFragment : BaseReaderFragment(), SeekBar.OnSeekBarChangeListene } Unit } - is MediaNavigator.State.Error -> { + is MediaNavigator.State.Failure -> { // Do nothing. } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/tts/TtsViewModel.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/tts/TtsViewModel.kt index 658f3ac102..0c006fdb21 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/tts/TtsViewModel.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/tts/TtsViewModel.kt @@ -144,9 +144,9 @@ class TtsViewModel private constructor( is MediaNavigator.State.Ended -> { stop() } - is MediaNavigator.State.Error -> { + is MediaNavigator.State.Failure -> { onPlaybackError( - (playback.state as TtsNavigator.State.Error).error + (playback.state as TtsNavigator.State.Failure).error ) } is MediaNavigator.State.Ready -> {}