Network monitor : try to make it more reliable...
This commit is contained in:
parent
3176cad7e2
commit
1185dbd276
1 changed files with 47 additions and 38 deletions
|
|
@ -14,6 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(FlowPreview::class)
|
||||||
|
|
||||||
package io.element.android.features.networkmonitor.impl
|
package io.element.android.features.networkmonitor.impl
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
@ -27,62 +29,69 @@ import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
import io.element.android.libraries.di.ApplicationContext
|
||||||
import io.element.android.libraries.di.SingleIn
|
import io.element.android.libraries.di.SingleIn
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ContributesBinding(scope = AppScope::class)
|
@ContributesBinding(scope = AppScope::class)
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
class NetworkMonitorImpl @Inject constructor(
|
class NetworkMonitorImpl @Inject constructor(
|
||||||
@ApplicationContext context: Context
|
@ApplicationContext context: Context,
|
||||||
|
appCoroutineScope: CoroutineScope,
|
||||||
) : NetworkMonitor {
|
) : NetworkMonitor {
|
||||||
|
|
||||||
private val connectivityManager: ConnectivityManager = context.getSystemService(ConnectivityManager::class.java)
|
private val connectivityManager: ConnectivityManager = context.getSystemService(ConnectivityManager::class.java)
|
||||||
|
|
||||||
private val callback = object : ConnectivityManager.NetworkCallback() {
|
override val connectivity: StateFlow<NetworkStatus> = callbackFlow {
|
||||||
override fun onAvailable(network: Network) {
|
|
||||||
_connectivity.value = connectivityManager.currentConnectionStatus()
|
/**
|
||||||
Timber.v("Connectivity status (available): ${connectivityManager.currentConnectionStatus()}")
|
* Assume there is no network available as soon as onLost is called.
|
||||||
|
* Reading activeNetwork synchronously from the callback is not safe.
|
||||||
|
* If there is an other active network we'll get some callback in onCapabilitiesChanged.
|
||||||
|
* Debounce the result to avoid quick offline<->online changes.
|
||||||
|
*/
|
||||||
|
val callback = object : ConnectivityManager.NetworkCallback() {
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
trySendBlocking(NetworkStatus.Offline)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCapabilitiesChanged(
|
||||||
|
network: Network,
|
||||||
|
networkCapabilities: NetworkCapabilities
|
||||||
|
) {
|
||||||
|
trySendBlocking(networkCapabilities.getNetworkStatus())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
trySendBlocking(connectivityManager.activeNetworkStatus())
|
||||||
override fun onLost(network: Network) {
|
|
||||||
_connectivity.value = connectivityManager.currentConnectionStatus()
|
|
||||||
Timber.v("Connectivity status (lost): ${connectivityManager.currentConnectionStatus()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCapabilitiesChanged(
|
|
||||||
network: Network,
|
|
||||||
networkCapabilities: NetworkCapabilities
|
|
||||||
) {
|
|
||||||
_connectivity.value = connectivityManager.currentConnectionStatus()
|
|
||||||
Timber.v("Connectivity status (changed): ${connectivityManager.currentConnectionStatus()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _connectivity = MutableStateFlow(NetworkStatus.Online)
|
|
||||||
override val connectivity: StateFlow<NetworkStatus> = _connectivity
|
|
||||||
|
|
||||||
init {
|
|
||||||
listenToConnectionChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun listenToConnectionChanges() {
|
|
||||||
val request = NetworkRequest.Builder()
|
val request = NetworkRequest.Builder()
|
||||||
// .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||||
// .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
|
||||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
||||||
.build()
|
.build()
|
||||||
connectivityManager.registerNetworkCallback(request, callback)
|
connectivityManager.registerNetworkCallback(request, callback)
|
||||||
|
Timber.d("Subscribe")
|
||||||
|
awaitClose {
|
||||||
|
Timber.d("Unsubscribe")
|
||||||
|
connectivityManager.unregisterNetworkCallback(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.debounce(300)
|
||||||
|
.stateIn(appCoroutineScope, SharingStarted.WhileSubscribed(), connectivityManager.activeNetworkStatus())
|
||||||
|
|
||||||
_connectivity.tryEmit(connectivityManager.currentConnectionStatus())
|
private fun ConnectivityManager.activeNetworkStatus(): NetworkStatus {
|
||||||
|
return activeNetwork?.let {
|
||||||
|
getNetworkCapabilities(it)?.getNetworkStatus()
|
||||||
|
} ?: NetworkStatus.Offline
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ConnectivityManager.currentConnectionStatus(): NetworkStatus {
|
private fun NetworkCapabilities.getNetworkStatus(): NetworkStatus {
|
||||||
val hasInternet = activeNetwork?.let(::getNetworkCapabilities)
|
val hasInternet = hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||||
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
||||||
?: false
|
|
||||||
return if (hasInternet) {
|
return if (hasInternet) {
|
||||||
NetworkStatus.Online
|
NetworkStatus.Online
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue