diff --git a/readium/navigators/web/fixedlayout/src/main/kotlin/org/readium/navigator/web/fixedlayout/spread/SpreadWebView.kt b/readium/navigators/web/fixedlayout/src/main/kotlin/org/readium/navigator/web/fixedlayout/spread/SpreadWebView.kt index a8f86f2841..06db12a04a 100644 --- a/readium/navigators/web/fixedlayout/src/main/kotlin/org/readium/navigator/web/fixedlayout/spread/SpreadWebView.kt +++ b/readium/navigators/web/fixedlayout/src/main/kotlin/org/readium/navigator/web/fixedlayout/spread/SpreadWebView.kt @@ -37,6 +37,7 @@ import org.readium.navigator.web.internals.webview.RelaxedWebView import org.readium.navigator.web.internals.webview.WebView import org.readium.navigator.web.internals.webview.WebViewScrollController import org.readium.navigator.web.internals.webview.WebViewState +import org.readium.navigator.web.internals.webview.invokeOnWebViewUpToDate import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.util.AbsoluteUrl @@ -96,8 +97,7 @@ internal fun SpreadWebView( documentStateApi?.let { documentStateApi -> documentStateApi.listener = DelegatingDocumentApiListener( onDocumentLoadedAndSizedDelegate = { - webView.requestLayout() - webView.setNextLayoutListener { + webView.invokeOnWebViewUpToDate { val scrollController = WebViewScrollController(webView) scrollController.moveToProgression( progression = progression, diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/pager/RenditionScrollState.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/pager/RenditionScrollState.kt index d53e8d54e7..f5b89c5c35 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/pager/RenditionScrollState.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/pager/RenditionScrollState.kt @@ -183,7 +183,7 @@ public class RenditionScrollState( return } - val scrollController = pageStates[index].scrollController.value!! + val scrollController = pageStates[index].scrollController.value ?: return val scrolled = scrollController.scrollToMax(orientation) if (scrolled > 0) { dispatchRawDelta(scrolled.toFloat()) diff --git a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/RelaxedWebView.kt b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/RelaxedWebView.kt index b561c4144a..4d9789e611 100644 --- a/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/RelaxedWebView.kt +++ b/readium/navigators/web/internals/src/main/kotlin/org/readium/navigator/web/internals/webview/RelaxedWebView.kt @@ -66,8 +66,9 @@ public class RelaxedWebView(context: Context) : WebView(context) { @Deprecated("Deprecated in Java") override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { super.onLayout(changed, l, t, r, b) - nextLayoutListener.invoke() + val listener = nextLayoutListener nextLayoutListener = {} + listener.invoke() } private var hasActionMode: Boolean = false @@ -140,8 +141,12 @@ private class CallbackDecorator( * has received data up-to-date at the moment when the call occurs or newer. */ public fun RelaxedWebView.invokeOnWebViewUpToDate(block: WebView.() -> Unit) { - requestLayout() - setNextLayoutListener { + if (verticalScrollExtent > 0 && horizontalScrollExtent > 0) { invokeOnReadyToBeDrawn(block) + } else { + requestLayout() + setNextLayoutListener { + invokeOnWebViewUpToDate(block) + } } } diff --git a/readium/shared/src/main/java/org/readium/r2/shared/util/zip/FileZipContainer.kt b/readium/shared/src/main/java/org/readium/r2/shared/util/zip/FileZipContainer.kt index 18ad265bf9..0828b9831a 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/util/zip/FileZipContainer.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/util/zip/FileZipContainer.kt @@ -17,6 +17,8 @@ import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.readium.r2.shared.InternalReadiumApi import org.readium.r2.shared.extensions.readFully @@ -42,6 +44,8 @@ internal class FileZipContainer( file: File, ) : Container { + private val mutex = Mutex() + private inner class Entry(private val url: Url, private val entry: ZipEntry) : Resource { @@ -76,20 +80,30 @@ internal class FileZipContainer( } override suspend fun read(range: LongRange?): Try = - try { - withContext(Dispatchers.IO) { - val bytes = - if (range == null) { - readFully() - } else { - readRange(range) - } - Try.success(bytes) + mutex.withLock { + try { + withContext(Dispatchers.IO) { + val bytes = + if (range == null) { + readFully() + } else { + readRange(range) + } + Try.success(bytes) + } + } catch (e: ZipException) { + Try.failure(ReadError.Decoding(e)) + } catch (e: IOException) { + Try.failure(ReadError.Access(FileSystemError.IO(e))) + } catch (e: IllegalStateException) { + Try.failure( + ReadError.Access( + FileSystemError.IO( + IOException("Zip file closed.", e) + ) + ) + ) } - } catch (e: ZipException) { - Try.failure(ReadError.Decoding(e)) - } catch (e: IOException) { - Try.failure(ReadError.Access(FileSystemError.IO(e))) } private suspend fun readFully(): ByteArray = @@ -156,9 +170,11 @@ internal class FileZipContainer( @OptIn(DelicateCoroutinesApi::class) override fun close() { GlobalScope.launch { - tryOrLog { - withContext(Dispatchers.IO) { - archive.close() + mutex.withLock { + tryOrLog { + withContext(Dispatchers.IO) { + archive.close() + } } } }