From f9b57800b1e457c625e99ed9ba0d58fff2d95ef0 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sun, 17 Dec 2023 21:57:55 +0700 Subject: [PATCH] DownloadJob: Network check changes (#10242) Mostly pulled from WorkManager --- .../tachiyomi/data/download/DownloadJob.kt | 53 +++++++++------ .../util/system/NetworkStateTracker.kt | 67 +++++++++++++++++++ 2 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkStateTracker.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt index 93826afe0..6a7b4469e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt @@ -13,13 +13,17 @@ import androidx.work.WorkManager import androidx.work.WorkerParameters import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.Notifications -import eu.kanade.tachiyomi.util.system.isConnectedToWifi -import eu.kanade.tachiyomi.util.system.isOnline +import eu.kanade.tachiyomi.util.system.NetworkState +import eu.kanade.tachiyomi.util.system.activeNetworkState +import eu.kanade.tachiyomi.util.system.networkStateFlow import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.setForegroundSafely -import kotlinx.coroutines.delay +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combineTransform +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import tachiyomi.domain.download.service.DownloadPreferences import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -50,7 +54,11 @@ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineW } override suspend fun doWork(): Result { - var active = checkConnectivity() && downloadManager.downloaderStart() + var networkCheck = checkNetworkState( + applicationContext.activeNetworkState(), + downloadPreferences.downloadOnlyOverWifi().get(), + ) + var active = networkCheck && downloadManager.downloaderStart() if (!active) { return Result.failure() @@ -58,29 +66,36 @@ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineW setForegroundSafely() + coroutineScope { + combineTransform( + applicationContext.networkStateFlow(), + downloadPreferences.downloadOnlyOverWifi().changes(), + transform = { a, b -> emit(checkNetworkState(a, b)) }, + ) + .onEach { networkCheck = it } + .launchIn(this) + } + // Keep the worker running when needed while (active) { - delay(100) - active = !isStopped && downloadManager.isRunning && checkConnectivity() + active = !isStopped && downloadManager.isRunning && networkCheck } return Result.success() } - private fun checkConnectivity(): Boolean { - return with(applicationContext) { - if (isOnline()) { - val noWifi = downloadPreferences.downloadOnlyOverWifi().get() && !isConnectedToWifi() - if (noWifi) { - downloadManager.downloaderStop( - applicationContext.getString(R.string.download_notifier_text_only_wifi), - ) - } - !noWifi - } else { - downloadManager.downloaderStop(applicationContext.getString(R.string.download_notifier_no_network)) - false + private fun checkNetworkState(state: NetworkState, requireWifi: Boolean): Boolean { + return if (state.isOnline) { + val noWifi = requireWifi && !state.isWifi + if (noWifi) { + downloadManager.downloaderStop( + applicationContext.getString(R.string.download_notifier_text_only_wifi), + ) } + !noWifi + } else { + downloadManager.downloaderStop(applicationContext.getString(R.string.download_notifier_no_network)) + false } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkStateTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkStateTracker.kt new file mode 100644 index 000000000..a4a686541 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkStateTracker.kt @@ -0,0 +1,67 @@ +package eu.kanade.tachiyomi.util.system + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.ConnectivityManager +import android.net.ConnectivityManager.NetworkCallback +import android.net.Network +import android.net.NetworkCapabilities +import android.os.Build +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow + +data class NetworkState( + val isConnected: Boolean, + val isValidated: Boolean, + val isWifi: Boolean, +) { + val isOnline = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + isConnected && isValidated + } else { + isConnected + } +} + +@Suppress("DEPRECATION") +fun Context.activeNetworkState(): NetworkState { + val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + return NetworkState( + isConnected = connectivityManager.activeNetworkInfo?.isConnected ?: false, + isValidated = capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false, + isWifi = wifiManager.isWifiEnabled && capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false, + ) +} + +@Suppress("DEPRECATION") +fun Context.networkStateFlow() = callbackFlow { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val networkCallback = object : NetworkCallback() { + override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { + trySend(activeNetworkState()) + } + override fun onLost(network: Network) { + trySend(activeNetworkState()) + } + } + + connectivityManager.registerDefaultNetworkCallback(networkCallback) + awaitClose { + connectivityManager.unregisterNetworkCallback(networkCallback) + } + } else { + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) { + trySend(activeNetworkState()) + } + } + } + + registerReceiver(receiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) + awaitClose { + unregisterReceiver(receiver) + } + } +}