Update components scope
This commit is contained in:
@@ -30,10 +30,12 @@ import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipFile
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val MIN_WEBTOON_RATIO = 2
|
||||
|
||||
@Singleton
|
||||
class MangaDataRepository @Inject constructor(
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val db: MangaDatabase,
|
||||
|
||||
@@ -17,7 +17,9 @@ import org.koitharu.kotatsu.core.db.entity.toManga
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.utils.ext.mapItems
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BookmarksRepository @Inject constructor(
|
||||
private val db: MangaDatabase,
|
||||
) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.text.style.ForegroundColorSpan
|
||||
import androidx.core.text.getSpans
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@@ -27,12 +26,9 @@ import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.MangaDataRepository
|
||||
import org.koitharu.kotatsu.base.domain.MangaIntent
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
||||
import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.details.domain.BranchComparator
|
||||
@@ -58,27 +54,17 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DetailsViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val historyRepository: HistoryRepository,
|
||||
favouritesRepository: FavouritesRepository,
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
trackingRepository: TrackingRepository,
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
private val bookmarksRepository: BookmarksRepository,
|
||||
private val settings: AppSettings,
|
||||
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
private val imageGetter: Html.ImageGetter,
|
||||
mangaRepositoryFactory: MangaRepository.Factory,
|
||||
private val delegate: MangaDetailsDelegate,
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val delegate = MangaDetailsDelegate(
|
||||
intent = MangaIntent(savedStateHandle),
|
||||
mangaDataRepository = mangaDataRepository,
|
||||
historyRepository = historyRepository,
|
||||
localMangaRepository = localMangaRepository,
|
||||
mangaRepositoryFactory = mangaRepositoryFactory,
|
||||
)
|
||||
|
||||
private var loadingJob: Job
|
||||
|
||||
val onShowToast = SingleLiveEvent<Int>()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.koitharu.kotatsu.details.ui
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.koitharu.kotatsu.base.domain.MangaDataRepository
|
||||
@@ -17,15 +19,17 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import javax.inject.Inject
|
||||
|
||||
class MangaDetailsDelegate(
|
||||
private val intent: MangaIntent,
|
||||
@ViewModelScoped
|
||||
class MangaDetailsDelegate @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val mangaDataRepository: MangaDataRepository,
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
) {
|
||||
|
||||
private val intent = MangaIntent(savedStateHandle)
|
||||
private val mangaData = MutableStateFlow(intent.manga)
|
||||
|
||||
val selectedBranch = MutableStateFlow<String?>(null)
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package org.koitharu.kotatsu.download.domain
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import coil.ImageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Scale
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.lifecycle.RetainedLifecycle
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.scopes.ServiceScoped
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -35,19 +36,22 @@ import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.await
|
||||
import org.koitharu.kotatsu.utils.RetainedLifecycleCoroutineScope
|
||||
import org.koitharu.kotatsu.utils.ext.copyToSuspending
|
||||
import org.koitharu.kotatsu.utils.ext.deleteAwait
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.utils.progress.PausingProgressJob
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val MAX_FAILSAFE_ATTEMPTS = 2
|
||||
private const val DOWNLOAD_ERROR_DELAY = 500L
|
||||
private const val SLOWDOWN_DELAY = 200L
|
||||
|
||||
class DownloadManager @AssistedInject constructor(
|
||||
@Assisted private val coroutineScope: CoroutineScope,
|
||||
@ServiceScoped
|
||||
class DownloadManager @Inject constructor(
|
||||
service: Service,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val imageLoader: ImageLoader,
|
||||
private val okHttp: OkHttpClient,
|
||||
@@ -64,6 +68,7 @@ class DownloadManager @AssistedInject constructor(
|
||||
androidx.core.R.dimen.compat_notification_large_icon_max_height,
|
||||
)
|
||||
private val semaphore = Semaphore(settings.downloadsParallelism)
|
||||
private val coroutineScope = (service as LifecycleService).lifecycleScope
|
||||
|
||||
fun downloadManga(
|
||||
manga: Manga,
|
||||
@@ -262,10 +267,4 @@ class DownloadManager @AssistedInject constructor(
|
||||
} finally {
|
||||
localMangaRepository.unlockManga(manga.id)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
|
||||
fun create(coroutineScope: CoroutineScope): DownloadManager
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,11 @@ import kotlin.collections.set
|
||||
@AndroidEntryPoint
|
||||
class DownloadService : BaseService() {
|
||||
|
||||
private lateinit var downloadManager: DownloadManager
|
||||
private lateinit var downloadNotification: DownloadNotification
|
||||
private lateinit var wakeLock: PowerManager.WakeLock
|
||||
|
||||
@Inject
|
||||
lateinit var downloadManagerFactory: DownloadManager.Factory
|
||||
lateinit var downloadManager: DownloadManager
|
||||
|
||||
private val jobs = LinkedHashMap<Int, PausingProgressJob<DownloadState>>()
|
||||
private val jobCount = MutableStateFlow(0)
|
||||
@@ -61,7 +60,6 @@ class DownloadService : BaseService() {
|
||||
downloadNotification = DownloadNotification(this)
|
||||
wakeLock = (applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager)
|
||||
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading")
|
||||
downloadManager = downloadManagerFactory.create(lifecycleScope)
|
||||
wakeLock.acquire(TimeUnit.HOURS.toMillis(8))
|
||||
DownloadNotification.createChannel(this)
|
||||
startForeground(DownloadNotification.ID_GROUP, downloadNotification.buildGroupNotification())
|
||||
|
||||
@@ -27,9 +27,11 @@ import org.koitharu.kotatsu.scrobbling.common.domain.tryScrobble
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.ext.mapItems
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
const val PROGRESS_NONE = -1f
|
||||
|
||||
@Singleton
|
||||
class HistoryRepository @Inject constructor(
|
||||
private val db: MangaDatabase,
|
||||
private val trackingRepository: TrackingRepository,
|
||||
@@ -37,7 +39,7 @@ class HistoryRepository @Inject constructor(
|
||||
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
) {
|
||||
|
||||
suspend fun getList(offset: Int, limit: Int = 20): List<Manga> {
|
||||
suspend fun getList(offset: Int, limit: Int): List<Manga> {
|
||||
val entities = db.historyDao.findAll(offset, limit)
|
||||
return entities.map { it.manga.toManga(it.tags.toMangaTags()) }
|
||||
}
|
||||
@@ -135,7 +137,7 @@ class HistoryRepository @Inject constructor(
|
||||
|
||||
/**
|
||||
* Try to replace one manga with another one
|
||||
* Useful for replacing saved manga on deleting it with remove source
|
||||
* Useful for replacing saved manga on deleting it with remote source
|
||||
*/
|
||||
suspend fun deleteOrSwap(manga: Manga, alternative: Manga?) {
|
||||
if (alternative == null || db.mangaDao.update(alternative.toEntity()) <= 0) {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package org.koitharu.kotatsu.reader.domain
|
||||
|
||||
import android.util.LongSparseArray
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
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.reader.ui.pager.ReaderPage
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val PAGES_TRIM_THRESHOLD = 120
|
||||
|
||||
class ChaptersLoader(
|
||||
@ViewModelScoped
|
||||
class ChaptersLoader @Inject constructor(
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
) {
|
||||
|
||||
|
||||
@@ -9,13 +9,11 @@ import dagger.hilt.android.ActivityRetainedLifecycle
|
||||
import dagger.hilt.android.lifecycle.RetainedLifecycle
|
||||
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -30,6 +28,7 @@ import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.await
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.RetainedLifecycleCoroutineScope
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.ext.withProgress
|
||||
import org.koitharu.kotatsu.utils.progress.ProgressDeferred
|
||||
@@ -57,7 +56,7 @@ class PageLoader @Inject constructor(
|
||||
lifecycle.addOnClearedListener(this)
|
||||
}
|
||||
|
||||
val loaderScope = CoroutineScope(SupervisorJob() + InternalErrorHandler() + Dispatchers.Default)
|
||||
val loaderScope = RetainedLifecycleCoroutineScope(lifecycle) + InternalErrorHandler() + Dispatchers.Default
|
||||
|
||||
private val tasks = LongSparseArray<ProgressDeferred<File, Float>>()
|
||||
private val convertLock = Mutex()
|
||||
@@ -68,7 +67,6 @@ class PageLoader @Inject constructor(
|
||||
private var prefetchQueueLimit = PREFETCH_LIMIT_DEFAULT // TODO adaptive
|
||||
|
||||
override fun onCleared() {
|
||||
loaderScope.cancel()
|
||||
synchronized(tasks) {
|
||||
tasks.clear()
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ class ReaderViewModel @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
private val pageSaveHelper: PageSaveHelper,
|
||||
private val pageLoader: PageLoader,
|
||||
private val chaptersLoader: ChaptersLoader,
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val intent = MangaIntent(savedStateHandle)
|
||||
@@ -83,8 +84,6 @@ class ReaderViewModel @Inject constructor(
|
||||
private val chapters: LongSparseArray<MangaChapter>
|
||||
get() = chaptersLoader.chapters
|
||||
|
||||
private val chaptersLoader = ChaptersLoader(mangaRepositoryFactory)
|
||||
|
||||
val readerMode = MutableLiveData<ReaderMode>()
|
||||
val onPageSaved = SingleLiveEvent<Uri?>()
|
||||
val onShowToast = SingleLiveEvent<Int>()
|
||||
|
||||
@@ -22,7 +22,9 @@ import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.util.levenshteinDistance
|
||||
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class MangaSearchRepository @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
private val db: MangaDatabase,
|
||||
|
||||
@@ -24,7 +24,9 @@ import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ShelfRepository @Inject constructor(
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val historyRepository: HistoryRepository,
|
||||
|
||||
@@ -23,9 +23,11 @@ import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates
|
||||
import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
private const val NO_ID = 0L
|
||||
|
||||
@Singleton
|
||||
class TrackingRepository @Inject constructor(
|
||||
private val db: MangaDatabase,
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import dagger.hilt.android.lifecycle.RetainedLifecycle
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class RetainedLifecycleCoroutineScope(
|
||||
private val lifecycle: RetainedLifecycle,
|
||||
) : CoroutineScope, RetainedLifecycle.OnClearedListener {
|
||||
|
||||
override val coroutineContext: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
|
||||
|
||||
init {
|
||||
lifecycle.addOnClearedListener(this)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
coroutineContext.cancel()
|
||||
lifecycle.removeOnClearedListener(this)
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,4 @@ import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
||||
val processLifecycleScope: LifecycleCoroutineScope
|
||||
inline get() = ProcessLifecycleOwner.get().lifecycleScope
|
||||
inline get() = ProcessLifecycleOwner.get().lifecycleScope
|
||||
|
||||
@@ -51,10 +51,10 @@ fun Fragment.addMenuProvider(provider: MenuProvider) {
|
||||
suspend fun Fragment.awaitViewLifecycle(): LifecycleOwner = suspendCancellableCoroutine { cont ->
|
||||
val liveData = viewLifecycleOwnerLiveData
|
||||
val observer = object : Observer<LifecycleOwner?> {
|
||||
override fun onChanged(result: LifecycleOwner?) {
|
||||
if (result != null) {
|
||||
override fun onChanged(value: LifecycleOwner?) {
|
||||
if (value != null) {
|
||||
liveData.removeObserver(this)
|
||||
cont.resume(result)
|
||||
cont.resume(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user