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..d20a5860
--- /dev/null
+++ b/src/main/java/com/infomaniak/lib/core/networking/NetworkAvailability.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.CoroutineDispatcher
+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, private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO) {
+
+ private val connectivityManager by lazy { context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
+
+ val isNetworkAvailable: Flow = callbackFlow {
+ val networks = mutableListOf()
+
+ 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(ioDispatcher) {
+ 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: MutableList) {
+ launch(ioDispatcher) {
+ send(hasAvailableNetwork(networks))
+ }
+ }
+
+ private fun networkRequestBuilder(): NetworkRequest {
+ return NetworkRequest.Builder().apply {
+ addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ }.build()
+ }
+
+ private fun hasInternetConnectivity(network: Network) = runCatching {
+ network.getByName(ROOT_SERVER_URL) != null
+ }.getOrDefault(false)
+
+ private fun hasAvailableNetwork(networks: MutableList) = 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)
- }
- }
-}