|
|
|
|
@ -1,10 +1,8 @@
|
|
|
|
|
package org.koitharu.kotatsu.reader.domain
|
|
|
|
|
|
|
|
|
|
import android.content.ContentResolver.MimeTypeInfo
|
|
|
|
|
import android.content.Context
|
|
|
|
|
import android.graphics.Rect
|
|
|
|
|
import android.net.Uri
|
|
|
|
|
import android.webkit.MimeTypeMap
|
|
|
|
|
import androidx.annotation.AnyThread
|
|
|
|
|
import androidx.collection.LongSparseArray
|
|
|
|
|
import androidx.collection.set
|
|
|
|
|
@ -29,6 +27,7 @@ import kotlinx.coroutines.sync.withPermit
|
|
|
|
|
import okhttp3.OkHttpClient
|
|
|
|
|
import okhttp3.Request
|
|
|
|
|
import okio.use
|
|
|
|
|
import org.koitharu.kotatsu.core.image.BitmapDecoderCompat
|
|
|
|
|
import org.koitharu.kotatsu.core.network.CommonHeaders
|
|
|
|
|
import org.koitharu.kotatsu.core.network.MangaHttpClient
|
|
|
|
|
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
|
|
|
|
|
@ -46,11 +45,13 @@ import org.koitharu.kotatsu.core.util.ext.exists
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.getCompletionResultOrNull
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.isPowerSaveMode
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.isTargetNotEmpty
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.mimeType
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.ramAvailable
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.use
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.withProgress
|
|
|
|
|
import org.koitharu.kotatsu.core.util.progress.ProgressDeferred
|
|
|
|
|
import org.koitharu.kotatsu.download.ui.worker.DownloadSlowdownDispatcher
|
|
|
|
|
import org.koitharu.kotatsu.local.data.PagesCache
|
|
|
|
|
import org.koitharu.kotatsu.local.data.isFileUri
|
|
|
|
|
import org.koitharu.kotatsu.local.data.isZipUri
|
|
|
|
|
@ -59,8 +60,6 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.mimeType
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.requireBody
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
|
|
|
|
import org.koitharu.kotatsu.core.image.BitmapDecoderCompat
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.mimeType
|
|
|
|
|
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
|
|
|
|
import java.util.LinkedList
|
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger
|
|
|
|
|
@ -79,6 +78,7 @@ class PageLoader @Inject constructor(
|
|
|
|
|
private val settings: AppSettings,
|
|
|
|
|
private val mangaRepositoryFactory: MangaRepository.Factory,
|
|
|
|
|
private val imageProxyInterceptor: ImageProxyInterceptor,
|
|
|
|
|
private val downloadSlowdownDispatcher: DownloadSlowdownDispatcher,
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
val loaderScope = RetainedLifecycleCoroutineScope(lifecycle) + InternalErrorHandler() + Dispatchers.Default
|
|
|
|
|
@ -127,7 +127,7 @@ class PageLoader @Inject constructor(
|
|
|
|
|
} else if (task?.isCancelled == false) {
|
|
|
|
|
return task
|
|
|
|
|
}
|
|
|
|
|
task = loadPageAsyncImpl(page, force)
|
|
|
|
|
task = loadPageAsyncImpl(page, skipCache = force, isPrefetch = false)
|
|
|
|
|
synchronized(tasks) {
|
|
|
|
|
tasks[page.id] = task
|
|
|
|
|
}
|
|
|
|
|
@ -186,7 +186,7 @@ class PageLoader @Inject constructor(
|
|
|
|
|
val page = prefetchQueue.pollFirst() ?: return@launch
|
|
|
|
|
if (cache.get(page.url) == null) {
|
|
|
|
|
synchronized(tasks) {
|
|
|
|
|
tasks[page.id] = loadPageAsyncImpl(page, false)
|
|
|
|
|
tasks[page.id] = loadPageAsyncImpl(page, skipCache = false, isPrefetch = true)
|
|
|
|
|
}
|
|
|
|
|
return@launch
|
|
|
|
|
}
|
|
|
|
|
@ -194,7 +194,11 @@ class PageLoader @Inject constructor(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun loadPageAsyncImpl(page: MangaPage, skipCache: Boolean): ProgressDeferred<Uri, Float> {
|
|
|
|
|
private fun loadPageAsyncImpl(
|
|
|
|
|
page: MangaPage,
|
|
|
|
|
skipCache: Boolean,
|
|
|
|
|
isPrefetch: Boolean,
|
|
|
|
|
): ProgressDeferred<Uri, Float> {
|
|
|
|
|
val progress = MutableStateFlow(PROGRESS_UNDEFINED)
|
|
|
|
|
val deferred = loaderScope.async {
|
|
|
|
|
if (!skipCache) {
|
|
|
|
|
@ -202,7 +206,7 @@ class PageLoader @Inject constructor(
|
|
|
|
|
}
|
|
|
|
|
counter.incrementAndGet()
|
|
|
|
|
try {
|
|
|
|
|
loadPageImpl(page, progress)
|
|
|
|
|
loadPageImpl(page, progress, isPrefetch)
|
|
|
|
|
} finally {
|
|
|
|
|
if (counter.decrementAndGet() == 0) {
|
|
|
|
|
onIdle()
|
|
|
|
|
@ -222,7 +226,11 @@ class PageLoader @Inject constructor(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private suspend fun loadPageImpl(page: MangaPage, progress: MutableStateFlow<Float>): Uri = semaphore.withPermit {
|
|
|
|
|
private suspend fun loadPageImpl(
|
|
|
|
|
page: MangaPage,
|
|
|
|
|
progress: MutableStateFlow<Float>,
|
|
|
|
|
isPrefetch: Boolean,
|
|
|
|
|
): Uri = semaphore.withPermit {
|
|
|
|
|
val pageUrl = getPageUrl(page)
|
|
|
|
|
check(pageUrl.isNotBlank()) { "Cannot obtain full image url for $page" }
|
|
|
|
|
val uri = Uri.parse(pageUrl)
|
|
|
|
|
@ -235,6 +243,9 @@ class PageLoader @Inject constructor(
|
|
|
|
|
|
|
|
|
|
uri.isFileUri() -> uri
|
|
|
|
|
else -> {
|
|
|
|
|
if (isPrefetch) {
|
|
|
|
|
downloadSlowdownDispatcher.delay(page.source)
|
|
|
|
|
}
|
|
|
|
|
val request = createPageRequest(pageUrl, page.source)
|
|
|
|
|
imageProxyInterceptor.interceptPageRequest(request, okHttp).ensureSuccess().use { response ->
|
|
|
|
|
response.requireBody().withProgress(progress).use {
|
|
|
|
|
|