All notable changes to this project will be documented in this file. Take a look at the migration guide to upgrade between two major versions.
Warning: Features marked as experimental may change or be removed in a future release without notice. Use with caution.
- Fixed computing the total progression of audiobook locators.
- Fixed starting the TTS from the current resource in a fixed-layout EPUB.
- Added
ExoPlayerDataSourceProvider
to implement resource caching with the media3 audio navigator.
- A new
LcpService.retrieveLicenseDocument()
API can be used to read the LCPL embedded in a ZIP package.
- EPUB: The
scroll
preference is now forced totrue
when rendering vertical text (e.g. CJK vertical). See this discussion for the rationale.
- Fixed concurrent access when streaming a ZIP container.
- Optimized scrolling to an EPUB text-based locator if it contains a CSS selector.
- Fixed applying the PSPDFKit reading progression preference on configuration changes.
- All the
completion
parameters of theNavigator
APIs are removed.
- All the APIs using or returning a
Date
objects are now using a customInstant
type.
- The
Link
property key for archive-based publication assets (e.g. an EPUB/ZIP) is nowhttps://readium.org/webpub-manifest/properties#archive
instead ofarchive
.
- #493 The LCP module does not require the Bluetooth permissions anymore to derive the device name.
- #325 Top EPUB selections no longer break when dragging the selection handles.
- Fixed applying preferences while the EPUB navigator fragment is being recreated.
- The new
HyperlinkNavigator.Listener.shouldFollowInternalLink(Link, LinkContext?)
allows you to handle footnotes according to your preference.- By default, the navigator now moves to the footnote content instead of displaying a pop-up as it did in version 2.x.
- You can use
LcpService.injectLicenseDocument()
to insert an LCPL into a package, if you downloaded it manually instead of usingLcpService.acquirePublication()
.
- The
DownloadManager
introduced in version 3.0.0-alpha.1 has been removed due to the Android Download Manager introducing unnecessary complexities in the toolkit. Instead, we chose to enable apps to manually handle an LCP download withLcpService.injectLicenseDocument()
.
- A new
Format
type was introduced to augmentMediaType
with more precise information about the format specifications of anAsset
. - The
DownloadManager
interface handles HTTP downloads. Components like theLcpService
rely on it for downloading publications. Readium v3 ships with two implementations:ForegroundDownloadManager
uses anHttpClient
to download files while the app is running.AndroidDownloadManager
is built upon Android'sDownloadManager
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.
- Support for keyboard events in the EPUB, PDF and image navigators. See
VisualNavigator.addInputListener()
.
- 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.
⚠️ To avoid conflicts when merging your app resources, all resources declared in the Readium toolkit now have the prefixreadium_
. 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.- Most APIs now return an
Error
instance instead of anException
in case of failure, as these objects are not thrown by the toolkit but returned as values
⚠️ 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 forLocator
andLink
'shref
. You must migrate the HREFs or Locators stored in your database, please consult the migration guide.Link.href
andLocator.href
are now respectivelyHref
andUrl
objects. If you still need the string value, you can calltoString()
MediaType
no longer has static helpers for sniffing it from a file or URL. Instead, you can use anAssetRetriever
to retrieve the format of a file.
- Version 3 includes a new component called
DirectionalNavigationAdapter
that replacesEdgeTapNavigation
. 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
andonDrag
events ofVisualNavigator.Listener
have been deprecated. You can now use multiple implementations ofInputListener
withVisualNavigator.addInputListener()
.
- The
Streamer
object has been deprecated in favor of components with smaller responsibilities:AssetRetriever
andPublicationOpener
.
LcpService.acquirePublication()
is deprecated in favor ofLcpService.publicationRetriever()
, which provides greater flexibility thanks to theDownloadManager
.- The way the host view of a
LcpDialogAuthentication
is retrieved was changed to support Android configuration changes.
- Both the Fuel and Kovenant libraries have been completely removed from the toolkit. With that, several deprecated functions have also been removed.
- The
putPublication
andgetPublication
helpers inIntent
are deprecated. Now, it is the application's responsibility to passPublication
objects between activities and reopen them when necessary.
- 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.
- #509 Support for the new 2.x LCP Profiles.
- Readium is now distributed with Maven Central. Take a look at the migration guide to update your Gradle configuration.
- The EPUB
backgroundColor
preference is now available with fixed-layout publications. - New
EPUBNavigatorFragment.Configuration.useReadiumCssFontSize
option to revert to the 2.2.0 strategy for setting the font size of reflowable EPUB publications.- The native font size strategy introduced in 2.3.0 uses the Android web view's
WebSettings.textZoom
property to adjust the font size. 2.2.0 was using Readium CSS's--USER__fontSize
variable. WebSettings.textZoom
will work with more publications than--USER__fontSize
, even the ones poorly authored. However the page width is not adjusted when changing the font size to keep the optimal line length.
- The native font size strategy introduced in 2.3.0 uses the Android web view's
- Scroll mode: jumping between two EPUB resources with a horizontal swipe triggers the
Navigator.Listener.onJumpToLocator()
callback.- This can be used to allow the user to go back to their previous location if they swiped across chapters by mistake.
- Support for non-linear EPUB resources with an opt-in in reading apps (contributed by @chrfalch in #375 and #376).
- Override loading non-linear resources with
VisualNavigator.Listener.shouldJumpToLink()
. - Present a new
EpubNavigatorFragment
by providing a customreadingOrder
with only this resource to the constructor.
- Override loading non-linear resources with
- Added dummy navigator fragment factories to prevent crashes caused by Android restoring the fragments after a process death.
- To use it, set the dummy fragment factory when you don't have access to the
Publication
instance. Then, either finish theActivity
or pop the fragment from the UI before it resumes.override fun onCreate(savedInstanceState: Bundle?) { val publication = model.publication ?: run { childFragmentManager.fragmentFactory = EpubNavigatorFragment.createDummyFactory() super.onCreate(savedInstanceState) requireActivity().finish() // or navController?.popBackStack() return } // Create the real navigator factory as usual... }
- To use it, set the dummy fragment factory when you don't have access to the
- The EPUB content iterator now returns
audio
andvideo
elements.
- Readium resources are now prefixed with
readium_
. Take care of updating any overridden resource by following the migration guide. Link
andLocator
'shref
are normalized as valid URLs to improve interoperability with the Readium Web toolkits.- You MUST migrate your database if you were persisting HREFs and Locators. Take a look at the migration guide for guidance.
Publication.localizedTitle
is nullable, as we cannot guarantee that all publication sources offer a title.- The
MediaType
sniffing helpers are deprecated in favor ofMediaTypeRetriever
(for media type and file extension hints and raw content) andAssetRetriever
(for URLs).
EpubNavigatorFragment.firstVisibleElementLocator()
now returns the first block element that is visible on the screen, even if it starts on previous pages.- This is used to make sure the user will not miss any context when restoring a TTS session in the middle of a resource.
- The
VisualNavigator
's drag and tap listener events are moved to a newaddInputListener()
API. - The new
DirectionalNavigationAdapter
component replacesEdgeTapNavigation
, helping you turn pages with the arrow and space keyboard keys, or taps on the edge of the screen.
DefaultHttClient.additionalHeaders
is deprecated. Set all the headers when creating a newHttpRequest
, or modify outgoing requests inDefaultHttpClient.Callback.onStartRequest()
.
- All the navigator
Activity
are deprecated in favor of theFragment
variants.
- The
Fetcher
interface was deprecated in favor of theContainer
one inreadium-shared
.
- #360 Fix EPUB JavaScript interface injection when rotating the screen on some devices.
- Fixed issue with the TTS starting from the beginning of the chapter instead of the current position.
- Extract the raw content (text, images, etc.) of a publication. Take a look at the user guide.
- Add support for unsafe HTTP redirections with
DefaultHttpClient
.- You will need to opt-in explicitly by implementing
DefaultHttpClient.Callback.onFollowUnsafeRedirect
.
- You will need to opt-in explicitly by implementing
- Improved Javascript support in the EPUB navigator:
- Register custom JavascriptInterface objects to inject native Kotlin code in the EPUB web views.
EpubNavigatorFragment.createFactory( publication = publication, …, config = EpubNavigatorFragment.Configuration().apply { registerJavascriptInterface("customInterface") { link -> MyCustomApi(link) } } ) class MyCustomApi(val link: Link) { @JavascriptInterface fun api(arg: String): String { return "API called from the resource ${link.href} with argument $arg") } }
- Evaluate JavaScript on the currently visible HTML resource with
EpubNavigatorFragment.evaluateJavascript()
.val result = navigator.evaluateJavascript("customInterface.api('argument')")
- Register custom JavascriptInterface objects to inject native Kotlin code in the EPUB web views.
- New PSPDFKit adapter for rendering PDF documents. Take a look at the user guide.
- A brand new text-to-speech implementation.
- Support for custom fonts with the EPUB navigator.
- New EPUB user preferences, as part of the revamped Settings API:
backgroundColor
- Default page background color.fontWeight
- Base text font weight.textColor
- Default page text color.textNormalization
- Normalize font style, weight and variants, which improves accessibility.imageFilter
- Filter applied to images in dark theme (darken, invert colors)language
- Language of the publication content.readingProgression
- Direction of the reading progression across resources, e.g. RTL.typeScale
- Scale applied to all element font sizes.paragraphIndent
- Text indentation for paragraphs.paragraphSpacing
- Vertical margins for paragraphs.hyphens
- Enable hyphenation.ligatures
- Enable ligatures in Arabic.
- Fixed scroll inertia when scrolling an EPUB.
- EPUB decorations can now be attached to
Locator
objects containing only an HTML ID (locations.fragments
) or a CSS selector (locations.cssSelector
).
TransformingResource
now caches its content by default, as it is the correct behavior in most cases. SetcacheBytes = false
explicitly to revert to the previous behavior.- The previous PDF navigator was extracted in its own package to support third-party PDF engines. This is a breaking change if your app supported PDF, take a look at the migration guide.
- The EPUB and PDF user preferences API got revamped. Take a look at the user guide and the migration guide to learn how to use it.
Decoration.extras
is now aMap<String, Any>
instead ofBundle
. You will need to update your app if you were storing custom data inextras
, for example:val decoration = Decoration(..., extras = mapOf("id" to id) ) val id = decoration.extras["id"] as? Long
- The local HTTP server is not needed anymore to render EPUB publications. Take a look at the migration guide.
- Fixed parsing the table of contents of an EPUB 3 using NCX instead of a Navigation Document.
- swift-toolkit#61 Fixed serving EPUB resources when the HREF contains an anchor or query parameters.
- Fixed emitting
currentLocator
with fixed layout EPUBs. - Prevent refreshing an already loaded EPUB resource when jumping to a
Locator
in it. - #86 Fixed page swipes while selecting text in an EPUB resource.
- The
onTap
event is not sent when an EPUB text selection is active anymore, to prevent showing the app bar while dismissing a selection. - #76 Fixed EPUB fixed layout font size affected by device settings.
Decoration
objects are now properly comparable withequals()
.- #292 Fix broken pagination when an EPUB uses
overflow-x: hidden
.
- #286 Fixed broken dependency to NanoHTTPD.
- A new
Publication.conformsTo()
API to identify the profile of a publication. - Support for the
conformsTo
RWPM metadata, to identify the profile of aPublication
.
- The PDF navigator now honors the publication reading progression with support for right-to-left and horizontal scrolling.
- The default (auto) reading progression for PDF is top-to-bottom, which is vertical scrolling.
- A new convenience utility
EdgeTapNavigation
to trigger page turns while tapping the screen edges.- It takes into account the navigator reading progression to move into the right direction.
- Call it from the
VisualNavigator.Listener.onTap()
callback as demonstrated below:
override fun onTap(point: PointF): Boolean { val navigated = edgeTapNavigation.onTap(point, requireView()) if (!navigated) { // Fallback action, for example toggling the app bar. } return true }
- The new
Navigator.Listener.onJumpToLocator()
API is called every time the navigator jumps to an explicit location, which might break the linear reading progression.- For example, it is called when clicking on internal links or programmatically calling
Navigator.go()
, but not when turning pages. - You can use this callback to implement a navigation history by differentiating between continuous and discontinuous moves.
- For example, it is called when clicking on internal links or programmatically calling
- You can now disable the display cutouts padding in the EPUB navigator (contributed by @szymn).
- This is useful when the navigator is not laid out full screen.
- (experimental) A new audiobook navigator based on Jetpack
media2
.- See the pull request #80 for the differences with the previous audiobook navigator.
- This navigator is located in its own module
readium-navigator-media2
. You will need to add it to your dependencies to use it. - The Test App demonstrates how to use the new audiobook navigator, see
MediaService
andAudioReaderFragment
.
- (experimental) The EPUB navigator now supports overridable drag gestures. See
VisualNavigator.Listener
.
Publication.type
is now deprecated in favor of the newPublication.conformsTo()
API which is more accurate.- For example, replace
publication.type == Publication.TYPE.EPUB
withpublication.conformsTo(Publication.Profile.EPUB)
before opening a publication with theEpubNavigatorFragment
.
- For example, replace
Link.toLocator()
is deprecated as it may create an incorrectLocator
if the linktype
is missing.- Use
publication.locatorFromLink()
instead.
- Use
- Fix building with Kotlin 1.6.
- Fixed the rendering of PDF covers in some edge cases.
- Fixed reading ranges of obfuscated EPUB resources.
- Fixed turning pages of an EPUB reflowable resource with an odd number of columns. A virtual blank trailing column is appended to the resource when displayed as two columns.
- EPUB: Fallback on
reflowable
if thepresentation.layout
hint is missing from a manifest. - EPUB: Offset of the current selection's
rect
to take into account the vertical padding. - Improve backward compatibility of JavaScript files using Babel.
- #193 Fixed invisible
<audio>
elements.
- Improve loading of EPUB reflowable resources.
- Resources are hidden until fully loaded and positioned.
- Intermediary locators are not broadcasted as
currentLocator
anymore while loading a resource. - Improved accuracy when jumping to the middle of a large resource.
EpubNavigatorFragment.PaginationListener.onPageLoaded()
is now called only a single time, for the currently visible page.VisualNavigator.Listener.onTap()
is called even when a resource is not fully loaded.
EpubNavigatorFragment
'sgoForward()
andgoBackward()
are now jumping to the previous or next pages instead of resources.- #20 EPUB navigator stuck between two pages with vertical swipes.
- #27 Internal links break the EPUB navigator (contributed by @mihai-wolfpack).
- (experimental) A new Publication
SearchService
to search through the resources' content, with a default implementationStringSearchService
. ContentProtection.Scheme
can be used to identify protection technologies using unique URI identifiers.Link
objects from archive-based publication assets (e.g. an EPUB/ZIP) have additional properties for entry metadata."properties" { "archive": { "entryLength": 8273, "isEntryCompressed": true } }
- EPUB publications implement a
SearchService
to search through the content. - Known DRM schemes (LCP and Adobe ADEPT) are now sniffed by the
Streamer
, when no registeredContentProtection
supports them.- This is helpful to present an error message when the user attempts to open a protected publication not supported by the app.
- The EPUB navigator is now able to navigate to a
Locator
using itstext
context. This is useful for search results or highlights missing precise locations. - Get or clear the current user selection of the navigators implementing
SelectableNavigator
. - (experimental) Support for the Decorator API to draw user interface elements over a publication's content.
- This can be used to render highlights over a text selection, for example.
- For now, only the EPUB navigator implements
DecorableNavigator
, for reflowable publications. You can implement custom decoration styles withHtmlDecorationTemplate
.
- Customize the EPUB selection context menu by providing a custom
ActionMode.Callback
implementation withEpubNavigatorFragment.Configuration.selectionActionModeCallback
.- This is an alternative to overriding
Activity.onActionModeStarted()
which does not seem to work anymore with Android 12.
- This is an alternative to overriding
- (experimental) A new audiobook navigator based on Android's
MediaSession
.- It supports out-of-the-box media style notifications and background playback.
- ExoPlayer is used by default for the actual playback, but you can use a custom player by implementing
MediaPlayer
.
- New APIs using coroutines and R2's
HttpClient
instead of Fuel and kovenant (contributed by @stevenzeck).
- Upgraded to Kotlin 1.5.31 and Gradle 7.1.1
- The default EPUB positions service now uses the archive entry length when available. This is similar to how Adobe RMSDK generates page numbers.
- To use the former strategy, create the
Streamer
with:Streamer(parsers = listOf(EpubParser(reflowablePositionsStrategy = OriginalLength(pageLength = 1024))))
- To use the former strategy, create the
- The order of precedence of
Locator
locations in the reflowable EPUB navigator is:text
, HTML ID, thenprogression
. The navigator will now fallback on less precise locations in case of failure.
- Migrated to Jetpack Room for the SQLite database storing rights and passphrases (contributed by @stevenzeck).
- Note that the internal SQL schema changed. You will need to update your app if you were querying the database manually.
- Crash with
HttpRequest.setPostForm()
on Android 6. - HREF normalization when a resource path contains special characters.
- EPUB style injection when a resource has a
<head>
tag with attributes.
- When restoring a
Locator
, The PDF navigator now falls back onlocations.position
if thepage=
fragment identifier is missing.
- Links in an OPDS 2 feed are normalized to the feed base URL.
HttpFetcher
is a new publication fetcher able to serve remote resources through HTTP.- The actual HTTP requests are performed with an instance of
HttpClient
.
- The actual HTTP requests are performed with an instance of
HttpClient
is a new protocol exposing a high level API to perform HTTP requests.DefaultHttpClient
is an implementation ofHttpClient
using standardHttpURLConnection
APIs. You can useDefaultHttpClient.Callback
to customize how requests are created and even recover from errors, e.g. to implement Authentication for OPDS.- You can provide your own implementation of
HttpClient
to Readium APIs if you prefer to use a third-party networking library.
Streamer
takes a new optionalHttpClient
dependency to handle HTTP requests.
- Scrolling to an EPUB ID (e.g. from the table of contents) when the target spans several screens.
Publication.Service.Context
now holds a reference to the parentPublication
. This can be used to access other services from a givenPublication.Service
implementation.- The default
LocatorService
implementation can be used to get aLocator
from a global progression in the publication.publication.locateProgression(0.5)
Server.addPublication()
is a new API which replacesaddEpub()
and is easier to use.- If the publication can be served, it will return the base URL which you need to provide to the Navigator
Activity
orFragment
. - You do not need to give the publication filename nor add the server port in the
$key-publicationPort
SharedPreference
value anymore.
- If the publication can be served, it will return the base URL which you need to provide to the Navigator
- You can observe the progress of an acquisition by providing an
onProgress
closure toLcpService.acquirePublication()
. - Extensibility in licenses'
Rights
model.
- The HTTP server now requests that publication resources are not cached by browsers.
- Caching poses a security risk for protected publications.
R2EpubActivity
andR2AudiobookActivity
require a newbaseUrl
Intent
extra. You need to set it to the base URL returned byServer.addPublication()
from the Streamer.
- The Renew Loan API got revamped to better support renewal through a web page.
- You will need to implement
LcpLicense.RenewListener
to coordinate the UX interaction. - If your application fits Material Design guidelines, take a look at
MaterialRenewListener
for a default implementation.
- You will need to implement
- Removed dependency on Joda's
DateTime
in public APIs.- You can always create a
DateTime
from the standardDate
objects if you relied on Joda's features in the callers.
- You can always create a
- #129 Improve performances when reading deflated ZIP resources.
- For example, it helps with large image-based FXL EPUB which used to be slow to render.
- #136
null
values in JSON string properties are now properly parsed as nullable types, instead of the string"null"
- #217 Interactive HTML elements are not bypassed anymore when handling touch gestures.
- Scripts using
preventDefault()
are now taken into account and do not trigger a tap event anymore.
- Scripts using
- #150 External links are opened in a Chrome Custom Tab instead of the navigator's web view.
- #52 Memory leak in EPUB web views. This fixes ongoing media playback when closing an EPUB.
- #287 Make sure the passphrase input is visible on smaller devices in the authentication dialog.
PublicationAsset
is a new interface which can be used to open a publication from various medium, such as a file, a remote URL or a custom source.File
was replaced byFileAsset
, which implementsPublicationAsset
.
- Support for display cutouts (screen notches).
- IMPORTANT: You need to remove any
setPadding()
statement from your app inUserSettings.kt
, if you copied it from the test app. - If you embed a navigator fragment (e.g.
EpubNavigatorFragment
) yourself, you need to opt-in by specifying thelayoutInDisplayCutoutMode
of the hostActivity
. R2EpubActivity
andR2CbzActivity
automatically applyLAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
to their window'slayoutInDisplayCutoutMode
.PdfNavigatorFragment
is not yet compatible with display cutouts, because of limitations from the underlying PDF viewer.
- IMPORTANT: You need to remove any
- Customize EPUB vertical padding by overriding the
r2.navigator.epub.vertical_padding
dimension.- Follow Android's convention for alternative resources to specify different paddings for landscape (
values-land
) or large screens.
- Follow Android's convention for alternative resources to specify different paddings for landscape (
- Upgraded to Kotlin 1.4.10.
Format
got merged intoMediaType
, to simplify the media type APIs.- You can use
MediaType.of()
to sniff the type of a file or bytes.- All the
MediaType.of()
functions are now suspending to prevent deadlocks withrunBlocking
.
- All the
MediaType
has now optionalname
andfileExtension
properties.- Some publication formats can be represented by several media type aliases. Using
mediaType.canonicalMediaType()
will give you the canonical media type to use, for example when persisting the file type in a database. All Readium APIs are already returning canonical media types, so it only matters if you create aMediaType
yourself from its string representation.
- You can use
ContentLayout
is deprecated, usepublication.metadata.effectiveReadingProgression
to determine the reading progression of a publication instead.
Streamer
is now expecting aPublicationAsset
instead of aFile
. You can create custom implementations ofPublicationAsset
to open a publication from different medium, such as a file, a remote URL, in-memory bytes, etc.FileAsset
can be used to replaceFile
and provides the same behavior.
- All
utils.js
functions were moved under areadium.
namespace. You will need to update your code if you were calling them manually.
- When acquiring a publication, falls back on the media type declared in the license link if the server returns an unknown media type.
- EPUBs declaring multiple languages were laid out from right to left if the first language had an RTL reading
progression. Now if no reading progression is set, the
effectiveReadingProgression
will be LTR. - #152 Panning through a zoomed-in fixed layout EPUB (contributed by @johanpoirier).
- #146 Various reflowable EPUB columns shift issues.
- Restoring the last EPUB location after configuration changes (e.g. screen rotation).
- Edge taps to turn pages when the app runs in a multi-windows environment.
- The Publication Services API allows to extend a
Publication
with custom implementations of known services. This version ships with a few predefined services:PositionsService
provides a list of discrete locations in the publication, no matter what the original format is.CoverService
provides an easy access to a bitmap version of the publication cover.
- The Composite Fetcher API can be used to extend the way publication resources are accessed.
- Support for exploded directories for any archive-based publication format.
- Content Protection handles DRM and other format-specific protections in a more systematic way.
- LCP now ships an
LcpContentProtection
implementation to be plugged into theStreamer
. - You can add custom
ContentProtection
implementations to support other DRMs by providing an instance to theStreamer
.
- LCP now ships an
- Streamer API offers a simple interface to parse a publication and replace standalone parsers.
- A generic
ImageParser
for bitmap-based archives (CBZ or exploded directories) and single image files. - A generic
AudioParser
for audio-based archives (Zipped Audio Book or exploded directories) and single audio files.
- Support for the new
Publication
model using the Content Protection for DRM rights and the Fetcher for resource access. - (experimental) New
Fragment
implementations as an alternative to the legacyActivity
ones (contributed by @johanpoirier).- The fragments are chromeless, to let you customize the reading UX.
- To create the fragments use the matching factory such as
EpubNavigatorFragment.createFactory()
, as showcased inR2EpubActivity
. - At the moment, highlights and TTS are not yet supported in the new EPUB navigator
Fragment
. - This is now the recommended way to integrate Readium in your applications.
- LCP implementation of the Content Protection API to work with the new Streamer API (contributed by @qnga).
- It is highly recommended that you upgrade to the new
Streamer
API to open publications, which will simplify DRM unlocking.
- It is highly recommended that you upgrade to the new
- Two default implementations of
LcpAuthenticating
:LcpDialogAuthentication
to prompt the user for its passphrase with the official LCP dialog.LcpPassphraseAuthentication
to provide directly a passphrase, pulled for example from a database or a web service.
LcpService::isLcpProtected()
provides a way to check if a file is protected with LCP.- All the
LcpException
errors are now implementingUserException
and are suitable for user display. UsegetUserMessage()
to get the localized message.
- The
Publication
andContainer
types were merged together to offer a single interface to a publication's resources.- Use
publication.get()
to read the content of a resource, such as the cover. It will automatically be decrypted if aContentProtection
was attached to thePublication
.
- Use
Container
andContentFilters
were replaced by a shared implementation of aFetcher
.
currentLocator
is now aStateFlow
instead ofLiveData
, to better support chromeless navigators such as an audiobook navigator.- If you were observing
currentLocator
in a UI context, you can continue to do so withcurrentLocator.asLiveData()
.
- If you were observing
- Improvements to the PDF navigator:
- The navigator doesn't require PDF publications to be served from an HTTP server anymore. A side effect is that the navigator is now able to open larger PDF files.
PdfNavigatorFragment.Listener::onResourceLoadFailed()
can be used to report fatal errors to the user, such as when trying to open a PDF document that is too large for the available memory.- A dedicated
PdfNavigatorFragment.createFactory()
was added, which deprecates the use ofNavigatorFragmentFactory
.
- The public API got modernized to be more Kotlin idiomatic (contributed by @qnga).
- All asynchronous APIs are now suspending to take advantage of Kotlin's coroutines.
LcpAuthenticating
is now provided with more information and you will need to update any implementation you may have.- If you copied the default authentication dialog, it's recommended to use
LcpDialogAuthentication
instead.
- If you copied the default authentication dialog, it's recommended to use
- Publications are now downloaded to a temporary location, to make sure disk storage can be recovered automatically by the system. After acquiring the publication, you need to move the downloaded file to another permanent location.
- The private
liblcp
dependency is now accessed through reflection, to allow switching LCP dynamically (contributed by @qnga).- You need to add
implementation "readium:liblcp:1.0.0@aar"
to yourbuild.gradle
. LcpService::create()
returnsnull
iflcplib
is not found.
- You need to add
OutOfMemoryError
occuring while opening large publications are now caught to prevent crashes. They are reported asResource.Exception.OutOfMemory
.- Readium can now open PDF documents of any size without crashing. However, LCP protected PDFs are still limited by the available memory.
- Readium can now open PDF documents of any size without crashing. However, LCP protected PDFs are still limited by the available memory.
- Various HTTP server fixes and optimizations.
- Prevent switching to the next resource by mistake when scrolling through an EPUB resource in scroll mode.
- Decrypting resources in some edge cases (contributed by @qnga)
- Issues with LSD interactions:
- Exceptions handling with
renew
andreturn
interactions. - Presentation of the
renew
interaction through an HTML page.
- Exceptions handling with
- The timeout of fetching the License Status Document is reduced to 5 seconds, to avoid blocking a publication opening in low Internet conditions.
- Support for Positions List, which provides a list of discrete locations in a publication and can be used to implement an approximation of page numbers.
- Get the visible position from the current
Locator
withlocations.position
. - The total number of positions can be retrieved with
publication.positions().size
. It is a suspending function because computing positions the first time can be expensive.
- Get the visible position from the current
- The new Format API simplifies the detection of file formats, including known publication formats such as EPUB and PDF.
- A format can be "sniffed" from files, raw bytes or even HTTP responses.
- Reading apps are welcome to extend the API with custom formats.
- Using
Link.mediaType.matches()
is now recommended to safely check the type of a resource. - More details about the Kotlin implementation can be found in the pull request.
- In
Publication
shared models:- Support for the Presentation Hints extension.
- Support for OPDS holds, copies and availability in
Link
, for library-specific features. - Readium Web Publication Manifest extensibility is now supported for
Publication
,Metadata
, link'sProperties
and locator'sLocations
, which means that you are now able to access custom JSON properties in a manifest by creating Kotlin extensions on the shared models.
- Support for Positions List with EPUB, CBZ and PDF. Positions provide a list of discrete locations in a publication and can be used to implement an approximation of page numbers.
- Get the visible position from the current
Locator
withlocations.position
. - The total number of positions can be retrieved with
publication.positions().size
. It is a suspending function because computing positions the first time can be expensive.
- Get the visible position from the current
ReadiumWebPubParser
to parse all Readium Web Publication profiles, including Audiobooks, LCP for Audiobooks and LCP for PDF. It parses both manifests and packages.- (experimental)
PDFParser
to parse single PDF documents.- The PDF parser is based on PdfiumAndroid, which may increase the size of your apps. Please open an issue if this is a problem for you, as we are considering different solutions to fix this in a future release.
- The position is now reported in the locators for EPUB, CBZ and PDF.
- (experimental) PDF navigator.
- Supports both single PDF and LCP protected PDF.
- As a proof of concept, it is implemented using
Fragment
instead ofActivity
.R2PdfActivity
showcases how to use thePdfNavigatorFragment
with the newNavigatorFragmentFactory
. - The navigator is based on AndroidPdfViewer, which may increase the size of your apps. Please open an issue if this is a problem for you, as we are considering different solutions to fix this in a future release.
- Support for PDF and Readium Audiobooks protected with LCP.
- The
Publication
shared models underwent an important refactoring and some of these changes are breaking. Please refer to the migration guide to update your codebase.- All the models are now immutable data classes, to improve code safety. This should not impact reading apps unless you created
Publication
or other models yourself. - A few types and enums were renamed to follow the Google Android Style coding convention better. Just follow deprecation warnings to update your codebase.
- All the models are now immutable data classes, to improve code safety. This should not impact reading apps unless you created
- The CSS, JavaScript and fonts injection in the
Server
was refactored to reduce the risk of collisions and simplify your codebase.- This is a breaking change, to upgrade your app you need to:
- Provide the application's
Context
when creating aServer
. - Remove the following injection statements, which are now handled directly by the Streamer:
server.loadCustomResource(assets.open("scripts/crypto-sha256.js"), "crypto-sha256.js", Injectable.Script) server.loadCustomResource(assets.open("scripts/highlight.js"), "highlight.js", Injectable.Script)
- Provide the application's
- This is a breaking change, to upgrade your app you need to:
- Upgraded to Readium CSS 1.0.0-beta.1.
- Two new fonts are available: AccessibleDfa and IA Writer Duospace.
- The file structure now follows strictly the one from ReadiumCSS's
dist/
, for easy upgrades and custom builds replacement.
LCPAuthenticating
can now return hashed passphrases in addition to clear ones. This can be used by reading apps fetching hashed passphrases from a web service or Authentication for OPDS, for example.
R2SyntheticPageList
was replaced by the aforementioned Positions List and can be safely removed from your codebase.
Navigator.currentLocation
andNavigatorDelegate.locationDidChange()
are deprecated in favor of a unifiedNavigator.currentLocator
, which is observable thanks toLiveData
.
- Important: Publications parsed from large manifests could crash the application when starting a reading activity. To fix this,
Publication
must not be put in anIntent
extra anymore. Instead, use the newIntent
extensions provided by Readium. This solution is a crutch until we move away fromActivity
in the Navigator and let reading apps handle the lifecycle ofPublication
themselves. - The local HTTP server was broken when provided with publication filenames containing invalid characters.
- XML namespace prefixes are now properly supported when an author chooses unusual ones (contributed by @qnga).
- The
AndroidManifest.xml
is not forcing anymoreallowBackup
andsupportsRtl
, to let reading apps manage these features themselves (contributed by @twaddington).
- The EPUB parser underwent a significant refactoring to fix a number of issues (contributed by @qnga)
- Metadata parsing was updated to follow our up-to-date specifications.
- XML namespace prefixes are now properly supported, when an author chooses unusual ones.
- Similarly, default vocabularies and prefixes for EPUB 3 Property Data Types are now properly handled.
Server
was broken when provided with publication filenames containing invalid characters.- EPUB publishers' default styles are not overriden by Readium CSS anymore.
- The
AndroidManifest.xml
is not forcing anymoreallowBackup
andsupportsRtl
, to let reading apps manage these features themselves (contributed by @twaddington).
- Important: Publications parsed from large manifests could crash the application when starting a reading activity. To fix this,
Publication
must not be put in anIntent
extra anymore. Instead, use the newIntent
extensions provided by Readium. This solution is a crutch until we move away fromActivity
in the Navigator and let reading apps handle the lifecycle ofPublication
themselves. - Crash when opening a publication with a space in its filename.
- Jumping to an EPUB location from the search.
- The
AndroidManifest.xml
is not forcing anymoreallowBackup
andsupportsRtl
, to let reading apps manage these features themselves (contributed by @twaddington).
- XML namespace prefixes are now properly supported when an author chooses unusual ones (contributed by @qnga).
- The
AndroidManifest.xml
is not forcing anymoreallowBackup
andsupportsRtl
, to let reading apps manage these features themselves (contributed by @twaddington).
OutOfMemoryError
when downloading a large publication. This fix removed the dependency to Fuel.- The
AndroidManifest.xml
is not forcing anymoreallowBackup
andsupportsRtl
, to let reading apps manage these features themselves (contributed by @twaddington).