mirror of
https://github.com/mollyim/mollyim-android.git
synced 2025-05-12 21:30:39 +01:00
Coordinate connectivity detection across multiple networks
This commit is contained in:
parent
94a2ecf429
commit
2dae3cccea
1 changed files with 73 additions and 44 deletions
|
@ -5,78 +5,107 @@
|
|||
|
||||
package org.thoughtcrime.securesms.messages
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.os.Build
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
|
||||
/**
|
||||
* Backcompat listener for determining when the network connection is lost.
|
||||
* On API 28+, [onNetworkLost] is invoked when the system notifies the app that the network is lost.
|
||||
* On earlier versions, [onNetworkLost] is invoked on any network change (gained, lost, losing, etc)
|
||||
* Therefore, [onNetworkLost] is a higher-order function, which takes a function to determine conditionally if it should run.
|
||||
* API 28+ only runs on lost networks, so it provides a conditional that's always true because that is guaranteed by the call site.
|
||||
* Earlier versions use [NetworkConstraint.isMet] to query the current network state upon receiving the broadcast.
|
||||
* Observes changes in internet connectivity and notifies when the connection is lost or regained.
|
||||
*
|
||||
* The [onNetworkLost] callback is triggered with a Boolean indicating whether the Internet connection
|
||||
* is lost or regained. The current connection state is also provided immediately upon registration.
|
||||
*/
|
||||
class NetworkConnectionListener(private val context: Context, private val onNetworkLost: (() -> Boolean) -> Unit) {
|
||||
class NetworkConnectionListener(context: Context, private val onNetworkLost: (() -> Boolean) -> Unit) {
|
||||
companion object {
|
||||
private val TAG = Log.tag(NetworkConnectionListener::class.java)
|
||||
}
|
||||
|
||||
private val connectivityManager = ServiceUtil.getConnectivityManager(context)
|
||||
|
||||
private val networkChangedCallback: ConnectivityManager.NetworkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onUnavailable() {
|
||||
super.onUnavailable()
|
||||
Log.d(TAG, "ConnectivityManager.NetworkCallback onUnavailable()")
|
||||
onNetworkLost { true }
|
||||
inner class NetworkStateCallback : ConnectivityManager.NetworkCallback() {
|
||||
|
||||
private val currentNetworks = mutableMapOf<Network, Boolean>()
|
||||
|
||||
private var lastConnectionState: Boolean? = null
|
||||
|
||||
private fun updateConnectionState(hasConnection: Boolean) {
|
||||
lastConnectionState = hasConnection
|
||||
onNetworkLost { !hasConnection }
|
||||
}
|
||||
|
||||
private fun connectionChanged() {
|
||||
synchronized(this) {
|
||||
val hasConnection = currentNetworks.any { it.value }
|
||||
if (lastConnectionState != hasConnection) {
|
||||
updateConnectionState(hasConnection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setInitialConnectionState(network: Network?, isAvailable: Boolean) {
|
||||
Log.d(TAG, "Initial state: Network $network is ${if (isAvailable) "UP" else "DOWN"}")
|
||||
|
||||
synchronized(this) {
|
||||
if (lastConnectionState == null) {
|
||||
if (network != null && isAvailable) {
|
||||
currentNetworks[network] = true
|
||||
updateConnectionState(hasConnection = true)
|
||||
} else {
|
||||
updateConnectionState(hasConnection = false)
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Initial state already set, skipping")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
|
||||
super.onBlockedStatusChanged(network, blocked)
|
||||
Log.d(TAG, "ConnectivityManager.NetworkCallback onBlockedStatusChanged()")
|
||||
onNetworkLost { blocked }
|
||||
}
|
||||
|
||||
override fun onAvailable(network: Network) {
|
||||
super.onAvailable(network)
|
||||
Log.d(TAG, "ConnectivityManager.NetworkCallback onAvailable()")
|
||||
onNetworkLost { false }
|
||||
Log.d(TAG, "Network $network is UP and ${if (blocked) "BLOCKED" else "UNBLOCKED"}")
|
||||
currentNetworks[network] = !blocked
|
||||
connectionChanged()
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
super.onLost(network)
|
||||
Log.d(TAG, "ConnectivityManager.NetworkCallback onLost()")
|
||||
onNetworkLost { true }
|
||||
Log.d(TAG, "Network $network LOST")
|
||||
currentNetworks.remove(network)
|
||||
connectionChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private val connectionReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Log.d(TAG, "BroadcastReceiver onReceive().")
|
||||
onNetworkLost { !NetworkConstraint.isMet(context) }
|
||||
}
|
||||
}
|
||||
private var networkStateCallback: NetworkStateCallback? = null
|
||||
|
||||
@Synchronized
|
||||
fun register() {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
connectivityManager.registerDefaultNetworkCallback(networkChangedCallback)
|
||||
} else {
|
||||
context.registerReceiver(connectionReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
|
||||
}
|
||||
if (networkStateCallback != null) return
|
||||
|
||||
val request =
|
||||
NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.build()
|
||||
|
||||
val callback = NetworkStateCallback()
|
||||
networkStateCallback = callback
|
||||
|
||||
connectivityManager.registerNetworkCallback(request, callback)
|
||||
|
||||
val network = connectivityManager.activeNetwork
|
||||
val hasInternet = connectivityManager
|
||||
.getNetworkCapabilities(network)
|
||||
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
?: false
|
||||
|
||||
callback.setInitialConnectionState(network, isAvailable = hasInternet)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun unregister() {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
connectivityManager.unregisterNetworkCallback(networkChangedCallback)
|
||||
} else {
|
||||
context.unregisterReceiver(connectionReceiver)
|
||||
networkStateCallback?.let { callback ->
|
||||
connectivityManager.unregisterNetworkCallback(callback)
|
||||
networkStateCallback = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue