Respect network data saver #1390

This commit is contained in:
Koitharu
2025-06-07 10:14:51 +03:00
parent dc1df527b2
commit db89bdfdff
3 changed files with 47 additions and 15 deletions

View File

@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.os
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
@@ -42,6 +43,17 @@ class NetworkState(
connectivityManager.unregisterNetworkCallback(callback)
}
fun isMetered(): Boolean {
return connectivityManager.isActiveNetworkMetered
}
fun isDataSaverEnabled(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& connectivityManager.restrictBackgroundStatus == RESTRICT_BACKGROUND_STATUS_ENABLED
fun isRestricted() = isMetered() && isDataSaverEnabled()
fun isOfflineOrRestricted() = !isOnline() || isRestricted()
suspend fun awaitForConnection() {
if (value) {
return

View File

@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
import org.koitharu.kotatsu.core.db.entity.toEntities
import org.koitharu.kotatsu.core.db.entity.toEntity
import org.koitharu.kotatsu.core.db.entity.toManga
import org.koitharu.kotatsu.core.db.entity.toMangaChapters
import org.koitharu.kotatsu.core.db.entity.toMangaTags
import org.koitharu.kotatsu.core.model.LocalMangaSource
import org.koitharu.kotatsu.core.model.isLocal
@@ -119,10 +120,10 @@ class MangaDataRepository @Inject constructor(
return db.getMangaDao().findByPublicUrl(publicUrl)?.toManga()
}
suspend fun resolveIntent(intent: MangaIntent): Manga? = when {
intent.manga != null -> intent.manga
intent.mangaId != 0L -> findMangaById(intent.mangaId, true)
intent.uri != null -> resolverProvider.get().resolve(intent.uri)
suspend fun resolveIntent(intent: MangaIntent, withChapters: Boolean): Manga? = when {
intent.manga != null -> intent.manga.withCachedChaptersIfNeeded(withChapters)
intent.mangaId != 0L -> findMangaById(intent.mangaId, withChapters)
intent.uri != null -> resolverProvider.get().resolve(intent.uri).withCachedChaptersIfNeeded(withChapters)
else -> null
}
@@ -184,6 +185,17 @@ class MangaDataRepository @Inject constructor(
emitInitialState = emitInitialState,
)
private suspend fun Manga.withCachedChaptersIfNeeded(flag: Boolean): Manga = if (flag && chapters.isNullOrEmpty()) {
val cachedChapters = db.getChaptersDao().findAll(id)
if (cachedChapters.isEmpty()) {
this
} else {
copy(chapters = cachedChapters.toMangaChapters())
}
} else {
this
}
private fun MangaPrefsEntity.getColorFilterOrNull(): ReaderColorFilter? {
return if (cfBrightness != 0f || cfContrast != 0f || cfInvert || cfGrayscale) {
ReaderColorFilter(cfBrightness, cfContrast, cfInvert, cfGrayscale)

View File

@@ -16,6 +16,7 @@ import kotlinx.coroutines.runInterruptible
import okio.IOException
import org.koitharu.kotatsu.core.model.isLocal
import org.koitharu.kotatsu.core.nav.MangaIntent
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.parser.CachingMangaRepository
import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.parser.MangaRepository
@@ -40,17 +41,12 @@ class DetailsLoadUseCase @Inject constructor(
private val recoverUseCase: RecoverMangaUseCase,
private val imageGetter: Html.ImageGetter,
private val newChaptersUseCaseProvider: Provider<CheckNewChaptersUseCase>,
private val networkState: NetworkState,
) {
operator fun invoke(intent: MangaIntent, force: Boolean): Flow<MangaDetails> = channelFlow {
val manga = requireNotNull(mangaDataRepository.resolveIntent(intent)) {
val manga = requireNotNull(mangaDataRepository.resolveIntent(intent, withChapters = true)) {
"Cannot resolve intent $intent"
}.let { m ->
if (m.chapters.isNullOrEmpty()) {
getCachedDetails(m.id) ?: m
} else {
m
}
}
val override = mangaDataRepository.getOverride(manga.id)
send(
@@ -69,6 +65,22 @@ class DetailsLoadUseCase @Inject constructor(
} else {
null
}
if (!force && networkState.isOfflineOrRestricted()) {
// try to avoid loading if has saved manga
val localManga = local?.await()
if (manga.isLocal || localManga != null) {
send(
MangaDetails(
manga = manga,
localManga = localManga,
override = override,
description = manga.description?.parseAsHtml(withImages = true)?.trim(),
isLoaded = true,
),
)
return@channelFlow
}
}
try {
val details = getDetails(manga, force)
launch { mangaDataRepository.updateChapters(details) }
@@ -147,8 +159,4 @@ class DetailsLoadUseCase @Inject constructor(
}.onFailure { e ->
e.printStackTraceDebug()
}
private suspend fun getCachedDetails(mangaId: Long): Manga? = runCatchingCancellable {
mangaDataRepository.findMangaById(mangaId, withChapters = true)
}.getOrNull()
}