Skip to content

Instantly share code, notes, and snippets.

@cdongieux
Created February 12, 2020 09:01
Show Gist options
  • Save cdongieux/ac90ca5280b1d115d8a55df06edcc500 to your computer and use it in GitHub Desktop.
Save cdongieux/ac90ca5280b1d115d8a55df06edcc500 to your computer and use it in GitHub Desktop.
package com.netgem.diamondcore.base.manager
import android.annotation.TargetApi
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.net.NetworkRequest
import android.os.Build
import androidx.core.net.ConnectivityManagerCompat
import androidx.lifecycle.*
import timber.log.Timber
object ConnectivityManager2 : LifecycleObserver {
private const val DEBUG = false
private val connectivityStatus_ = MutableLiveData<ConnectivityStatus>()
private var appContext: Context? = null
private var platformConnectivityManager: ConnectivityManager? = null
private val callback = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
if (DEBUG) Timber.d("onAvailable() called with: network = [%s]", network)
super.onAvailable(network)
updateFromNetwork(available = network)
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
if (DEBUG) Timber.d("onCapabilitiesChanged() called with: network = [%s], networkCapabilities = [%s]", network, networkCapabilities)
super.onCapabilitiesChanged(network, networkCapabilities)
updateFromNetwork(modified = network)
}
override fun onLost(network: Network) {
if (DEBUG) Timber.d("onLost() called with: network = [%s]", network)
super.onLost(network)
updateFromNetwork(lost = network)
}
}
} else {
null
}
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (DEBUG) Timber.d("onReceive() called with: intent = [%s]", intent)
updateFromActiveNetworkInfo()
}
}
val connectivityStatus: LiveData<ConnectivityStatus> = Transformations.distinctUntilChanged(connectivityStatus_)
fun init(context: Context) {
appContext = context.applicationContext
platformConnectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
if (DEBUG) Timber.d("start() called")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
platformConnectivityManager?.registerNetworkCallback(
NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(),
callback!!
)
} else {
@Suppress("DEPRECATION")
appContext?.registerReceiver(receiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
if (DEBUG) Timber.d("stop() called")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
platformConnectivityManager?.unregisterNetworkCallback(callback!!)
} else {
appContext?.unregisterReceiver(receiver)
}
}
private fun updateFromNetwork(available: Network? = null, modified: Network? = null, lost: Network? = null) {
if (DEBUG) Timber.d("updateFromNetwork() called with: available = [%s], modified = [%s], lost = [%s]", available, modified, lost)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
updateFromActiveNetwork(available, modified, lost)
} else {
updateFromActiveNetworkInfo()
}
}
@TargetApi(Build.VERSION_CODES.M)
private fun updateFromActiveNetwork(available: Network? = null, modified: Network? = null, lost: Network? = null) {
if (DEBUG) Timber.d("updateFromActiveNetwork() called with: available = [%s], modified = [%s], lost = [%s]", available, modified, lost)
val manager = platformConnectivityManager ?: return
val network: Network? = available ?: modified ?: if (lost != null) {
val otherNetworks = manager.allNetworks.filter { it != lost }
// other networks might not have the Internet capability, so find the first one having it
otherNetworks.firstOrNull {
manager.getNetworkCapabilities(it)?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
}
} else {
manager.activeNetwork
}
if (DEBUG) Timber.d("updateFromActiveNetwork: network = [%s] with capabilities = [%s]", network, manager.getNetworkCapabilities(network))
val connectivityStatus = if (network == null) {
ConnectivityStatus.Offline
} else {
val isConnected = manager.getNetworkCapabilities(network)?.let { networkCapabilities ->
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ||
(networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) &&
manager.allNetworks.any { manager.getNetworkCapabilities(it)?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true })
} == true
if (isConnected) {
ConnectivityStatus.Online(
getConnectivityType(network),
isMetered(network)
)
} else {
ConnectivityStatus.Offline
}
}
updateStatus(connectivityStatus)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun getConnectivityType(network: Network): ConnectivityType {
val manager = platformConnectivityManager ?: return ConnectivityType.NONE
val networkCapabilities = manager.getNetworkCapabilities(network)
return when {
networkCapabilities == null -> ConnectivityType.NONE
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> ConnectivityType.WIFI
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> ConnectivityType.MOBILE
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> ConnectivityType.ETHERNET
else -> ConnectivityType.OTHER
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun isMetered(network: Network): Boolean {
val manager = platformConnectivityManager ?: return false
val networkCapabilities = manager.getNetworkCapabilities(network)
return when {
networkCapabilities == null -> false
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) -> false
else -> true
}
}
private fun updateFromActiveNetworkInfo() {
if (DEBUG) Timber.d("updateFromActiveNetworkInfo() called")
val manager = platformConnectivityManager ?: return
@Suppress("DEPRECATION")
val networkInfo = manager.activeNetworkInfo
val connectivityStatus = if (networkInfo == null) {
ConnectivityStatus.Offline
} else {
@Suppress("DEPRECATION")
if (networkInfo.isConnected) {
ConnectivityStatus.Online(
when (networkInfo.type) {
ConnectivityManager.TYPE_WIFI -> ConnectivityType.WIFI
ConnectivityManager.TYPE_MOBILE -> ConnectivityType.MOBILE
ConnectivityManager.TYPE_ETHERNET -> ConnectivityType.ETHERNET
else -> ConnectivityType.OTHER
},
ConnectivityManagerCompat.isActiveNetworkMetered(manager)
)
} else {
ConnectivityStatus.Offline
}
}
updateStatus(connectivityStatus)
}
private fun updateStatus(connectivityStatus: ConnectivityStatus) {
if (DEBUG) Timber.d("updateStatus() called with: connectivityStatus = [%s]", connectivityStatus)
connectivityStatus_.postValue(connectivityStatus)
}
sealed class ConnectivityStatus {
data class Online(
val connectivityType: ConnectivityType = ConnectivityType.NONE,
val isMetered: Boolean = false
) : ConnectivityStatus()
object Offline : ConnectivityStatus()
val isOnline: Boolean get() = this is Online
val isOffline: Boolean get() = this is Offline
}
enum class ConnectivityType {
WIFI,
MOBILE,
ETHERNET,
OTHER,
NONE
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment