Prefetch chapter
parent
656a707b4c
commit
37b76de142
@ -0,0 +1,22 @@
|
||||
package org.koitharu.kotatsu.core.cache
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
|
||||
interface ContentCache {
|
||||
|
||||
suspend fun getDetails(source: MangaSource, url: String): Manga?
|
||||
|
||||
fun putDetails(source: MangaSource, url: String, details: Deferred<Manga>)
|
||||
|
||||
suspend fun getPages(source: MangaSource, url: String): List<MangaPage>?
|
||||
|
||||
fun putPages(source: MangaSource, url: String, pages: Deferred<List<MangaPage>>)
|
||||
|
||||
data class Key(
|
||||
val source: MangaSource,
|
||||
val url: String,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package org.koitharu.kotatsu.core.cache
|
||||
|
||||
import androidx.collection.LruCache
|
||||
import kotlinx.coroutines.Deferred
|
||||
|
||||
class DeferredLruCache<T>(maxSize: Int) : LruCache<ContentCache.Key, Deferred<T>>(maxSize) {
|
||||
|
||||
override fun entryRemoved(
|
||||
evicted: Boolean,
|
||||
key: ContentCache.Key,
|
||||
oldValue: Deferred<T>,
|
||||
newValue: Deferred<T>?,
|
||||
) {
|
||||
super.entryRemoved(evicted, key, oldValue, newValue)
|
||||
oldValue.cancel()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package org.koitharu.kotatsu.core.cache
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
|
||||
@Suppress("DeferredResultUnused")
|
||||
class MemoryContentCache : ContentCache {
|
||||
|
||||
private val detailsCache = DeferredLruCache<Manga>(10)
|
||||
private val pagesCache = DeferredLruCache<List<MangaPage>>(10)
|
||||
|
||||
override suspend fun getDetails(source: MangaSource, url: String): Manga? {
|
||||
return detailsCache[ContentCache.Key(source, url)]?.await()
|
||||
}
|
||||
|
||||
override fun putDetails(source: MangaSource, url: String, details: Deferred<Manga>) {
|
||||
detailsCache.put(ContentCache.Key(source, url), details)
|
||||
}
|
||||
|
||||
override suspend fun getPages(source: MangaSource, url: String): List<MangaPage>? {
|
||||
return pagesCache[ContentCache.Key(source, url)]?.await()
|
||||
}
|
||||
|
||||
override fun putPages(source: MangaSource, url: String, pages: Deferred<List<MangaPage>>) {
|
||||
pagesCache.put(ContentCache.Key(source, url), pages)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package org.koitharu.kotatsu.core.cache
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
|
||||
class StubContentCache : ContentCache {
|
||||
|
||||
override suspend fun getDetails(source: MangaSource, url: String): Manga? = null
|
||||
|
||||
override fun putDetails(source: MangaSource, url: String, details: Deferred<Manga>) = Unit
|
||||
|
||||
override suspend fun getPages(source: MangaSource, url: String): List<MangaPage>? = null
|
||||
|
||||
override fun putPages(source: MangaSource, url: String, pages: Deferred<List<MangaPage>>) = Unit
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package org.koitharu.kotatsu.details.service
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.base.ui.CoroutineIntentService
|
||||
import org.koitharu.kotatsu.core.cache.ContentCache
|
||||
import org.koitharu.kotatsu.core.cache.StubContentCache
|
||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaChapters
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat
|
||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MangaPrefetchService : CoroutineIntentService() {
|
||||
|
||||
@Inject
|
||||
lateinit var mangaRepositoryFactory: MangaRepository.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var cache: ContentCache
|
||||
|
||||
override suspend fun processIntent(startId: Int, intent: Intent) {
|
||||
when (intent.action) {
|
||||
ACTION_PREFETCH_DETAILS -> prefetchDetails(
|
||||
manga = intent.getParcelableExtraCompat<ParcelableManga>(EXTRA_MANGA)?.manga ?: return,
|
||||
)
|
||||
|
||||
ACTION_PREFETCH_PAGES -> prefetchPages(
|
||||
chapter = intent.getParcelableExtraCompat<ParcelableMangaChapters>(EXTRA_CHAPTER)
|
||||
?.chapters?.singleOrNull() ?: return,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(startId: Int, error: Throwable) = Unit
|
||||
|
||||
private suspend fun prefetchDetails(manga: Manga) = coroutineScope {
|
||||
val source = mangaRepositoryFactory.create(manga.source)
|
||||
processLifecycleScope.launch(Dispatchers.Default) {
|
||||
runCatchingCancellable { source.getDetails(manga) }
|
||||
}.join()
|
||||
}
|
||||
|
||||
private suspend fun prefetchPages(chapter: MangaChapter) {
|
||||
val source = mangaRepositoryFactory.create(chapter.source)
|
||||
processLifecycleScope.launch(Dispatchers.Default) {
|
||||
runCatchingCancellable { source.getPages(chapter) }
|
||||
}.join()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_MANGA = "manga"
|
||||
private const val EXTRA_CHAPTER = "manga"
|
||||
private const val ACTION_PREFETCH_DETAILS = "details"
|
||||
private const val ACTION_PREFETCH_PAGES = "pages"
|
||||
|
||||
fun prefetchDetails(context: Context, manga: Manga) {
|
||||
if (!isPrefetchAvailable(context, manga.source)) return
|
||||
val intent = Intent(context, MangaPrefetchService::class.java)
|
||||
intent.action = ACTION_PREFETCH_DETAILS
|
||||
intent.putExtra(EXTRA_MANGA, ParcelableManga(manga, withChapters = false))
|
||||
context.startService(intent)
|
||||
}
|
||||
|
||||
fun prefetchPages(context: Context, chapter: MangaChapter) {
|
||||
if (!isPrefetchAvailable(context, chapter.source)) return
|
||||
val intent = Intent(context, MangaPrefetchService::class.java)
|
||||
intent.action = ACTION_PREFETCH_PAGES
|
||||
intent.putExtra(EXTRA_CHAPTER, ParcelableMangaChapters(listOf(chapter)))
|
||||
context.startService(intent)
|
||||
}
|
||||
|
||||
private fun isPrefetchAvailable(context: Context, source: MangaSource): Boolean {
|
||||
if (source == MangaSource.LOCAL) {
|
||||
return false
|
||||
}
|
||||
val entryPoint = EntryPointAccessors.fromApplication(context, PrefetchCompanionEntryPoint::class.java)
|
||||
if (entryPoint.contentCache is StubContentCache) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package org.koitharu.kotatsu.details.service
|
||||
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import org.koitharu.kotatsu.core.cache.ContentCache
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface PrefetchCompanionEntryPoint {
|
||||
val settings: AppSettings
|
||||
val contentCache: ContentCache
|
||||
}
|
||||
Loading…
Reference in New Issue