Merge branch 'devel' into next
This commit is contained in:
@@ -34,7 +34,7 @@ abstract class BaseViewModel : ViewModel() {
|
||||
|
||||
val isLoading: StateFlow<Boolean>
|
||||
get() = loadingCounter.map { it > 0 }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), loadingCounter.value > 0)
|
||||
|
||||
protected fun launchJob(
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import coil.ImageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.SuccessResult
|
||||
import coil.util.CoilUtils
|
||||
import com.google.android.material.chip.Chip
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -333,7 +334,7 @@ class DetailsFragment :
|
||||
private fun loadCover(manga: Manga) {
|
||||
val imageUrl = manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }
|
||||
val lastResult = CoilUtils.result(requireViewBinding().imageViewCover)
|
||||
if (lastResult?.request?.data == imageUrl) {
|
||||
if (lastResult is SuccessResult && lastResult.request.data == imageUrl) {
|
||||
return
|
||||
}
|
||||
val request = ImageRequest.Builder(context ?: return)
|
||||
|
||||
@@ -142,11 +142,13 @@ class DownloadsViewModel @Inject constructor(
|
||||
fun remove(ids: Set<Long>) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
val snapshot = works.value ?: return@launchJob
|
||||
val uuids = HashSet<UUID>(ids.size)
|
||||
for (work in snapshot) {
|
||||
if (work.id.mostSignificantBits in ids) {
|
||||
workScheduler.delete(work.id)
|
||||
uuids.add(work.id)
|
||||
}
|
||||
}
|
||||
workScheduler.delete(uuids)
|
||||
onActionDone.call(ReversibleAction(R.string.downloads_removed, null))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.koitharu.kotatsu.core.util.WorkManagerHelper
|
||||
import org.koitharu.kotatsu.core.util.ext.deleteAwait
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.core.util.ext.writeAllCancellable
|
||||
import org.koitharu.kotatsu.core.util.progress.TimeLeftEstimator
|
||||
import org.koitharu.kotatsu.download.domain.DownloadState
|
||||
@@ -60,7 +61,6 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.await
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -322,9 +322,9 @@ class DownloadWorker @AssistedInject constructor(
|
||||
manga: Manga,
|
||||
includedIds: LongArray?,
|
||||
): List<MangaChapter> {
|
||||
val chapters = checkNotNull(manga.chapters?.toMutableList()) {
|
||||
val chapters = checkNotNull(manga.chapters) {
|
||||
"Chapters list must not be null"
|
||||
}
|
||||
}.toMutableList()
|
||||
if (includedIds != null) {
|
||||
val chaptersIdsSet = includedIds.toMutableSet()
|
||||
chapters.retainAll { x -> chaptersIdsSet.remove(x.id) }
|
||||
@@ -399,6 +399,13 @@ class DownloadWorker @AssistedInject constructor(
|
||||
WorkManagerHelper(workManager).deleteWork(id)
|
||||
}
|
||||
|
||||
suspend fun delete(ids: Collection<UUID>) {
|
||||
val wm = workManager
|
||||
val helper = WorkManagerHelper(wm)
|
||||
ids.forEach { id -> wm.cancelWorkById(id).await() }
|
||||
helper.deleteWorks(ids)
|
||||
}
|
||||
|
||||
suspend fun removeCompleted() {
|
||||
val helper = WorkManagerHelper(workManager)
|
||||
val finishedWorks = helper.getFinishedWorkInfosByTag(TAG)
|
||||
@@ -406,10 +413,7 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
|
||||
suspend fun updateConstraints() {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiresStorageNotLow(true)
|
||||
.setRequiredNetworkType(if (settings.isDownloadsWiFiOnly) NetworkType.UNMETERED else NetworkType.CONNECTED)
|
||||
.build()
|
||||
val constraints = createConstraints()
|
||||
val helper = WorkManagerHelper(workManager)
|
||||
val works = helper.getWorkInfosByTag(TAG)
|
||||
for (work in works) {
|
||||
@@ -418,6 +422,7 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
val request = OneTimeWorkRequestBuilder<DownloadWorker>()
|
||||
.setConstraints(constraints)
|
||||
.addTag(TAG)
|
||||
.setId(work.id)
|
||||
.build()
|
||||
helper.updateWork(request)
|
||||
@@ -425,15 +430,15 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private suspend fun scheduleImpl(data: Collection<Data>) {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiresStorageNotLow(true)
|
||||
.setRequiredNetworkType(if (settings.isDownloadsWiFiOnly) NetworkType.UNMETERED else NetworkType.CONNECTED)
|
||||
.build()
|
||||
if (data.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val constraints = createConstraints()
|
||||
val requests = data.map { inputData ->
|
||||
OneTimeWorkRequestBuilder<DownloadWorker>()
|
||||
.setConstraints(constraints)
|
||||
.addTag(TAG)
|
||||
.keepResultsForAtLeast(7, TimeUnit.DAYS)
|
||||
.keepResultsForAtLeast(30, TimeUnit.DAYS)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS)
|
||||
.setInputData(inputData)
|
||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||
@@ -441,6 +446,10 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
workManager.enqueue(requests).await()
|
||||
}
|
||||
|
||||
private fun createConstraints() = Constraints.Builder()
|
||||
.setRequiredNetworkType(if (settings.isDownloadsWiFiOnly) NetworkType.UNMETERED else NetworkType.CONNECTED)
|
||||
.build()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
@@ -4,6 +4,9 @@ import androidx.annotation.WorkerThread
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.koitharu.kotatsu.core.model.getLocaleTitle
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
@@ -19,18 +22,26 @@ class NewSourcesViewModel @Inject constructor(
|
||||
|
||||
private val initialList = settings.newSources
|
||||
val sources = MutableStateFlow<List<SourceConfigItem>?>(null)
|
||||
private var listUpdateJob: Job? = null
|
||||
|
||||
init {
|
||||
launchJob(Dispatchers.Default) {
|
||||
listUpdateJob = launchJob(Dispatchers.Default) {
|
||||
sources.value = buildList()
|
||||
}
|
||||
}
|
||||
|
||||
fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean) {
|
||||
if (isEnabled) {
|
||||
settings.hiddenSources -= item.source.name
|
||||
} else {
|
||||
settings.hiddenSources += item.source.name
|
||||
val prevJob = listUpdateJob
|
||||
listUpdateJob = launchJob(Dispatchers.Default) {
|
||||
if (isEnabled) {
|
||||
settings.hiddenSources -= item.source.name
|
||||
} else {
|
||||
settings.hiddenSources += item.source.name
|
||||
}
|
||||
prevJob?.cancelAndJoin()
|
||||
val list = buildList()
|
||||
ensureActive()
|
||||
sources.value = list
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,3 +72,4 @@ class NewSourcesViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user