From bc4a8cc78a0c4f9583f335606955c1551274937e Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:57:07 -0500 Subject: [PATCH 01/27] Update versions --- gradle.properties | 4 +-- gradle/libs.versions.toml | 39 ++++++++++++------------ gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/gradle.properties b/gradle.properties index 245f9ebe27..0ba3f0ab63 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,8 +9,8 @@ pom.groupId=org.readium.kotlin-toolkit pom.version=3.3.0 android.minSdk=23 -android.compileSdk=36 -android.targetSdk=36 +android.compileSdk=37 +android.targetSdk=37 # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 444a497905..81e7876fec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] # Keep compatibility with https://kotlinlang.org/docs/multiplatform/multiplatform-compatibility-guide.html#version-compatibility -kotlin = "2.3.20" -agp = "9.0.0" +kotlin = "2.4.0" +agp = "9.2.1" desugar_jdk_libs = "2.1.5" gradle-maven-publish-plugin = "0.32.0" @@ -14,43 +14,44 @@ androidx-browser = "1.10.0" androidx-cardview = "1.0.0" # Keep compatibility with https://kotlinlang.org/docs/multiplatform/compose-compatibility-and-versioning.html#jetpack-compose-artifacts-used -androidx-compose-animation = "1.10.5" -androidx-compose-foundation = "1.10.5" -androidx-compose-material = "1.10.5" -androidx-compose-runtime = "1.10.5" -androidx-compose-ui = "1.10.5" +androidx-compose-animation = "1.11.2" +androidx-compose-foundation = "1.11.2" +androidx-compose-material = "1.11.2" +androidx-compose-runtime = "1.11.2" +androidx-compose-ui = "1.11.2" androidx-compose-material3 = "1.4.0" androidx-constraintlayout = "2.2.1" -androidx-core = "1.18.0" +androidx-core = "1.19.0" androidx-datastore = "1.2.1" androidx-fragment-ktx = "1.8.9" androidx-legacy = "1.0.0" androidx-lifecycle = "2.10.0" -androidx-media3 = "1.10.0" -androidx-navigation = "2.9.7" -androidx-paging = "3.4.2" +androidx-media3 = "1.10.1" +androidx-navigation = "2.9.8" +androidx-paging = "3.5.0" androidx-recyclerview = "1.4.0" androidx-room = "2.8.4" androidx-viewpager2 = "1.1.0" +# Do not update until minSdk is 24 androidx-webkit = "1.15.0" assertj = "3.27.7" dokka = "2.2.0" -google-material = "1.13.0" +google-material = "1.14.0" jsoup = "1.22.2" junit = "4.13.2" -kotlinx-coroutines = "1.10.2" -kotlinx-coroutines-test = "1.10.2" -kotlinx-datetime = "0.7.1" -kotlinx-serialization-json = "1.10.0" -kotlinx-collections-immutable = "0.4.0" +kotlinx-coroutines = "1.11.0" +kotlinx-coroutines-test = "1.11.0" +kotlinx-datetime = "0.8.0" +kotlinx-serialization-json = "1.11.0" +kotlinx-collections-immutable = "0.5.0" -ksp = "2.3.4" +ksp = "2.3.9" ktlint = "14.2.0" @@ -62,7 +63,7 @@ picasso = "2.8" pspdfkit = "8.4.1" robolectric = "4.16.1" -mockk = "1.14.9" +mockk = "1.14.11" timber = "5.0.1" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d706aba609..efb903f917 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From d6b49c4d369a6d0eab7d099e6fd4e002c5cbbea2 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:57:44 -0500 Subject: [PATCH 02/27] Fix various deprecations --- .../demo/navigator/decorations/HighlightsManager.kt | 9 ++++----- .../java/org/readium/r2/shared/extensions/JSON.kt | 1 + .../r2/testapp/reader/VisualReaderFragment.kt | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/demos/navigator/src/main/java/org/readium/demo/navigator/decorations/HighlightsManager.kt b/demos/navigator/src/main/java/org/readium/demo/navigator/decorations/HighlightsManager.kt index e2cfefb818..10ca9d0d87 100644 --- a/demos/navigator/src/main/java/org/readium/demo/navigator/decorations/HighlightsManager.kt +++ b/demos/navigator/src/main/java/org/readium/demo/navigator/decorations/HighlightsManager.kt @@ -17,7 +17,6 @@ import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.update import org.readium.navigator.common.Decoration import org.readium.navigator.common.DecorationLocation @@ -90,14 +89,14 @@ sealed class HighlightsManager( tint = tint, annotation = annotation ) - highlightsMutable.update { it.put(id, highlight) } + highlightsMutable.update { it.putting(id, highlight) } return id } fun updateHighlightAnnotation(id: Long, annotation: String) { val highlight = checkNotNull(highlightsMutable.value[id]) .copy(annotation = annotation) - highlightsMutable.update { it.put(id, highlight) } + highlightsMutable.update { it.putting(id, highlight) } } fun updateHighlightStyle( @@ -108,11 +107,11 @@ sealed class HighlightsManager( val originalHighlight = checkNotNull(highlightsMutable.value[id]) val highlight = originalHighlight .copy(style = style ?: originalHighlight.style, tint = tint ?: originalHighlight.tint) - highlightsMutable.update { it.put(id, highlight) } + highlightsMutable.update { it.putting(id, highlight) } } fun deleteHighlight(id: Long) { - highlightsMutable.update { it.remove(id) } + highlightsMutable.update { it.removing(id) } } } diff --git a/readium/shared/src/main/java/org/readium/r2/shared/extensions/JSON.kt b/readium/shared/src/main/java/org/readium/r2/shared/extensions/JSON.kt index 54c2a542bb..58a5ddd71f 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/extensions/JSON.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/extensions/JSON.kt @@ -277,6 +277,7 @@ public fun JSONObject.optStringsFromArrayOrSingle(name: String, remove: Boolean return when (value) { is JSONArray -> value.toList().filterIsInstance(String::class.java) is String -> listOf(value) + null -> emptyList() else -> emptyList() } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/VisualReaderFragment.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/VisualReaderFragment.kt index 248fc034c9..275178dcf3 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/VisualReaderFragment.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/VisualReaderFragment.kt @@ -20,7 +20,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.view.WindowInsets -import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.ImageButton import android.widget.LinearLayout @@ -45,6 +44,8 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.dp import androidx.core.view.MenuHost import androidx.core.view.MenuProvider +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -518,13 +519,12 @@ abstract class VisualReaderFragment : BaseReaderFragment() { .create() fun dismiss() { + alert.window?.let { window -> + WindowCompat.getInsetsController(window, note).hide(WindowInsetsCompat.Type.ime()) + } + alert.dismiss() mode?.finish() - (activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) - .hideSoftInputFromWindow( - note.applicationWindowToken, - InputMethodManager.HIDE_NOT_ALWAYS - ) } with(view) { From a565a7adf10c90065fd15dd897c3cd8310d0d361 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:39:28 -0500 Subject: [PATCH 03/27] Use backing fields --- .../exoplayer/audio/ExoPlayerEngine.kt | 14 ++++---------- .../navigator/PdfiumDocumentFragment.kt | 8 ++++---- .../navigator/PsPdfKitDocumentFragment.kt | 6 +++--- .../navigator/epub/EpubNavigatorViewModel.kt | 9 ++++----- .../navigator/image/ImageNavigatorFragment.kt | 14 +++++++------- .../r2/navigator/pager/R2EpubPageFragment.kt | 19 +++++++++++++------ .../r2/navigator/pdf/PdfNavigatorViewModel.kt | 15 +++++---------- .../media/tts/android/AndroidTtsEngine.kt | 13 +++++-------- .../r2/shared/util/logging/WarningLogger.kt | 6 +++--- .../r2/testapp/reader/ReaderViewModel.kt | 9 ++++----- 10 files changed, 52 insertions(+), 61 deletions(-) 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 b5d176fc39..ddfd6421b1 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 @@ -178,7 +178,7 @@ public class ExoPlayerEngine private constructor( } override fun onEvents(player: Player, events: Player.Events) { - _playback.value = exoPlayer.playback + playback.value = exoPlayer.playback } } @@ -201,12 +201,6 @@ public class ExoPlayerEngine private constructor( exoPlayer.addListener(Listener()) } - private val _settings: MutableStateFlow = - MutableStateFlow(settingsResolver.settings(initialPreferences)) - - private val _playback: MutableStateFlow = - MutableStateFlow(exoPlayer.playback) - private val sessionPlayer = object : ForwardingPlayer(exoPlayer) { @@ -228,7 +222,7 @@ public class ExoPlayerEngine private constructor( val positionRefreshDelay = (1.0 / configuration.positionRefreshRate.value).seconds while (isActive) { delay(positionRefreshDelay) - _playback.value = exoPlayer.playback + playback.value = exoPlayer.playback } } @@ -236,10 +230,10 @@ public class ExoPlayerEngine private constructor( } override val playback: StateFlow - get() = _playback.asStateFlow() + field = MutableStateFlow(exoPlayer.playback) override val settings: StateFlow - get() = _settings.asStateFlow() + field = MutableStateFlow(settingsResolver.settings(initialPreferences)) override fun play() { exoPlayer.play() diff --git a/readium/adapters/pdfium/navigator/src/main/java/org/readium/adapter/pdfium/navigator/PdfiumDocumentFragment.kt b/readium/adapters/pdfium/navigator/src/main/java/org/readium/adapter/pdfium/navigator/PdfiumDocumentFragment.kt index 447b628ef5..bd733d3e76 100644 --- a/readium/adapters/pdfium/navigator/src/main/java/org/readium/adapter/pdfium/navigator/PdfiumDocumentFragment.kt +++ b/readium/adapters/pdfium/navigator/src/main/java/org/readium/adapter/pdfium/navigator/PdfiumDocumentFragment.kt @@ -92,7 +92,7 @@ public class PdfiumDocumentFragment internal constructor( private lateinit var resetJob: SingleJob - private fun reset(pageIndex: Int = _pageIndex.value) { + private fun reset(pageIndex: Int = this.pageIndex.value) { if (view == null) return val context = context?.applicationContext ?: return @@ -137,7 +137,7 @@ public class PdfiumDocumentFragment internal constructor( } } .onPageChange { index, _ -> - _pageIndex.value = convertPageIndexFromView(index) + this@PdfiumDocumentFragment.pageIndex.value = convertPageIndexFromView(index) } .onTap { event -> listener?.onTap(PointF(event.x, event.y)) ?: false @@ -148,8 +148,8 @@ public class PdfiumDocumentFragment internal constructor( private var pageCount = 0 - private val _pageIndex = MutableStateFlow(initialPageIndex) - override val pageIndex: StateFlow = _pageIndex.asStateFlow() + override val pageIndex: StateFlow + field = MutableStateFlow(initialPageIndex) override fun goToPageIndex(index: Int, animated: Boolean): Boolean { if (!isValidPageIndex(index)) { diff --git a/readium/adapters/pspdfkit/navigator/src/main/java/org/readium/adapter/pspdfkit/navigator/PsPdfKitDocumentFragment.kt b/readium/adapters/pspdfkit/navigator/src/main/java/org/readium/adapter/pspdfkit/navigator/PsPdfKitDocumentFragment.kt index 858d4cfb9d..daeb74017f 100644 --- a/readium/adapters/pspdfkit/navigator/src/main/java/org/readium/adapter/pspdfkit/navigator/PsPdfKitDocumentFragment.kt +++ b/readium/adapters/pspdfkit/navigator/src/main/java/org/readium/adapter/pspdfkit/navigator/PsPdfKitDocumentFragment.kt @@ -233,8 +233,8 @@ public class PsPdfKitDocumentFragment internal constructor( return config.build() } - private val _pageIndex = MutableStateFlow(initialPageIndex) - override val pageIndex: StateFlow = _pageIndex.asStateFlow() + override val pageIndex: StateFlow + field = MutableStateFlow(initialPageIndex) override fun goToPageIndex(index: Int, animated: Boolean): Boolean { val fragment = pdfFragment ?: return false @@ -263,7 +263,7 @@ public class PsPdfKitDocumentFragment internal constructor( private inner class PsPdfKitListener : DocumentListener, OnPreparePopupToolbarListener { override fun onPageChanged(document: PdfDocument, pageIndex: Int) { - _pageIndex.value = pageIndex + this@PsPdfKitDocumentFragment.pageIndex.value = pageIndex } override fun onDocumentClick(): Boolean { diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt index 3d276c20cf..e6af0c2006 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt @@ -81,11 +81,10 @@ internal class EpubNavigatorViewModel( private val settingsPolicy: EpubSettingsResolver = EpubSettingsResolver(publication.metadata, defaults) - private val _settings: MutableStateFlow = - MutableStateFlow(settingsPolicy.settings(initialPreferences)) - val settings: StateFlow = _settings.asStateFlow() + val settings: StateFlow + field = MutableStateFlow(settingsPolicy.settings(initialPreferences)) - val overflow: StateFlow = _settings + val overflow: StateFlow = settings .mapStateIn(viewModelScope) { settings -> SimpleOverflow( readingProgression = settings.readingProgression, @@ -214,7 +213,7 @@ internal class EpubNavigatorViewModel( val oldSettings = settings.value val newSettings = settingsPolicy.settings(preferences) - _settings.value = newSettings + settings.value = newSettings css.update { it.update(newSettings, useReadiumCssFontSize = config.useReadiumCssFontSize) } val needsInvalidation: Boolean = ( diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt index 5da9b86b9b..b6a83db426 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt @@ -69,11 +69,11 @@ public class ImageNavigatorFragment private constructor( internal lateinit var adapter: R2PagerAdapter private lateinit var currentActivity: FragmentActivity - override val currentLocator: StateFlow get() = _currentLocator - private val _currentLocator = MutableStateFlow( - initialLocator?.let { publication.normalizeLocator(it) } - ?: requireNotNull(publication.locatorFromLink(publication.readingOrder.first())) - ) + override val currentLocator: StateFlow + field = MutableStateFlow( + initialLocator?.let { publication.normalizeLocator(it) } + ?: requireNotNull(publication.locatorFromLink(publication.readingOrder.first())) + ) private var currentPagerPosition: Int = 0 internal var resources: List = emptyList() @@ -160,10 +160,10 @@ public class ImageNavigatorFragment private constructor( private fun notifyCurrentLocation() { val locator = positions.getOrNull(resourcePager.currentItem) - ?.takeUnless { it == _currentLocator.value } + ?.takeUnless { it == currentLocator.value } ?: return - _currentLocator.value = locator + currentLocator.value = locator } override fun go(locator: Locator, animated: Boolean): Boolean { diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt index 97348b11e8..570104458e 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt @@ -15,7 +15,12 @@ import android.annotation.SuppressLint import android.graphics.PointF import android.os.Bundle import android.util.DisplayMetrics -import android.view.* +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView @@ -34,7 +39,10 @@ import androidx.webkit.WebViewFeature import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlin.math.roundToInt -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.readium.r2.navigator.R import org.readium.r2.navigator.R2BasicWebView @@ -76,7 +84,6 @@ internal class R2EpubPageFragment : Fragment() { private val binding get() = _binding!! private var isLoading: Boolean = false - private val _isLoaded = MutableStateFlow(false) internal fun setFontSize(fontSize: Double) { textZoom = (fontSize * 100).roundToInt() @@ -93,7 +100,7 @@ internal class R2EpubPageFragment : Fragment() { */ @InternalReadiumApi val isLoaded: StateFlow - get() = _isLoaded.asStateFlow() + field = MutableStateFlow(false) /** * Waits for the page to be loaded. @@ -241,7 +248,7 @@ internal class R2EpubPageFragment : Fragment() { resourceUrl?.let { isLoading = true - _isLoaded.value = false + isLoaded.value = false webView.loadUrl(it.toString()) } @@ -392,7 +399,7 @@ internal class R2EpubPageFragment : Fragment() { private fun onLoadPage() { if (!isLoading) return isLoading = false - _isLoaded.value = true + isLoaded.value = true if (view == null) return diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pdf/PdfNavigatorViewModel.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pdf/PdfNavigatorViewModel.kt index 8020631423..662d946d32 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pdf/PdfNavigatorViewModel.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pdf/PdfNavigatorViewModel.kt @@ -32,21 +32,16 @@ internal class PdfNavigatorViewModel, ) : AndroidViewModel(application) { - private val _currentLocator: MutableStateFlow = - MutableStateFlow( + val currentLocator: StateFlow + field = MutableStateFlow( requireNotNull(publication.locatorFromLink(publication.readingOrder.first())) .copy(locations = initialLocations ?: Locator.Locations()) ) - val currentLocator: StateFlow = _currentLocator.asStateFlow() - - private val _settings: MutableStateFlow = - MutableStateFlow(computeSettings(initialPreferences)) - - val settings: StateFlow = _settings.asStateFlow() + val settings: StateFlow field = MutableStateFlow(computeSettings(initialPreferences)) fun submitPreferences(preferences: P) = viewModelScope.launch { - _settings.value = computeSettings(preferences) + settings.value = computeSettings(preferences) } private fun computeSettings(preferences: P): S = @@ -54,7 +49,7 @@ internal class PdfNavigatorViewModel - _currentLocator.value = locator + currentLocator.value = locator } } 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 e836661149..558c6a64fb 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 @@ -257,10 +257,6 @@ public class AndroidTtsEngine private constructor( private val coroutineScope: CoroutineScope = MainScope() - private val _settings: MutableStateFlow = - MutableStateFlow(settingsResolver.settings(initialPreferences)) - .apply { engine.setupPitchAndSpeed(value) } - private var utteranceListener: TtsEngine.Listener? = null @@ -270,12 +266,13 @@ public class AndroidTtsEngine private constructor( private var isClosed: Boolean = false - override val settings: StateFlow = - _settings.asStateFlow() + override val settings: StateFlow + field = MutableStateFlow(settingsResolver.settings(initialPreferences)) + .apply { engine.setupPitchAndSpeed(value) } override fun submitPreferences(preferences: AndroidTtsPreferences) { val newSettings = settingsResolver.settings(preferences) - _settings.value = newSettings + settings.value = newSettings (state as? State.EngineAvailable) ?.engine?.setupPitchAndSpeed(newSettings) } @@ -369,7 +366,7 @@ public class AndroidTtsEngine private constructor( private fun onReconnectionSucceeded(engine: TextToSpeech) { val previousState = state as State.WaitingForService setupListener(engine) - engine.setupPitchAndSpeed(_settings.value) + engine.setupPitchAndSpeed(settings.value) state = State.EngineAvailable(engine) if (isClosed) { engine.shutdown() diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/logging/WarningLogger.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/logging/WarningLogger.kt index be46174778..47cfaad42e 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/logging/WarningLogger.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/logging/WarningLogger.kt @@ -32,11 +32,11 @@ public class ListWarningLogger : WarningLogger { /** * The list of accumulated [Warning]s. */ - public val warnings: List get() = _warnings - private val _warnings = mutableListOf() + public val warnings: List + field: MutableList = mutableListOf() override fun log(warning: Warning) { - _warnings.add(warning) + warnings.add(warning) } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderViewModel.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderViewModel.kt index d59ff652cc..5d81c1a15c 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderViewModel.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderViewModel.kt @@ -220,7 +220,7 @@ class ReaderViewModel( fun search(query: String) = viewModelScope.launch { if (query == lastSearchQuery) return@launch lastSearchQuery = query - _searchLocators.value = emptyList() + searchLocators.value = emptyList() searchIterator = publication.search(query) ?: run { activityChannel.send( @@ -235,14 +235,13 @@ class ReaderViewModel( } fun cancelSearch() = viewModelScope.launch { - _searchLocators.value = emptyList() + searchLocators.value = emptyList() searchIterator?.close() searchIterator = null pagingSourceFactory.invalidate() } - val searchLocators: StateFlow> get() = _searchLocators - private var _searchLocators = MutableStateFlow>(emptyList()) + val searchLocators: StateFlow> field = MutableStateFlow>(emptyList()) /** * Maps the current list of search result locators into a list of [Decoration] objects to @@ -309,7 +308,7 @@ class ReaderViewModel( override suspend fun next(): SearchTry { val iterator = searchIterator ?: return Try.success(null) return iterator.next().onSuccess { - _searchLocators.value += (it?.locators ?: emptyList()) + searchLocators.value += (it?.locators ?: emptyList()) } } } From 84091355937f0bdf43cfedc5a324f57b5f3e8009 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Mon, 6 Jul 2026 09:38:21 -0500 Subject: [PATCH 04/27] Update to Room 3 --- gradle/libs.versions.toml | 15 +++++----- readium/lcp/build.gradle.kts | 9 ++++-- .../org/readium/r2/lcp/persistence/LcpDao.kt | 10 +++---- .../readium/r2/lcp/persistence/LcpDatabase.kt | 30 +++++++++---------- .../org/readium/r2/lcp/persistence/License.kt | 6 ++-- .../readium/r2/lcp/persistence/Passphrase.kt | 6 ++-- settings.gradle.kts | 21 +++++++++++-- test-app/build.gradle.kts | 10 +++++-- .../readium/r2/testapp/data/db/AppDatabase.kt | 13 ++++---- .../readium/r2/testapp/data/db/BooksDao.kt | 8 ++--- .../readium/r2/testapp/data/db/CatalogDao.kt | 8 ++--- .../org/readium/r2/testapp/data/model/Book.kt | 6 ++-- .../readium/r2/testapp/data/model/Bookmark.kt | 8 ++--- .../readium/r2/testapp/data/model/Catalog.kt | 6 ++-- .../r2/testapp/data/model/Highlight.kt | 19 +++++++----- 15 files changed, 102 insertions(+), 73 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81e7876fec..80e6d296b8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,12 +26,12 @@ androidx-core = "1.19.0" androidx-datastore = "1.2.1" androidx-fragment-ktx = "1.8.9" androidx-legacy = "1.0.0" -androidx-lifecycle = "2.10.0" +androidx-lifecycle = "2.11.0" androidx-media3 = "1.10.1" androidx-navigation = "2.9.8" androidx-paging = "3.5.0" androidx-recyclerview = "1.4.0" -androidx-room = "2.8.4" +androidx-room3 = "3.0.0" androidx-viewpager2 = "1.1.0" # Do not update until minSdk is 24 androidx-webkit = "1.15.0" @@ -49,7 +49,7 @@ kotlinx-coroutines = "1.11.0" kotlinx-coroutines-test = "1.11.0" kotlinx-datetime = "0.8.0" kotlinx-serialization-json = "1.11.0" -kotlinx-collections-immutable = "0.5.0" +kotlinx-collections-immutable = "0.5.1" ksp = "2.3.9" @@ -96,9 +96,8 @@ androidx-navigation-fragment = { group = "androidx.navigation", name = "navigati androidx-navigation-ui = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "androidx-navigation" } androidx-paging = { group = "androidx.paging", name = "paging-runtime-ktx", version.ref = "androidx-paging" } androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidx-recyclerview" } -androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androidx-room" } -androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androidx-room" } -androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androidx-room" } +androidx-room3 = { group = "androidx.room3", name = "room3-runtime", version.ref = "androidx-room3" } +androidx-room3-compiler = { group = "androidx.room3", name = "room3-compiler", version.ref = "androidx-room3" } androidx-viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "androidx-viewpager2" } androidx-webkit = { group = "androidx.webkit", name = "webkit", version.ref = "androidx-webkit" } @@ -137,13 +136,13 @@ plugin-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref [plugins] +androidx-room3 = { id = "androidx.room3", version.ref = "androidx-room3" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } -compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } [bundles] compose = ["androidx-compose-activity", "androidx-compose-animation", "androidx-compose-foundation", "androidx-compose-material", "androidx-compose-material3", "androidx-compose-runtime", "androidx-compose-ui", "androidx-compose-ui-tooling"] media3 = ["androidx-media3-session", "androidx-media3-common", "androidx-media3-exoplayer"] -room = ["androidx-room-runtime", "androidx-room-ktx"] diff --git a/readium/lcp/build.gradle.kts b/readium/lcp/build.gradle.kts index 65028962a7..363fa4a59f 100644 --- a/readium/lcp/build.gradle.kts +++ b/readium/lcp/build.gradle.kts @@ -7,6 +7,7 @@ plugins { id("readium.library-conventions") alias(libs.plugins.ksp) + alias(libs.plugins.androidx.room3) } android { @@ -21,6 +22,10 @@ kotlin { } } +room3 { + schemaDirectory("$projectDir/schemas") +} + dependencies { implementation(libs.kotlinx.coroutines.android) @@ -33,8 +38,8 @@ dependencies { implementation(libs.androidx.browser) implementation(libs.kotlinx.datetime) - implementation(libs.bundles.room) - ksp(libs.androidx.room.compiler) + implementation(libs.androidx.room3) + ksp(libs.androidx.room3.compiler) // Tests testImplementation(libs.junit) diff --git a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDao.kt b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDao.kt index c4c0b37c46..0d93830ca1 100644 --- a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDao.kt +++ b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDao.kt @@ -1,10 +1,10 @@ package org.readium.r2.lcp.persistence -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction +import androidx.room3.Dao +import androidx.room3.Insert +import androidx.room3.OnConflictStrategy +import androidx.room3.Query +import androidx.room3.Transaction import kotlinx.coroutines.flow.Flow @Dao diff --git a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDatabase.kt b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDatabase.kt index d7744f9bc1..e034dfc17d 100644 --- a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDatabase.kt +++ b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/LcpDatabase.kt @@ -10,11 +10,12 @@ package org.readium.r2.lcp.persistence import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase +import androidx.room3.Database +import androidx.room3.Room +import androidx.room3.RoomDatabase +import androidx.room3.migration.Migration +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.execSQL @Database( entities = [Passphrase::class, License::class], @@ -35,8 +36,8 @@ internal abstract class LcpDatabase : RoomDatabase() { return tempInstance } val MIGRATION_1_2 = object : Migration(1, 2) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( + override suspend fun migrate(connection: SQLiteConnection) { + connection.execSQL( """ CREATE TABLE passphrases ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -47,15 +48,15 @@ internal abstract class LcpDatabase : RoomDatabase() { ) """.trimIndent() ) - db.execSQL( + connection.execSQL( """ INSERT INTO passphrases (license_id, provider, user_id, passphrase) SELECT id, origin, userId, passphrase FROM Transactions """.trimIndent() ) - db.execSQL("DROP TABLE Transactions") + connection.execSQL("DROP TABLE Transactions") - db.execSQL( + connection.execSQL( """ CREATE TABLE new_Licenses ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -66,20 +67,19 @@ internal abstract class LcpDatabase : RoomDatabase() { ) """.trimIndent() ) - db.execSQL( + connection.execSQL( """ INSERT INTO new_Licenses (license_id, right_print, right_copy, registered) SELECT id, printsLeft, copiesLeft, registered FROM Licenses """.trimIndent() ) - db.execSQL("DROP TABLE Licenses") - db.execSQL("ALTER TABLE new_Licenses RENAME TO licenses") + connection.execSQL("DROP TABLE Licenses") + connection.execSQL("ALTER TABLE new_Licenses RENAME TO licenses") } } synchronized(this) { - val instance = Room.databaseBuilder( + val instance = Room.databaseBuilder( context.applicationContext, - LcpDatabase::class.java, "lcpdatabase" ).addMigrations(MIGRATION_1_2).build() INSTANCE = instance diff --git a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/License.kt b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/License.kt index 8e4884445e..6debd29b49 100644 --- a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/License.kt +++ b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/License.kt @@ -9,9 +9,9 @@ package org.readium.r2.lcp.persistence -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey +import androidx.room3.ColumnInfo +import androidx.room3.Entity +import androidx.room3.PrimaryKey @Entity(tableName = License.TABLE_NAME) internal data class License( diff --git a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/Passphrase.kt b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/Passphrase.kt index 2cf94fcda3..8ad3b4f25d 100644 --- a/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/Passphrase.kt +++ b/readium/lcp/src/main/java/org/readium/r2/lcp/persistence/Passphrase.kt @@ -9,9 +9,9 @@ package org.readium.r2.lcp.persistence -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey +import androidx.room3.ColumnInfo +import androidx.room3.Entity +import androidx.room3.PrimaryKey @Entity(tableName = Passphrase.TABLE_NAME) internal data class Passphrase( diff --git a/settings.gradle.kts b/settings.gradle.kts index dd81830f54..706ae0e2d5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,12 +1,27 @@ -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" -} /* * Copyright 2021 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. */ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { diff --git a/test-app/build.gradle.kts b/test-app/build.gradle.kts index b313f16c5d..ddd47744a1 100644 --- a/test-app/build.gradle.kts +++ b/test-app/build.gradle.kts @@ -9,6 +9,7 @@ plugins { kotlin("plugin.parcelize") alias(libs.plugins.ksp) alias(libs.plugins.compose.compiler) + alias(libs.plugins.androidx.room3) } android { @@ -63,9 +64,14 @@ kotlin { compilerOptions { languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3 freeCompilerArgs.add("-Xannotation-default-target=param-property") + freeCompilerArgs.add("-XXLanguage:+ExplicitBackingFields") } } +room3 { + schemaDirectory("$projectDir/schemas") +} + dependencies { coreLibraryDesugaring(libs.desugar.jdk.libs) @@ -112,6 +118,6 @@ dependencies { implementation(libs.bundles.media3) // Room database - implementation(libs.bundles.room) - ksp(libs.androidx.room.compiler) + implementation(libs.androidx.room3) + ksp(libs.androidx.room3.compiler) } diff --git a/test-app/src/main/java/org/readium/r2/testapp/data/db/AppDatabase.kt b/test-app/src/main/java/org/readium/r2/testapp/data/db/AppDatabase.kt index e97f0eaf2a..75a74a0c95 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/data/db/AppDatabase.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/data/db/AppDatabase.kt @@ -7,10 +7,10 @@ package org.readium.r2.testapp.data.db import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.TypeConverters +import androidx.room3.ColumnTypeConverters +import androidx.room3.Database +import androidx.room3.Room +import androidx.room3.RoomDatabase import org.readium.r2.testapp.data.model.* import org.readium.r2.testapp.data.model.Book import org.readium.r2.testapp.data.model.Bookmark @@ -22,7 +22,7 @@ import org.readium.r2.testapp.data.model.Highlight version = 1, exportSchema = false ) -@TypeConverters( +@ColumnTypeConverters( HighlightConverters::class ) abstract class AppDatabase : RoomDatabase() { @@ -41,9 +41,8 @@ abstract class AppDatabase : RoomDatabase() { return tempInstance } synchronized(this) { - val instance = Room.databaseBuilder( + val instance = Room.databaseBuilder( context.applicationContext, - AppDatabase::class.java, "database" ).build() INSTANCE = instance diff --git a/test-app/src/main/java/org/readium/r2/testapp/data/db/BooksDao.kt b/test-app/src/main/java/org/readium/r2/testapp/data/db/BooksDao.kt index e448185b15..eebce06afc 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/data/db/BooksDao.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/data/db/BooksDao.kt @@ -7,10 +7,10 @@ package org.readium.r2.testapp.data.db import androidx.annotation.ColorInt -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query +import androidx.room3.Dao +import androidx.room3.Insert +import androidx.room3.OnConflictStrategy +import androidx.room3.Query import kotlinx.coroutines.flow.Flow import org.readium.r2.testapp.data.model.Book import org.readium.r2.testapp.data.model.Bookmark diff --git a/test-app/src/main/java/org/readium/r2/testapp/data/db/CatalogDao.kt b/test-app/src/main/java/org/readium/r2/testapp/data/db/CatalogDao.kt index 04f5c934af..e7948cefc1 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/data/db/CatalogDao.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/data/db/CatalogDao.kt @@ -6,10 +6,10 @@ package org.readium.r2.testapp.data.db -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query +import androidx.room3.Dao +import androidx.room3.Insert +import androidx.room3.OnConflictStrategy +import androidx.room3.Query import kotlinx.coroutines.flow.Flow import org.readium.r2.testapp.data.model.Catalog diff --git a/test-app/src/main/java/org/readium/r2/testapp/data/model/Book.kt b/test-app/src/main/java/org/readium/r2/testapp/data/model/Book.kt index 9995e77e4a..0437202ee1 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/data/model/Book.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/data/model/Book.kt @@ -6,9 +6,9 @@ package org.readium.r2.testapp.data.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey +import androidx.room3.ColumnInfo +import androidx.room3.Entity +import androidx.room3.PrimaryKey import org.readium.r2.shared.util.AbsoluteUrl import org.readium.r2.shared.util.mediatype.MediaType diff --git a/test-app/src/main/java/org/readium/r2/testapp/data/model/Bookmark.kt b/test-app/src/main/java/org/readium/r2/testapp/data/model/Bookmark.kt index ca00d35ab8..f17eaa4af9 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/data/model/Bookmark.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/data/model/Bookmark.kt @@ -6,10 +6,10 @@ package org.readium.r2.testapp.data.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Index -import androidx.room.PrimaryKey +import androidx.room3.ColumnInfo +import androidx.room3.Entity +import androidx.room3.Index +import androidx.room3.PrimaryKey import org.json.JSONObject import org.readium.r2.shared.publication.Locator import org.readium.r2.shared.util.Url diff --git a/test-app/src/main/java/org/readium/r2/testapp/data/model/Catalog.kt b/test-app/src/main/java/org/readium/r2/testapp/data/model/Catalog.kt index f84c82936a..152838a980 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/data/model/Catalog.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/data/model/Catalog.kt @@ -7,9 +7,9 @@ package org.readium.r2.testapp.data.model import android.os.Parcelable -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey +import androidx.room3.ColumnInfo +import androidx.room3.Entity +import androidx.room3.PrimaryKey import kotlinx.parcelize.Parcelize @Parcelize diff --git a/test-app/src/main/java/org/readium/r2/testapp/data/model/Highlight.kt b/test-app/src/main/java/org/readium/r2/testapp/data/model/Highlight.kt index 4d6b8d7467..8e14cf205f 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/data/model/Highlight.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/data/model/Highlight.kt @@ -7,7 +7,12 @@ package org.readium.r2.testapp.data.model import androidx.annotation.ColorInt -import androidx.room.* +import androidx.room3.ColumnInfo +import androidx.room3.ColumnTypeConverter +import androidx.room3.Entity +import androidx.room3.ForeignKey +import androidx.room3.Index +import androidx.room3.PrimaryKey import org.json.JSONObject import org.readium.r2.shared.publication.Locator import org.readium.r2.shared.util.Url @@ -129,25 +134,25 @@ data class Highlight( } class HighlightConverters { - @TypeConverter + @ColumnTypeConverter fun styleFromString(value: String?): Highlight.Style = Highlight.Style.getOrDefault(value) - @TypeConverter + @ColumnTypeConverter fun styleToString(style: Highlight.Style): String = style.value - @TypeConverter + @ColumnTypeConverter fun textFromString(value: String?): Locator.Text = Locator.Text.fromJSON( value?.let { JSONObject(it) } ) - @TypeConverter + @ColumnTypeConverter fun textToString(text: Locator.Text): String = text.toJSON().toString() - @TypeConverter + @ColumnTypeConverter fun locationsFromString(value: String?): Locator.Locations = Locator.Locations.fromJSON( value?.let { JSONObject(it) } ) - @TypeConverter + @ColumnTypeConverter fun locationsToString(text: Locator.Locations): String = text.toJSON().toString() } From 590f36c98c71b9762c8c9f1ad506d287edd42abe Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:40:54 -0500 Subject: [PATCH 05/27] Update languageVersion in build.gradle files --- buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts | 2 +- demos/navigator/build.gradle.kts | 2 +- readium/lcp/build.gradle.kts | 2 +- readium/navigator/build.gradle.kts | 2 +- test-app/build.gradle.kts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts b/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts index 52b74b9ad4..fab32a641e 100644 --- a/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts @@ -48,7 +48,7 @@ kotlin { compilerOptions { freeCompilerArgs.add("-Xannotation-default-target=param-property") - languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3 + languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 allWarningsAsErrors = true } } diff --git a/demos/navigator/build.gradle.kts b/demos/navigator/build.gradle.kts index 227b96ef91..9afbf22042 100644 --- a/demos/navigator/build.gradle.kts +++ b/demos/navigator/build.gradle.kts @@ -58,7 +58,7 @@ android { kotlin { compilerOptions { - languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3 + languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn") } } diff --git a/readium/lcp/build.gradle.kts b/readium/lcp/build.gradle.kts index 363fa4a59f..36d7968984 100644 --- a/readium/lcp/build.gradle.kts +++ b/readium/lcp/build.gradle.kts @@ -16,7 +16,7 @@ android { kotlin { compilerOptions { - languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3 + languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 // See https://github.com/readium/kotlin-toolkit/pull/525#issuecomment-2300084041 freeCompilerArgs.add("-Xconsistent-data-class-copy-visibility") } diff --git a/readium/navigator/build.gradle.kts b/readium/navigator/build.gradle.kts index 14189c5b44..cb03c8f7ab 100644 --- a/readium/navigator/build.gradle.kts +++ b/readium/navigator/build.gradle.kts @@ -19,7 +19,7 @@ android { kotlin { compilerOptions { - languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3 + languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 // See https://github.com/readium/kotlin-toolkit/pull/525#issuecomment-2300084041 freeCompilerArgs.add("-Xconsistent-data-class-copy-visibility") } diff --git a/test-app/build.gradle.kts b/test-app/build.gradle.kts index ddd47744a1..6d854b1abf 100644 --- a/test-app/build.gradle.kts +++ b/test-app/build.gradle.kts @@ -62,8 +62,8 @@ android { kotlin { compilerOptions { - languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3 freeCompilerArgs.add("-Xannotation-default-target=param-property") + languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 freeCompilerArgs.add("-XXLanguage:+ExplicitBackingFields") } } From 135d418774e4f862a74c980584fd4f9b7236f2c3 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:41:11 -0500 Subject: [PATCH 06/27] Remove -Xannotation-default-target=param-property --- buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts | 1 - test-app/build.gradle.kts | 1 - 2 files changed, 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts b/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts index fab32a641e..4ade2a70d2 100644 --- a/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts @@ -47,7 +47,6 @@ kotlin { explicitApi() compilerOptions { - freeCompilerArgs.add("-Xannotation-default-target=param-property") languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 allWarningsAsErrors = true } diff --git a/test-app/build.gradle.kts b/test-app/build.gradle.kts index 6d854b1abf..fef51d9b8b 100644 --- a/test-app/build.gradle.kts +++ b/test-app/build.gradle.kts @@ -62,7 +62,6 @@ android { kotlin { compilerOptions { - freeCompilerArgs.add("-Xannotation-default-target=param-property") languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 freeCompilerArgs.add("-XXLanguage:+ExplicitBackingFields") } From e533c34a69aff7c331b85284f3ee3cd2fc1db401 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:41:30 -0500 Subject: [PATCH 07/27] Revert AGP to 9.1.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 80e6d296b8..3f13390508 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ # Keep compatibility with https://kotlinlang.org/docs/multiplatform/multiplatform-compatibility-guide.html#version-compatibility kotlin = "2.4.0" -agp = "9.2.1" +agp = "9.1.1" desugar_jdk_libs = "2.1.5" gradle-maven-publish-plugin = "0.32.0" From 35b907c5cad93e82b1c6e7c73a6fe694d250dffe Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:41:57 -0500 Subject: [PATCH 08/27] Update Kotlin to 2.4.10 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3f13390508..723a4ba2ac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] # Keep compatibility with https://kotlinlang.org/docs/multiplatform/multiplatform-compatibility-guide.html#version-compatibility -kotlin = "2.4.0" +kotlin = "2.4.10" agp = "9.1.1" desugar_jdk_libs = "2.1.5" From d6004da35789487e2f40a49d460e0190e9d30b47 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:42:03 -0500 Subject: [PATCH 09/27] Add comment about gradle version --- gradle/wrapper/gradle-wrapper.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index efb903f917..d022cb89cd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +# Keep compatibility with https://kotlinlang.org/docs/multiplatform/multiplatform-compatibility-guide.html#version-compatibility distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From c7bb57b69692c66d198199804512c5f144764ac5 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:46:56 -0500 Subject: [PATCH 10/27] Replace java.util.UUID with kotlin.uuid.Uuid --- .../readium/adapter/pspdfkit/document/ResourceDataProvider.kt | 4 ++-- .../r2/lcp/license/container/ContentZipLicenseContainer.kt | 4 ++-- .../src/main/java/org/readium/r2/lcp/service/DeviceService.kt | 4 ++-- .../java/org/readium/navigator/media/tts/TtsEngineFacade.kt | 3 ++- .../org/readium/navigator/media/tts/session/TtsTimeline.kt | 3 ++- .../main/java/org/readium/r2/testapp/domain/CoverStorage.kt | 4 ++-- .../org/readium/r2/testapp/domain/PublicationRetriever.kt | 4 ++-- .../src/main/java/org/readium/r2/testapp/utils/FileSystem.kt | 4 ++-- .../main/java/org/readium/r2/testapp/utils/extensions/Uri.kt | 4 ++-- 9 files changed, 18 insertions(+), 16 deletions(-) diff --git a/readium/adapters/pspdfkit/document/src/main/java/org/readium/adapter/pspdfkit/document/ResourceDataProvider.kt b/readium/adapters/pspdfkit/document/src/main/java/org/readium/adapter/pspdfkit/document/ResourceDataProvider.kt index d3822c1b97..e709bf6377 100644 --- a/readium/adapters/pspdfkit/document/src/main/java/org/readium/adapter/pspdfkit/document/ResourceDataProvider.kt +++ b/readium/adapters/pspdfkit/document/src/main/java/org/readium/adapter/pspdfkit/document/ResourceDataProvider.kt @@ -7,7 +7,7 @@ package org.readium.adapter.pspdfkit.document import com.pspdfkit.document.providers.DataProvider -import java.util.UUID +import kotlin.uuid.Uuid import kotlinx.coroutines.runBlocking import org.readium.r2.shared.util.data.ReadError import org.readium.r2.shared.util.getOrElse @@ -47,7 +47,7 @@ internal class ResourceDataProvider( * shorter than 50 chars. This method must be implemented for caching to work properly. */ // FIXME: Check whether we need to use a persistent ID. - override fun getUid(): String = UUID.randomUUID().toString().take(50) + override fun getUid(): String = Uuid.random().toString().take(50) override fun read(size: Long, offset: Long): ByteArray = runBlocking { val range = offset until (offset + size) diff --git a/readium/lcp/src/main/java/org/readium/r2/lcp/license/container/ContentZipLicenseContainer.kt b/readium/lcp/src/main/java/org/readium/r2/lcp/license/container/ContentZipLicenseContainer.kt index 3142053df2..a19190b42b 100644 --- a/readium/lcp/src/main/java/org/readium/r2/lcp/license/container/ContentZipLicenseContainer.kt +++ b/readium/lcp/src/main/java/org/readium/r2/lcp/license/container/ContentZipLicenseContainer.kt @@ -12,8 +12,8 @@ import android.net.Uri import java.io.ByteArrayInputStream import java.io.File import java.io.FileOutputStream -import java.util.UUID import java.util.zip.ZipFile +import kotlin.uuid.Uuid import org.readium.r2.lcp.LcpError import org.readium.r2.lcp.LcpException import org.readium.r2.lcp.license.model.LicenseDocument @@ -39,7 +39,7 @@ internal class ContentZipLicenseContainer( override fun write(license: LicenseDocument) { try { - val tmpZip = File(cache, UUID.randomUUID().toString()) + val tmpZip = File(cache, Uuid.random().toString()) contentResolver.openInputStream(zipUri) ?.use { it.copyTo(FileOutputStream(tmpZip)) } ?: throw LcpException(LcpError.Container.WriteFailed(pathInZip)) diff --git a/readium/lcp/src/main/java/org/readium/r2/lcp/service/DeviceService.kt b/readium/lcp/src/main/java/org/readium/r2/lcp/service/DeviceService.kt index 0f45f43dc4..26dd384dbe 100644 --- a/readium/lcp/src/main/java/org/readium/r2/lcp/service/DeviceService.kt +++ b/readium/lcp/src/main/java/org/readium/r2/lcp/service/DeviceService.kt @@ -12,7 +12,7 @@ package org.readium.r2.lcp.service import android.content.Context import android.os.Build import androidx.core.content.edit -import java.util.UUID +import kotlin.uuid.Uuid import org.readium.r2.lcp.license.model.LicenseDocument import org.readium.r2.lcp.license.model.components.Link import org.readium.r2.shared.util.AbsoluteUrl @@ -38,7 +38,7 @@ internal class DeviceService( preferences.getString(key, null) ?.let { return it } - val id = UUID.randomUUID().toString() + val id = Uuid.random().toString() preferences.edit { putString(key, id) } return id } diff --git a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsEngineFacade.kt b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsEngineFacade.kt index e2e22d9d4e..c942745c94 100644 --- a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsEngineFacade.kt +++ b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/TtsEngineFacade.kt @@ -7,6 +7,7 @@ package org.readium.navigator.media.tts import java.util.* +import kotlin.uuid.Uuid import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.suspendCancellableCoroutine @@ -37,7 +38,7 @@ internal class TtsEngineFacade< suspendCancellableCoroutine { continuation -> continuation.invokeOnCancellation { engine.stop() } currentTask?.continuation?.cancel() - val id = TtsEngine.RequestId(UUID.randomUUID().toString()) + val id = TtsEngine.RequestId(Uuid.random().toString()) currentTask = UtteranceTask(id, continuation, onRange) engine.speak(id, text, language) } diff --git a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsTimeline.kt b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsTimeline.kt index 42d6e9a06d..3ae37605ed 100644 --- a/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsTimeline.kt +++ b/readium/navigators/media/tts/src/main/java/org/readium/navigator/media/tts/session/TtsTimeline.kt @@ -10,6 +10,7 @@ import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.Timeline import java.util.* +import kotlin.uuid.Uuid @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) internal class TtsTimeline( @@ -17,7 +18,7 @@ internal class TtsTimeline( ) : Timeline() { private val uuids = mediaItems.indices - .map { UUID.randomUUID() } + .map { Uuid.random() } override fun getWindowCount(): Int { return mediaItems.size diff --git a/test-app/src/main/java/org/readium/r2/testapp/domain/CoverStorage.kt b/test-app/src/main/java/org/readium/r2/testapp/domain/CoverStorage.kt index 151a673d72..759a8d34e8 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/domain/CoverStorage.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/domain/CoverStorage.kt @@ -4,7 +4,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import java.io.File import java.io.FileOutputStream -import java.util.UUID +import kotlin.uuid.Uuid import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.readium.r2.shared.publication.Publication @@ -55,7 +55,7 @@ class CoverStorage( private suspend fun storeCover(cover: Bitmap?): File = withContext(Dispatchers.IO) { - val coverImageFile = File(coverDir(), "${UUID.randomUUID()}.png") + val coverImageFile = File(coverDir(), "${Uuid.random()}.png") val resized = cover?.let { Bitmap.createScaledBitmap(it, 120, 200, true) } val fos = FileOutputStream(coverImageFile) resized?.compress(Bitmap.CompressFormat.PNG, 80, fos) diff --git a/test-app/src/main/java/org/readium/r2/testapp/domain/PublicationRetriever.kt b/test-app/src/main/java/org/readium/r2/testapp/domain/PublicationRetriever.kt index 6f0528a90a..d5b36dcc3a 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/domain/PublicationRetriever.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/domain/PublicationRetriever.kt @@ -9,7 +9,7 @@ package org.readium.r2.testapp.domain import android.content.Context import android.net.Uri import java.io.File -import java.util.UUID +import kotlin.uuid.Uuid import org.readium.r2.lcp.LcpService import org.readium.r2.shared.publication.Publication import org.readium.r2.shared.publication.opds.images @@ -160,7 +160,7 @@ class PublicationRetriever( return Try.failure(ImportError.Publication(PublicationError(it))) } - val fileName = "${UUID.randomUUID()}.${actualFormat.fileExtension.value}" + val fileName = "${Uuid.random()}.${actualFormat.fileExtension.value}" val bookshelfFile = File(bookshelfDir, fileName) try { diff --git a/test-app/src/main/java/org/readium/r2/testapp/utils/FileSystem.kt b/test-app/src/main/java/org/readium/r2/testapp/utils/FileSystem.kt index 88d4adeb61..a7cf24cd23 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/utils/FileSystem.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/utils/FileSystem.kt @@ -4,7 +4,7 @@ import java.io.File import java.io.FileNotFoundException import java.io.IOException import java.io.InputStream -import java.util.UUID +import kotlin.uuid.Uuid import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.readium.r2.shared.util.Try @@ -50,7 +50,7 @@ suspend fun InputStream.toFileUnsafe(file: File) { * Copies the content of this [InputStream] to a new file in [dir]. */ suspend fun InputStream.copyToNewFile(dir: File): Try { - val filename = UUID.randomUUID().toString() + val filename = Uuid.random().toString() val file = File(dir, filename) return toFile(file).map { file } } diff --git a/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Uri.kt b/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Uri.kt index 8601d2abca..0d1864c46c 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Uri.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/utils/extensions/Uri.kt @@ -13,14 +13,14 @@ import android.provider.MediaStore import java.io.File import java.io.FileNotFoundException import java.io.IOException -import java.util.UUID +import kotlin.uuid.Uuid import org.readium.r2.shared.util.Try import org.readium.r2.shared.util.content.ContentResolverError import org.readium.r2.testapp.utils.toFile import org.readium.r2.testapp.utils.tryOrNull suspend fun Uri.copyToTempFile(context: Context, dir: File): Try { - val filename = UUID.randomUUID().toString() + val filename = Uuid.random().toString() val file = File(dir, "$filename.${extension(context)}") val inputStream = try { From fe890901e9d3e77f3763456345849d2dc6517be7 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:53:27 -0500 Subject: [PATCH 11/27] Various updates --- gradle/libs.versions.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 723a4ba2ac..5fd584398b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,17 +21,17 @@ androidx-compose-runtime = "1.11.2" androidx-compose-ui = "1.11.2" androidx-compose-material3 = "1.4.0" -androidx-constraintlayout = "2.2.1" +androidx-constraintlayout = "2.2.2" androidx-core = "1.19.0" androidx-datastore = "1.2.1" androidx-fragment-ktx = "1.8.9" androidx-legacy = "1.0.0" androidx-lifecycle = "2.11.0" -androidx-media3 = "1.10.1" +androidx-media3 = "1.11.0" androidx-navigation = "2.9.8" androidx-paging = "3.5.0" androidx-recyclerview = "1.4.0" -androidx-room3 = "3.0.0" +androidx-room3 = "3.0.1" androidx-viewpager2 = "1.1.0" # Do not update until minSdk is 24 androidx-webkit = "1.15.0" @@ -42,7 +42,7 @@ dokka = "2.2.0" google-material = "1.14.0" -jsoup = "1.22.2" +jsoup = "1.23.1" junit = "4.13.2" kotlinx-coroutines = "1.11.0" From 3f32136dc2a2f529fc755c1fc7ee78516f562b10 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:53:49 -0500 Subject: [PATCH 12/27] Update maven publish plugin --- .github/workflows/publish.yml | 2 +- MAINTAINING.md | 2 +- .../src/main/kotlin/readium.library-conventions.gradle.kts | 4 +--- gradle/libs.versions.toml | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f7bffdab19..6a665213dd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,7 +19,7 @@ jobs: java-version: 17 - name: Publish to Maven Central - run: ./gradlew publishToMavenCentral --no-configuration-cache + run: ./gradlew publishToMavenCentral env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/MAINTAINING.md b/MAINTAINING.md index d7db3c91a7..d2f71cd7a0 100644 --- a/MAINTAINING.md +++ b/MAINTAINING.md @@ -49,7 +49,7 @@ If the `Publish` workflow fails, you may need to publish to Maven Central manual 1. Make sure you have the secrets in `.envrc` and [direnv](https://direnv.net) installed. 2. Run: ``` - ./gradlew publishToMavenCentral --no-configuration-cache + ./gradlew publishToMavenCentral ``` 3. Sign in to https://central.sonatype.com/publishing/deployments 4. Publish manually the components diff --git a/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts b/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts index 4ade2a70d2..7261ddc99b 100644 --- a/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/readium.library-conventions.gradle.kts @@ -1,5 +1,3 @@ -import com.vanniktech.maven.publish.SonatypeHost - plugins { // FIXME: For now, we cannot use the versions catalog in precompiled scripts: https://github.com/gradle/gradle/issues/15383 id("com.android.library") @@ -102,6 +100,6 @@ mavenPublishing { } } - publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + publishToMavenCentral() signAllPublications() } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5fd584398b..7b07e500e8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ kotlin = "2.4.10" agp = "9.1.1" desugar_jdk_libs = "2.1.5" -gradle-maven-publish-plugin = "0.32.0" +gradle-maven-publish-plugin = "0.37.0" androidx-activity = "1.13.0" androidx-annotation = "1.10.0" From d2f98a5ee75b4ba0beee6c7f7c9bdadfc748b3a3 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:06:45 -0500 Subject: [PATCH 13/27] Fix tests --- .../lcp/src/test/java/org/readium/r2/lcp/LcpDecryptorTest.kt | 2 ++ .../src/test/java/org/readium/r2/lcp/service/CRLServiceTest.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/readium/lcp/src/test/java/org/readium/r2/lcp/LcpDecryptorTest.kt b/readium/lcp/src/test/java/org/readium/r2/lcp/LcpDecryptorTest.kt index 44c9bacee9..fe83b59bd5 100644 --- a/readium/lcp/src/test/java/org/readium/r2/lcp/LcpDecryptorTest.kt +++ b/readium/lcp/src/test/java/org/readium/r2/lcp/LcpDecryptorTest.kt @@ -12,9 +12,11 @@ import org.readium.r2.shared.util.checkSuccess import org.readium.r2.shared.util.resource.InMemoryResource import org.readium.r2.shared.util.resource.Resource import org.robolectric.ParameterizedRobolectricTestRunner +import org.robolectric.annotation.Config @OptIn(ExperimentalStdlibApi::class) @RunWith(ParameterizedRobolectricTestRunner::class) +@Config(sdk = [36]) class LcpDecryptorTest(val testCase: TestCase) { data class TestCase( diff --git a/readium/lcp/src/test/java/org/readium/r2/lcp/service/CRLServiceTest.kt b/readium/lcp/src/test/java/org/readium/r2/lcp/service/CRLServiceTest.kt index f4f1f00aeb..5431012585 100644 --- a/readium/lcp/src/test/java/org/readium/r2/lcp/service/CRLServiceTest.kt +++ b/readium/lcp/src/test/java/org/readium/r2/lcp/service/CRLServiceTest.kt @@ -23,8 +23,10 @@ import org.readium.r2.shared.util.http.HttpStatus import org.readium.r2.shared.util.http.HttpStreamResponse import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) +@Config(sdk = [36]) class CRLServiceTest { class TestHttpClient : HttpClient { From 33b8ff8e11eba128e88370f8c247c3a22b4c64c5 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Thu, 13 Aug 2026 22:44:23 -0500 Subject: [PATCH 14/27] Update kotlinc.xml --- .idea/kotlinc.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 03fcfb7bea..ebd2e7b219 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,7 @@ - \ No newline at end of file From 69ce1f96a3a7d94d4728534a2c849f5f0683fbdc Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Sat, 15 Aug 2026 20:56:43 -0500 Subject: [PATCH 15/27] Update libs.versions.toml --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b07e500e8..cfaa486d1c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ gradle-maven-publish-plugin = "0.37.0" androidx-activity = "1.13.0" androidx-annotation = "1.10.0" -androidx-appcompat = "1.7.1" +androidx-appcompat = "1.8.0" androidx-browser = "1.10.0" androidx-cardview = "1.0.0" @@ -24,12 +24,12 @@ androidx-compose-material3 = "1.4.0" androidx-constraintlayout = "2.2.2" androidx-core = "1.19.0" androidx-datastore = "1.2.1" -androidx-fragment-ktx = "1.8.9" +androidx-fragment-ktx = "1.9.0" androidx-legacy = "1.0.0" androidx-lifecycle = "2.11.0" androidx-media3 = "1.11.0" androidx-navigation = "2.9.8" -androidx-paging = "3.5.0" +androidx-paging = "3.5.1" androidx-recyclerview = "1.4.0" androidx-room3 = "3.0.1" androidx-viewpager2 = "1.1.0" From 0227781277e863c3277e229834bfbf58ebc8ed89 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:15:57 -0500 Subject: [PATCH 16/27] Update minSdk to 24 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 79ed1544ba..ca0e374a99 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ pom.groupId=org.readium.kotlin-toolkit pom.version=3.3.0 -android.minSdk=23 +android.minSdk=24 android.compileSdk=37 android.targetSdk=37 From ce775b1eec7d832854a210f1ef1a2e1f5769efb1 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:16:44 -0500 Subject: [PATCH 17/27] Various updates --- gradle/libs.versions.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cfaa486d1c..23f4eb28fe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,11 +14,11 @@ androidx-browser = "1.10.0" androidx-cardview = "1.0.0" # Keep compatibility with https://kotlinlang.org/docs/multiplatform/compose-compatibility-and-versioning.html#jetpack-compose-artifacts-used -androidx-compose-animation = "1.11.2" -androidx-compose-foundation = "1.11.2" -androidx-compose-material = "1.11.2" -androidx-compose-runtime = "1.11.2" -androidx-compose-ui = "1.11.2" +androidx-compose-animation = "1.12.0" +androidx-compose-foundation = "1.12.0" +androidx-compose-material = "1.12.0" +androidx-compose-runtime = "1.12.0" +androidx-compose-ui = "1.12.0" androidx-compose-material3 = "1.4.0" androidx-constraintlayout = "2.2.2" @@ -28,10 +28,10 @@ androidx-fragment-ktx = "1.9.0" androidx-legacy = "1.0.0" androidx-lifecycle = "2.11.0" androidx-media3 = "1.11.0" -androidx-navigation = "2.9.8" +androidx-navigation = "2.10.0" androidx-paging = "3.5.1" androidx-recyclerview = "1.4.0" -androidx-room3 = "3.0.1" +androidx-room3 = "3.0.2" androidx-viewpager2 = "1.1.0" # Do not update until minSdk is 24 androidx-webkit = "1.15.0" @@ -42,14 +42,14 @@ dokka = "2.2.0" google-material = "1.14.0" -jsoup = "1.23.1" +jsoup = "1.23.2" junit = "4.13.2" kotlinx-coroutines = "1.11.0" kotlinx-coroutines-test = "1.11.0" kotlinx-datetime = "0.8.0" kotlinx-serialization-json = "1.11.0" -kotlinx-collections-immutable = "0.5.1" +kotlinx-collections-immutable = "0.5.2" ksp = "2.3.9" From 2475c452fbc07c3dcdfd7bd06cd387edba07d03e Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:16:55 -0500 Subject: [PATCH 18/27] Update WebKit --- gradle/libs.versions.toml | 3 +-- .../r2/navigator/pager/R2EpubPageFragment.kt | 20 +++++++++++++++++++ .../r2/navigator/pager/R2FXLPageFragment.kt | 19 ++++++++++++++++++ .../web/internals/server/WebViewClient.kt | 9 +++++++++ .../internals/webview/ComposableWebView.kt | 8 ++++++++ 5 files changed, 57 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 23f4eb28fe..95f58d0e37 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,8 +33,7 @@ androidx-paging = "3.5.1" androidx-recyclerview = "1.4.0" androidx-room3 = "3.0.2" androidx-viewpager2 = "1.1.0" -# Do not update until minSdk is 24 -androidx-webkit = "1.15.0" +androidx-webkit = "1.17.0" assertj = "3.27.7" diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt index 903a9d08e8..f1e970c53a 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt @@ -21,6 +21,7 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.webkit.RenderProcessGoneDetail import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView @@ -33,6 +34,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewClientCompat import androidx.webkit.WebViewCompat import androidx.webkit.WebViewFeature @@ -177,6 +179,12 @@ internal class R2EpubPageFragment : Fragment() { webView.settings.builtInZoomControls = true webView.settings.displayZoomControls = false webView.settings.textZoom = textZoom + if (WebViewFeature.isFeatureSupported(WebViewFeature.DOWNLOAD_FAVICONS_ENABLED)) { + WebSettingsCompat.setDownloadFaviconsEnabled( + webView.settings, + false + ) + } webView.resourceUrl = resourceUrl webView.setPadding(0, 0, 0, 0) webView.addJavascriptInterface(webView, "Android") @@ -238,6 +246,18 @@ internal class R2EpubPageFragment : Fragment() { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? = (webView as? R2BasicWebView)?.shouldInterceptRequest(view, request) + + override fun onRenderProcessGone( + view: WebView, + detail: RenderProcessGoneDetail, + ): Boolean { + this@R2EpubPageFragment.webView?.let { wv -> + (wv.parent as? ViewGroup)?.removeView(wv) + wv.destroy() + } + this@R2EpubPageFragment.webView = null + return true + } } webView.isHapticFeedbackEnabled = false diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt index ae7e6c6c59..af7e678ee0 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt @@ -15,13 +15,16 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.webkit.RenderProcessGoneDetail import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewClientCompat +import androidx.webkit.WebViewFeature import org.readium.r2.navigator.R2BasicWebView import org.readium.r2.navigator.databinding.ReadiumNavigatorFragmentFxllayoutDoubleBinding import org.readium.r2.navigator.databinding.ReadiumNavigatorFragmentFxllayoutSingleBinding @@ -161,6 +164,12 @@ internal class R2FXLPageFragment : Fragment() { // accessibility font size system setting which breaks the layout of some fixed layouts. // See https://github.com/readium/kotlin-toolkit/issues/76 webView.settings.textZoom = 100 + if (WebViewFeature.isFeatureSupported(WebViewFeature.DOWNLOAD_FAVICONS_ENABLED)) { + WebSettingsCompat.setDownloadFaviconsEnabled( + webView.settings, + false + ) + } webView.setInitialScale(1) @@ -183,6 +192,16 @@ internal class R2FXLPageFragment : Fragment() { webView.listener?.onPageLoaded(webView, link) } } + + override fun onRenderProcessGone( + view: WebView, + detail: RenderProcessGoneDetail, + ): Boolean { + webViews.remove(view) + (view.parent as? ViewGroup)?.removeView(view) + view.destroy() + return true + } } webView.isHapticFeedbackEnabled = false webView.isLongClickable = false diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt index 35841631b7..f7cc5a0075 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt @@ -6,6 +6,7 @@ package org.readium.navigator.web.internals.server +import android.webkit.RenderProcessGoneDetail import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView @@ -21,4 +22,12 @@ public class WebViewClient( ): WebResourceResponse? { return webViewServer.shouldInterceptRequest(request) } + + override fun onRenderProcessGone( + view: WebView, + detail: RenderProcessGoneDetail, + ): Boolean { + view.destroy() + return true + } } diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt index 7ea993c2d4..a80a5c8405 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt @@ -25,6 +25,8 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView +import androidx.webkit.WebSettingsCompat +import androidx.webkit.WebViewFeature /** * A wrapper around the Android View WebView to provide a basic WebView composable. @@ -187,6 +189,12 @@ public fun WebView( this.layoutParams = layoutParams this.webChromeClient = chromeClient this.webViewClient = client + if (WebViewFeature.isFeatureSupported(WebViewFeature.DOWNLOAD_FAVICONS_ENABLED)) { + WebSettingsCompat.setDownloadFaviconsEnabled( + this.settings, + false + ) + } state.webView = this onCreated(this) } From cb1b1b7f0a5c16d186d252b5ee6c8c2123e2b545 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:09:07 -0500 Subject: [PATCH 19/27] Update Kotlin and Gradle --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- .../adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index ebd2e7b219..7ca2a839b9 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -2,6 +2,6 @@ \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 95f58d0e37..e596e4ace8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] # Keep compatibility with https://kotlinlang.org/docs/multiplatform/multiplatform-compatibility-guide.html#version-compatibility -kotlin = "2.4.10" -agp = "9.1.1" +kotlin = "2.4.20" +agp = "9.3.1" desugar_jdk_libs = "2.1.5" gradle-maven-publish-plugin = "0.37.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d022cb89cd..df1189072e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists # Keep compatibility with https://kotlinlang.org/docs/multiplatform/multiplatform-compatibility-guide.html#version-compatibility -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt b/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt index ccf7082b71..38fdce1903 100644 --- a/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt +++ b/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt @@ -23,9 +23,11 @@ import org.readium.r2.shared.publication.services.positionsServiceFactory import org.readium.r2.shared.util.Url import org.readium.r2.shared.util.mediatype.MediaType import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config @OptIn(DelicateReadiumApi::class) @RunWith(RobolectricTestRunner::class) +@Config(sdk = [36]) class LegacyLocatorMigrationTest { private val href = Url("document.pdf")!! From a18ca9b050640f0bc1c0319fc620b097098ccaa8 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:11:13 -0500 Subject: [PATCH 20/27] Update checks.yml --- .github/workflows/checks.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 838b9aed23..fc25f2003b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -5,6 +5,7 @@ on: push: branches: [ main, develop ] pull_request: + types: [ opened, synchronize, reopened, ready_for_review ] concurrency: group: ${{ github.workflow }}-${{ github.ref }} From 40fa327e007549c2dc90fcd48669e635c2684a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Menu?= Date: Fri, 11 Sep 2026 14:34:01 +0200 Subject: [PATCH 21/27] Update README and CHANGELOG --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6d6658693..5d84c3398e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. Take a look ## [Unreleased] +:warning: The minimum supported Android version is now API 24 (Android 7.0). + ### Added #### Navigator diff --git a/README.md b/README.md index 02e6b9619c..4be73e9610 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Guides are available to help you make the most of the toolkit. | Readium | Android min SDK | Android compile SDK | Kotlin compiler (✻) | Gradle (✻) | |---------|-----------------|---------------------|---------------------|------------| -| `develop` | 23 | 36 | 2.3.20 | 9.1.0 | +| `develop` | 24 | 37 | 2.4.20 | 9.7.0 | | 3.2.0 | 23 | 36 | 2.3.20 | 9.1.0 | | 3.1.2 | 23 | 36 | 2.1.21 | 8.14.1 | | 3.1.0 | 21 | 35 | 2.1.20 | 8.13 | From e501277b6503d223682881bf93b5bf72898cb493 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:01:09 -0500 Subject: [PATCH 22/27] Remove Room3 plugin from LCP --- readium/lcp/build.gradle.kts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/readium/lcp/build.gradle.kts b/readium/lcp/build.gradle.kts index 36d7968984..576a434c2a 100644 --- a/readium/lcp/build.gradle.kts +++ b/readium/lcp/build.gradle.kts @@ -7,7 +7,6 @@ plugins { id("readium.library-conventions") alias(libs.plugins.ksp) - alias(libs.plugins.androidx.room3) } android { @@ -22,10 +21,6 @@ kotlin { } } -room3 { - schemaDirectory("$projectDir/schemas") -} - dependencies { implementation(libs.kotlinx.coroutines.android) From 74c73d6c8c3d7300b128dee0d8d9442078296388 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:01:23 -0500 Subject: [PATCH 23/27] Remove ExplicitBackingFields back compilerOptions --- test-app/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/test-app/build.gradle.kts b/test-app/build.gradle.kts index fef51d9b8b..73756ba0d6 100644 --- a/test-app/build.gradle.kts +++ b/test-app/build.gradle.kts @@ -63,7 +63,6 @@ android { kotlin { compilerOptions { languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_4 - freeCompilerArgs.add("-XXLanguage:+ExplicitBackingFields") } } From 3985630391b3b6d67bdfb212c1738932f1b99c84 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:17:57 -0500 Subject: [PATCH 24/27] Remove setDownloadFaviconsEnabled related code --- .../org/readium/r2/navigator/pager/R2EpubPageFragment.kt | 7 ------- .../org/readium/r2/navigator/pager/R2FXLPageFragment.kt | 8 -------- .../navigator/web/internals/webview/ComposableWebView.kt | 8 -------- 3 files changed, 23 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt index f1e970c53a..30a48eef32 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt @@ -34,7 +34,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewClientCompat import androidx.webkit.WebViewCompat import androidx.webkit.WebViewFeature @@ -179,12 +178,6 @@ internal class R2EpubPageFragment : Fragment() { webView.settings.builtInZoomControls = true webView.settings.displayZoomControls = false webView.settings.textZoom = textZoom - if (WebViewFeature.isFeatureSupported(WebViewFeature.DOWNLOAD_FAVICONS_ENABLED)) { - WebSettingsCompat.setDownloadFaviconsEnabled( - webView.settings, - false - ) - } webView.resourceUrl = resourceUrl webView.setPadding(0, 0, 0, 0) webView.addJavascriptInterface(webView, "Android") diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt index af7e678ee0..3ef1cc78e7 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt @@ -22,9 +22,7 @@ import android.webkit.WebView import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewClientCompat -import androidx.webkit.WebViewFeature import org.readium.r2.navigator.R2BasicWebView import org.readium.r2.navigator.databinding.ReadiumNavigatorFragmentFxllayoutDoubleBinding import org.readium.r2.navigator.databinding.ReadiumNavigatorFragmentFxllayoutSingleBinding @@ -164,12 +162,6 @@ internal class R2FXLPageFragment : Fragment() { // accessibility font size system setting which breaks the layout of some fixed layouts. // See https://github.com/readium/kotlin-toolkit/issues/76 webView.settings.textZoom = 100 - if (WebViewFeature.isFeatureSupported(WebViewFeature.DOWNLOAD_FAVICONS_ENABLED)) { - WebSettingsCompat.setDownloadFaviconsEnabled( - webView.settings, - false - ) - } webView.setInitialScale(1) diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt index a80a5c8405..7ea993c2d4 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/ComposableWebView.kt @@ -25,8 +25,6 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView -import androidx.webkit.WebSettingsCompat -import androidx.webkit.WebViewFeature /** * A wrapper around the Android View WebView to provide a basic WebView composable. @@ -189,12 +187,6 @@ public fun WebView( this.layoutParams = layoutParams this.webChromeClient = chromeClient this.webViewClient = client - if (WebViewFeature.isFeatureSupported(WebViewFeature.DOWNLOAD_FAVICONS_ENABLED)) { - WebSettingsCompat.setDownloadFaviconsEnabled( - this.settings, - false - ) - } state.webView = this onCreated(this) } From ef6f3cae46ec471a3b06a43c9110df2c1eae8ce8 Mon Sep 17 00:00:00 2001 From: Steven Zeck <8315038+stevenzeck@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:19:03 -0500 Subject: [PATCH 25/27] Move sdk to robolectric.properties --- .../adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt | 2 -- .../pdfium/navigator/src/test/resources/robolectric.properties | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 readium/adapters/pdfium/navigator/src/test/resources/robolectric.properties diff --git a/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt b/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt index 38fdce1903..ccf7082b71 100644 --- a/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt +++ b/readium/adapters/pdfium/navigator/src/test/java/org/readium/adapter/pdfium/navigator/LegacyLocatorMigrationTest.kt @@ -23,11 +23,9 @@ import org.readium.r2.shared.publication.services.positionsServiceFactory import org.readium.r2.shared.util.Url import org.readium.r2.shared.util.mediatype.MediaType import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config @OptIn(DelicateReadiumApi::class) @RunWith(RobolectricTestRunner::class) -@Config(sdk = [36]) class LegacyLocatorMigrationTest { private val href = Url("document.pdf")!! diff --git a/readium/adapters/pdfium/navigator/src/test/resources/robolectric.properties b/readium/adapters/pdfium/navigator/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..28a4ed295f --- /dev/null +++ b/readium/adapters/pdfium/navigator/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=30 From 8127e6f2b62d591e937f114a6823c3cd342ca926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Menu?= Date: Fri, 11 Sep 2026 18:10:13 +0200 Subject: [PATCH 26/27] Fixes --- .../org/readium/r2/navigator/epub/WebViewServer.kt | 3 +-- .../readium/r2/navigator/pager/R2EpubPageFragment.kt | 12 ------------ .../readium/r2/navigator/pager/R2FXLPageFragment.kt | 10 ---------- .../navigator/web/internals/server/WebViewClient.kt | 8 -------- .../navigator/web/internals/server/WebViewServer.kt | 3 +-- 5 files changed, 2 insertions(+), 34 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/WebViewServer.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/WebViewServer.kt index 0d1bbb910b..c8de9c9454 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/WebViewServer.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/WebViewServer.kt @@ -259,8 +259,7 @@ internal class WebViewServer( * from any origin, including the package domain. */ private fun WebResourceResponse.allowCors() { - responseHeaders = responseHeaders ?: mutableMapOf() - responseHeaders["Access-Control-Allow-Origin"] = "*" + responseHeaders = responseHeaders + mapOf("Access-Control-Allow-Origin" to "*") } private fun serveErrorResponse(): WebResourceResponse { diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt index f1e970c53a..179b37d5be 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt @@ -246,18 +246,6 @@ internal class R2EpubPageFragment : Fragment() { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? = (webView as? R2BasicWebView)?.shouldInterceptRequest(view, request) - - override fun onRenderProcessGone( - view: WebView, - detail: RenderProcessGoneDetail, - ): Boolean { - this@R2EpubPageFragment.webView?.let { wv -> - (wv.parent as? ViewGroup)?.removeView(wv) - wv.destroy() - } - this@R2EpubPageFragment.webView = null - return true - } } webView.isHapticFeedbackEnabled = false diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt index af7e678ee0..96033a582d 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt @@ -192,16 +192,6 @@ internal class R2FXLPageFragment : Fragment() { webView.listener?.onPageLoaded(webView, link) } } - - override fun onRenderProcessGone( - view: WebView, - detail: RenderProcessGoneDetail, - ): Boolean { - webViews.remove(view) - (view.parent as? ViewGroup)?.removeView(view) - view.destroy() - return true - } } webView.isHapticFeedbackEnabled = false webView.isLongClickable = false diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt index f7cc5a0075..7929726e44 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt @@ -22,12 +22,4 @@ public class WebViewClient( ): WebResourceResponse? { return webViewServer.shouldInterceptRequest(request) } - - override fun onRenderProcessGone( - view: WebView, - detail: RenderProcessGoneDetail, - ): Boolean { - view.destroy() - return true - } } diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewServer.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewServer.kt index ed8b637f05..b2773f5580 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewServer.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewServer.kt @@ -225,8 +225,7 @@ public class WebViewServer( * from any origin, including the package domain. */ private fun WebResourceResponse.allowCors() { - responseHeaders = responseHeaders ?: mutableMapOf() - responseHeaders["Access-Control-Allow-Origin"] = "*" + responseHeaders = responseHeaders + mapOf("Access-Control-Allow-Origin" to "*") } private fun errorResource(): Resource = From dc2da88718707c45e5b91627854ac0fc0e233f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Menu?= Date: Fri, 11 Sep 2026 18:27:53 +0200 Subject: [PATCH 27/27] Apply batched suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt | 1 - .../java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt | 1 - .../org/readium/navigator/web/internals/server/WebViewClient.kt | 1 - 3 files changed, 3 deletions(-) diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt index 4659a55957..903a9d08e8 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt @@ -21,7 +21,6 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.WindowManager -import android.webkit.RenderProcessGoneDetail import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt index 6790be00a4..ae7e6c6c59 100755 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt @@ -15,7 +15,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.webkit.RenderProcessGoneDetail import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt index 7929726e44..35841631b7 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/server/WebViewClient.kt @@ -6,7 +6,6 @@ package org.readium.navigator.web.internals.server -import android.webkit.RenderProcessGoneDetail import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView