Respect network data saver #1390
This commit is contained in:
@@ -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()
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user