From 958661bba8b35edba4f52a72334031663fdda5b2 Mon Sep 17 00:00:00 2001 From: Vincent TE Date: Wed, 31 Jul 2024 08:17:38 +0200 Subject: [PATCH] Create a class instead of an extension --- .../core/networking/NetworkAvailability.kt | 123 ++++++++++++++++++ .../lib/core/utils/NetworkExtensions.kt | 75 ----------- 2 files changed, 123 insertions(+), 75 deletions(-) create mode 100644 src/main/java/com/infomaniak/lib/core/networking/NetworkAvailability.kt delete mode 100644 src/main/java/com/infomaniak/lib/core/utils/NetworkExtensions.kt diff --git a/src/main/java/com/infomaniak/lib/core/networking/NetworkAvailability.kt b/src/main/java/com/infomaniak/lib/core/networking/NetworkAvailability.kt new file mode 100644 index 00000000..679adaf8 --- /dev/null +++ b/src/main/java/com/infomaniak/lib/core/networking/NetworkAvailability.kt @@ -0,0 +1,123 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.lib.core.networking + +import android.content.Context +import android.net.ConnectivityManager +import android.net.ConnectivityManager.NetworkCallback +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.os.Build +import io.sentry.Sentry +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.ProducerScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch + +class NetworkAvailability(private val context: Context) { + + val isNetworkAvailable: Flow = callbackFlow { + val networks = mutableSetOf() + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + val callback = object : NetworkCallback() { + + override fun onAvailable(network: Network) { + networks.add(network) + sendNetworkAvailability(networks) + } + + override fun onLost(network: Network) { + networks.remove(network) + sendNetworkAvailability(networks) + } + } + + launch(Dispatchers.IO) { + send(getInitialNetworkAvailability(connectivityManager)) + } + + registerNetworkCallback(connectivityManager, callback) + + awaitClose { unregisterNetworkCallback(connectivityManager, callback) } + } + + private suspend fun ProducerScope.registerNetworkCallback( + connectivityManager: ConnectivityManager, + callback: NetworkCallback, + ) { + runCatching { + connectivityManager.registerNetworkCallback(networkRequestBuilder(), callback) + }.onFailure { exception -> + // Fix potential Exception thrown by ConnectivityManager on Android 11 + // Already fixed in Android S and above + // https://issuetracker.google.com/issues/175055271 + Sentry.captureException(exception) + send(false) + } + } + + private fun unregisterNetworkCallback(connectivityManager: ConnectivityManager, callback: NetworkCallback) { + runCatching { + connectivityManager.unregisterNetworkCallback(callback) + }.onFailure { exception -> + Sentry.captureException(exception) + } + } + + @Suppress("DEPRECATION") + private fun getInitialNetworkAvailability(connectivityManager: ConnectivityManager): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + connectivityManager.activeNetwork?.let(::hasInternetConnectivity) ?: false + } else { + connectivityManager.activeNetworkInfo?.isConnected ?: false + } + } + + private fun ProducerScope.sendNetworkAvailability(networks: MutableSet) { + launch(Dispatchers.IO) { + send(hasAvailableNetwork(networks)) + } + } + + private fun networkRequestBuilder(): NetworkRequest { + return NetworkRequest.Builder().apply { + addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH) + addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) addTransportType(NetworkCapabilities.TRANSPORT_USB) + addTransportType(NetworkCapabilities.TRANSPORT_VPN) + addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) + }.build() + } + + private fun hasInternetConnectivity(network: Network) = runCatching { + network.getByName(ROOT_SERVER_URL) != null + }.getOrDefault(false) + + private fun hasAvailableNetwork(networks: MutableSet) = networks.any(::hasInternetConnectivity) + + companion object { + private const val ROOT_SERVER_URL = "a.root-servers.net" + } +} diff --git a/src/main/java/com/infomaniak/lib/core/utils/NetworkExtensions.kt b/src/main/java/com/infomaniak/lib/core/utils/NetworkExtensions.kt deleted file mode 100644 index 14200ef9..00000000 --- a/src/main/java/com/infomaniak/lib/core/utils/NetworkExtensions.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Infomaniak Core - Android - * Copyright (C) 2024 Infomaniak Network SA - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.infomaniak.lib.core.utils - -import android.content.Context -import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import android.os.Build -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.launch - -fun Context.networkStatusFlow(): Flow { - val networks = mutableSetOf() - val rootServerUrl = "a.root-servers.net" - - fun networkRequestBuilder(): NetworkRequest { - return NetworkRequest.Builder().apply { - addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH) - addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) addTransportType(NetworkCapabilities.TRANSPORT_USB) - addTransportType(NetworkCapabilities.TRANSPORT_VPN) - addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) - }.build() - } - - fun hasInternetConnectivity(network: Network): Boolean = runCatching { - network.getByName(rootServerUrl) != null - }.getOrDefault(false) - - fun hasAvailableNetwork() = networks.any(::hasInternetConnectivity) - - return callbackFlow { - val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - - val callback = object : ConnectivityManager.NetworkCallback() { - override fun onAvailable(network: Network) { - networks.add(network) - launch { send(hasAvailableNetwork()) } - } - - override fun onLost(network: Network) { - networks.remove(network) - launch { send(hasAvailableNetwork()) } - } - } - - connectivityManager.registerNetworkCallback(networkRequestBuilder(), callback) - - awaitClose { - connectivityManager.unregisterNetworkCallback(callback) - } - } -}