Respect network data saver #1390

master
Koitharu 11 months ago
parent dc1df527b2
commit db89bdfdff
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.os
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback import android.net.ConnectivityManager.NetworkCallback
import android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED
import android.net.Network import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkRequest import android.net.NetworkRequest
@ -42,6 +43,17 @@ class NetworkState(
connectivityManager.unregisterNetworkCallback(callback) 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() { suspend fun awaitForConnection() {
if (value) { if (value) {
return return

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

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

Loading…
Cancel
Save