FairEmail/app/src/main/java/com/bugsnag/android/ConnectivityCompat.kt

156 lines
5.2 KiB
Kotlin

package com.bugsnag.android
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.net.NetworkCapabilities
import android.os.Build
import androidx.annotation.RequiresApi
internal typealias NetworkChangeCallback = (hasConnection: Boolean, networkState: String) -> Unit
internal interface Connectivity {
fun registerForNetworkChanges()
fun unregisterForNetworkChanges()
fun hasNetworkConnection(): Boolean
fun retrieveNetworkAccessState(): String
}
internal class ConnectivityCompat(
context: Context,
callback: NetworkChangeCallback?
) : Connectivity {
private val cm = context.getConnectivityManager()
private val connectivity: Connectivity =
when {
cm == null -> UnknownConnectivity
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> ConnectivityApi24(cm, callback)
else -> ConnectivityLegacy(context, cm, callback)
}
override fun registerForNetworkChanges() {
runCatching { connectivity.registerForNetworkChanges() }
}
override fun hasNetworkConnection(): Boolean {
val result = runCatching { connectivity.hasNetworkConnection() }
return result.getOrElse { true } // allow network requests to be made if state unknown
}
override fun unregisterForNetworkChanges() {
runCatching { connectivity.unregisterForNetworkChanges() }
}
override fun retrieveNetworkAccessState(): String {
val result = runCatching { connectivity.retrieveNetworkAccessState() }
return result.getOrElse { "unknown" }
}
}
@Suppress("DEPRECATION")
internal class ConnectivityLegacy(
private val context: Context,
private val cm: ConnectivityManager,
callback: NetworkChangeCallback?
) : Connectivity {
private val changeReceiver = ConnectivityChangeReceiver(callback)
private val activeNetworkInfo: android.net.NetworkInfo?
get() = try {
cm.activeNetworkInfo
} catch (e: NullPointerException) {
// in some rare cases we get a remote NullPointerException via Parcel.readException
null
}
override fun registerForNetworkChanges() {
val intentFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
context.registerReceiverSafe(changeReceiver, intentFilter)
}
override fun unregisterForNetworkChanges() = context.unregisterReceiverSafe(changeReceiver)
override fun hasNetworkConnection(): Boolean {
return activeNetworkInfo?.isConnectedOrConnecting ?: false
}
override fun retrieveNetworkAccessState(): String {
return when (activeNetworkInfo?.type) {
null -> "none"
ConnectivityManager.TYPE_WIFI -> "wifi"
ConnectivityManager.TYPE_ETHERNET -> "ethernet"
else -> "cellular" // all other types are cellular in some form
}
}
private inner class ConnectivityChangeReceiver(private val cb: NetworkChangeCallback?) :
BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
cb?.invoke(hasNetworkConnection(), retrieveNetworkAccessState())
}
}
}
@RequiresApi(Build.VERSION_CODES.N)
internal class ConnectivityApi24(
private val cm: ConnectivityManager,
callback: NetworkChangeCallback?
) : Connectivity {
private val networkCallback = ConnectivityTrackerCallback(callback)
override fun registerForNetworkChanges() = cm.registerDefaultNetworkCallback(networkCallback)
override fun unregisterForNetworkChanges() = cm.unregisterNetworkCallback(networkCallback)
override fun hasNetworkConnection() = cm.activeNetwork != null
override fun retrieveNetworkAccessState(): String {
val network = cm.activeNetwork
val capabilities = if (network != null) cm.getNetworkCapabilities(network) else null
return when {
capabilities == null -> "none"
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "wifi"
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "ethernet"
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "cellular"
else -> "unknown"
}
}
private inner class ConnectivityTrackerCallback(private val cb: NetworkChangeCallback?) :
ConnectivityManager.NetworkCallback() {
override fun onUnavailable() {
super.onUnavailable()
cb?.invoke(false, retrieveNetworkAccessState())
}
override fun onAvailable(network: Network) {
super.onAvailable(network)
cb?.invoke(true, retrieveNetworkAccessState())
}
}
}
/**
* Connectivity used in cases where we cannot access the system ConnectivityManager.
* We assume that there is some sort of network and do not attempt to report any network changes.
*/
internal object UnknownConnectivity : Connectivity {
override fun registerForNetworkChanges() {}
override fun unregisterForNetworkChanges() {}
override fun hasNetworkConnection(): Boolean {
return true
}
override fun retrieveNetworkAccessState(): String {
return "unknown"
}
}