Update components scope

pull/311/head
Koitharu 3 years ago
parent b4e0704a3a
commit efc4bbacb5
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -30,10 +30,12 @@ import java.io.File
import java.io.InputStream import java.io.InputStream
import java.util.zip.ZipFile import java.util.zip.ZipFile
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.roundToInt import kotlin.math.roundToInt
private const val MIN_WEBTOON_RATIO = 2 private const val MIN_WEBTOON_RATIO = 2
@Singleton
class MangaDataRepository @Inject constructor( class MangaDataRepository @Inject constructor(
private val okHttpClient: OkHttpClient, private val okHttpClient: OkHttpClient,
private val db: MangaDatabase, 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.parsers.model.Manga
import org.koitharu.kotatsu.utils.ext.mapItems import org.koitharu.kotatsu.utils.ext.mapItems
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
import javax.inject.Singleton
@Singleton
class BookmarksRepository @Inject constructor( class BookmarksRepository @Inject constructor(
private val db: MangaDatabase, private val db: MangaDatabase,
) { ) {

@ -7,7 +7,6 @@ import android.text.style.ForegroundColorSpan
import androidx.core.text.getSpans import androidx.core.text.getSpans
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.asFlow import androidx.lifecycle.asFlow
import androidx.lifecycle.asLiveData import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -27,12 +26,9 @@ import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R 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.base.ui.BaseViewModel
import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository 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.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.prefs.observeAsFlow
import org.koitharu.kotatsu.details.domain.BranchComparator import org.koitharu.kotatsu.details.domain.BranchComparator
@ -58,27 +54,17 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class DetailsViewModel @Inject constructor( class DetailsViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val historyRepository: HistoryRepository, private val historyRepository: HistoryRepository,
favouritesRepository: FavouritesRepository, favouritesRepository: FavouritesRepository,
private val localMangaRepository: LocalMangaRepository, private val localMangaRepository: LocalMangaRepository,
trackingRepository: TrackingRepository, trackingRepository: TrackingRepository,
mangaDataRepository: MangaDataRepository,
private val bookmarksRepository: BookmarksRepository, private val bookmarksRepository: BookmarksRepository,
private val settings: AppSettings, private val settings: AppSettings,
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>, private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
private val imageGetter: Html.ImageGetter, private val imageGetter: Html.ImageGetter,
mangaRepositoryFactory: MangaRepository.Factory, private val delegate: MangaDetailsDelegate,
) : BaseViewModel() { ) : BaseViewModel() {
private val delegate = MangaDetailsDelegate(
intent = MangaIntent(savedStateHandle),
mangaDataRepository = mangaDataRepository,
historyRepository = historyRepository,
localMangaRepository = localMangaRepository,
mangaRepositoryFactory = mangaRepositoryFactory,
)
private var loadingJob: Job private var loadingJob: Job
val onShowToast = SingleLiveEvent<Int>() val onShowToast = SingleLiveEvent<Int>()

@ -1,5 +1,7 @@
package org.koitharu.kotatsu.details.ui 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.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.base.domain.MangaDataRepository 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.parsers.model.MangaSource
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
import javax.inject.Inject
class MangaDetailsDelegate( @ViewModelScoped
private val intent: MangaIntent, class MangaDetailsDelegate @Inject constructor(
savedStateHandle: SavedStateHandle,
private val mangaDataRepository: MangaDataRepository, private val mangaDataRepository: MangaDataRepository,
private val historyRepository: HistoryRepository, private val historyRepository: HistoryRepository,
private val localMangaRepository: LocalMangaRepository, private val localMangaRepository: LocalMangaRepository,
private val mangaRepositoryFactory: MangaRepository.Factory, private val mangaRepositoryFactory: MangaRepository.Factory,
) { ) {
private val intent = MangaIntent(savedStateHandle)
private val mangaData = MutableStateFlow(intent.manga) private val mangaData = MutableStateFlow(intent.manga)
val selectedBranch = MutableStateFlow<String?>(null) val selectedBranch = MutableStateFlow<String?>(null)

@ -1,17 +1,18 @@
package org.koitharu.kotatsu.download.domain package org.koitharu.kotatsu.download.domain
import android.app.Service
import android.content.Context import android.content.Context
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import coil.ImageLoader import coil.ImageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.Scale import coil.size.Scale
import dagger.assisted.Assisted import dagger.hilt.android.lifecycle.RetainedLifecycle
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ServiceScoped
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.delay 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.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.await 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.copyToSuspending
import org.koitharu.kotatsu.utils.ext.deleteAwait import org.koitharu.kotatsu.utils.ext.deleteAwait
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
import org.koitharu.kotatsu.utils.progress.PausingProgressJob import org.koitharu.kotatsu.utils.progress.PausingProgressJob
import java.io.File import java.io.File
import javax.inject.Inject
private const val MAX_FAILSAFE_ATTEMPTS = 2 private const val MAX_FAILSAFE_ATTEMPTS = 2
private const val DOWNLOAD_ERROR_DELAY = 500L private const val DOWNLOAD_ERROR_DELAY = 500L
private const val SLOWDOWN_DELAY = 200L private const val SLOWDOWN_DELAY = 200L
class DownloadManager @AssistedInject constructor( @ServiceScoped
@Assisted private val coroutineScope: CoroutineScope, class DownloadManager @Inject constructor(
service: Service,
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val imageLoader: ImageLoader, private val imageLoader: ImageLoader,
private val okHttp: OkHttpClient, private val okHttp: OkHttpClient,
@ -64,6 +68,7 @@ class DownloadManager @AssistedInject constructor(
androidx.core.R.dimen.compat_notification_large_icon_max_height, androidx.core.R.dimen.compat_notification_large_icon_max_height,
) )
private val semaphore = Semaphore(settings.downloadsParallelism) private val semaphore = Semaphore(settings.downloadsParallelism)
private val coroutineScope = (service as LifecycleService).lifecycleScope
fun downloadManga( fun downloadManga(
manga: Manga, manga: Manga,
@ -262,10 +267,4 @@ class DownloadManager @AssistedInject constructor(
} finally { } finally {
localMangaRepository.unlockManga(manga.id) localMangaRepository.unlockManga(manga.id)
} }
@AssistedFactory
interface Factory {
fun create(coroutineScope: CoroutineScope): DownloadManager
}
} }

@ -44,12 +44,11 @@ import kotlin.collections.set
@AndroidEntryPoint @AndroidEntryPoint
class DownloadService : BaseService() { class DownloadService : BaseService() {
private lateinit var downloadManager: DownloadManager
private lateinit var downloadNotification: DownloadNotification private lateinit var downloadNotification: DownloadNotification
private lateinit var wakeLock: PowerManager.WakeLock private lateinit var wakeLock: PowerManager.WakeLock
@Inject @Inject
lateinit var downloadManagerFactory: DownloadManager.Factory lateinit var downloadManager: DownloadManager
private val jobs = LinkedHashMap<Int, PausingProgressJob<DownloadState>>() private val jobs = LinkedHashMap<Int, PausingProgressJob<DownloadState>>()
private val jobCount = MutableStateFlow(0) private val jobCount = MutableStateFlow(0)
@ -61,7 +60,6 @@ class DownloadService : BaseService() {
downloadNotification = DownloadNotification(this) downloadNotification = DownloadNotification(this)
wakeLock = (applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager) wakeLock = (applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager)
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading") .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading")
downloadManager = downloadManagerFactory.create(lifecycleScope)
wakeLock.acquire(TimeUnit.HOURS.toMillis(8)) wakeLock.acquire(TimeUnit.HOURS.toMillis(8))
DownloadNotification.createChannel(this) DownloadNotification.createChannel(this)
startForeground(DownloadNotification.ID_GROUP, downloadNotification.buildGroupNotification()) 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.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.utils.ext.mapItems import org.koitharu.kotatsu.utils.ext.mapItems
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
const val PROGRESS_NONE = -1f const val PROGRESS_NONE = -1f
@Singleton
class HistoryRepository @Inject constructor( class HistoryRepository @Inject constructor(
private val db: MangaDatabase, private val db: MangaDatabase,
private val trackingRepository: TrackingRepository, private val trackingRepository: TrackingRepository,
@ -37,7 +39,7 @@ class HistoryRepository @Inject constructor(
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>, 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) val entities = db.historyDao.findAll(offset, limit)
return entities.map { it.manga.toManga(it.tags.toMangaTags()) } 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 * 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?) { suspend fun deleteOrSwap(manga: Manga, alternative: Manga?) {
if (alternative == null || db.mangaDao.update(alternative.toEntity()) <= 0) { if (alternative == null || db.mangaDao.update(alternative.toEntity()) <= 0) {

@ -1,16 +1,19 @@
package org.koitharu.kotatsu.reader.domain package org.koitharu.kotatsu.reader.domain
import android.util.LongSparseArray import android.util.LongSparseArray
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
import javax.inject.Inject
private const val PAGES_TRIM_THRESHOLD = 120 private const val PAGES_TRIM_THRESHOLD = 120
class ChaptersLoader( @ViewModelScoped
class ChaptersLoader @Inject constructor(
private val mangaRepositoryFactory: MangaRepository.Factory, private val mangaRepositoryFactory: MangaRepository.Factory,
) { ) {

@ -9,13 +9,11 @@ import dagger.hilt.android.ActivityRetainedLifecycle
import dagger.hilt.android.lifecycle.RetainedLifecycle import dagger.hilt.android.lifecycle.RetainedLifecycle
import dagger.hilt.android.scopes.ActivityRetainedScoped import dagger.hilt.android.scopes.ActivityRetainedScoped
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock 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.model.MangaSource
import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage 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.printStackTraceDebug
import org.koitharu.kotatsu.utils.ext.withProgress import org.koitharu.kotatsu.utils.ext.withProgress
import org.koitharu.kotatsu.utils.progress.ProgressDeferred import org.koitharu.kotatsu.utils.progress.ProgressDeferred
@ -57,7 +56,7 @@ class PageLoader @Inject constructor(
lifecycle.addOnClearedListener(this) 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 tasks = LongSparseArray<ProgressDeferred<File, Float>>()
private val convertLock = Mutex() private val convertLock = Mutex()
@ -68,7 +67,6 @@ class PageLoader @Inject constructor(
private var prefetchQueueLimit = PREFETCH_LIMIT_DEFAULT // TODO adaptive private var prefetchQueueLimit = PREFETCH_LIMIT_DEFAULT // TODO adaptive
override fun onCleared() { override fun onCleared() {
loaderScope.cancel()
synchronized(tasks) { synchronized(tasks) {
tasks.clear() tasks.clear()
} }

@ -70,6 +70,7 @@ class ReaderViewModel @Inject constructor(
private val settings: AppSettings, private val settings: AppSettings,
private val pageSaveHelper: PageSaveHelper, private val pageSaveHelper: PageSaveHelper,
private val pageLoader: PageLoader, private val pageLoader: PageLoader,
private val chaptersLoader: ChaptersLoader,
) : BaseViewModel() { ) : BaseViewModel() {
private val intent = MangaIntent(savedStateHandle) private val intent = MangaIntent(savedStateHandle)
@ -83,8 +84,6 @@ class ReaderViewModel @Inject constructor(
private val chapters: LongSparseArray<MangaChapter> private val chapters: LongSparseArray<MangaChapter>
get() = chaptersLoader.chapters get() = chaptersLoader.chapters
private val chaptersLoader = ChaptersLoader(mangaRepositoryFactory)
val readerMode = MutableLiveData<ReaderMode>() val readerMode = MutableLiveData<ReaderMode>()
val onPageSaved = SingleLiveEvent<Uri?>() val onPageSaved = SingleLiveEvent<Uri?>()
val onShowToast = SingleLiveEvent<Int>() 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.parsers.util.levenshteinDistance
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
import javax.inject.Singleton
@Singleton
class MangaSearchRepository @Inject constructor( class MangaSearchRepository @Inject constructor(
private val settings: AppSettings, private val settings: AppSettings,
private val db: MangaDatabase, 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.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ShelfRepository @Inject constructor( class ShelfRepository @Inject constructor(
private val localMangaRepository: LocalMangaRepository, private val localMangaRepository: LocalMangaRepository,
private val historyRepository: HistoryRepository, 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 org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
import java.util.Date import java.util.Date
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
private const val NO_ID = 0L private const val NO_ID = 0L
@Singleton
class TrackingRepository @Inject constructor( class TrackingRepository @Inject constructor(
private val db: MangaDatabase, 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)
}
}

@ -51,10 +51,10 @@ fun Fragment.addMenuProvider(provider: MenuProvider) {
suspend fun Fragment.awaitViewLifecycle(): LifecycleOwner = suspendCancellableCoroutine { cont -> suspend fun Fragment.awaitViewLifecycle(): LifecycleOwner = suspendCancellableCoroutine { cont ->
val liveData = viewLifecycleOwnerLiveData val liveData = viewLifecycleOwnerLiveData
val observer = object : Observer<LifecycleOwner?> { val observer = object : Observer<LifecycleOwner?> {
override fun onChanged(result: LifecycleOwner?) { override fun onChanged(value: LifecycleOwner?) {
if (result != null) { if (value != null) {
liveData.removeObserver(this) liveData.removeObserver(this)
cont.resume(result) cont.resume(value)
} }
} }
} }

Loading…
Cancel
Save