-
-
Save alistairsykes/5034fa7133e246df04b78bedd17d3a50 to your computer and use it in GitHub Desktop.
import android.app.Application | |
import android.arch.lifecycle.LiveData | |
import android.content.Context | |
import android.net.ConnectivityManager | |
import android.net.Network | |
import android.net.NetworkInfo | |
import android.net.NetworkRequest | |
import android.os.Build | |
import android.support.annotation.RequiresPermission | |
import android.support.annotation.VisibleForTesting | |
/** | |
* A LiveData class which wraps the network connection status | |
* Requires Permission: ACCESS_NETWORK_STATE | |
* | |
* See https://developer.android.com/training/monitoring-device-state/connectivity-monitoring | |
* See https://developer.android.com/reference/android/net/ConnectivityManager | |
* See https://developer.android.com/reference/android/net/ConnectivityManager#CONNECTIVITY_ACTION | |
*/ | |
class ConnectivityLiveData @VisibleForTesting internal constructor(private val connectivityManager: ConnectivityManager) | |
: LiveData<Boolean>() { | |
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) | |
constructor(application: Application) : this(application.getSystemService(Context.CONNECTIVITY_SERVICE) | |
as ConnectivityManager) | |
private val networkCallback = object : ConnectivityManager.NetworkCallback() { | |
override fun onAvailable(network: Network?) { | |
postValue(true) | |
} | |
override fun onLost(network: Network?) { | |
postValue(false) | |
} | |
} | |
override fun onActive() { | |
super.onActive() | |
val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo | |
postValue(activeNetwork?.isConnectedOrConnecting == true) | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { | |
connectivityManager.registerDefaultNetworkCallback(networkCallback) | |
} else { | |
val builder = NetworkRequest.Builder() | |
connectivityManager.registerNetworkCallback(builder.build(), networkCallback) | |
} | |
} | |
override fun onInactive() { | |
super.onInactive() | |
connectivityManager.unregisterNetworkCallback(networkCallback) | |
} | |
} |
You can create one by using the constructor which takes an Application
. Ensure your ViewModel
is an AndroidViewModel
then you'll have access to an Application
.
Something a bit like this would work:
internal class MyViewModel(
application: Application
private val connectivity: LiveData<Boolean>
) : AndroidViewModel(application) {
constructor(application: Application) : this(
application = application,
connectivity = ConnectivityLiveData(application)
)
// ...
}
Or if you want to use lazy it should be something like:
val getConnectivity: ConnectivityLiveData by lazy {
ConnectivityLiveData(getApplication<Application>())
}
FYI, this is the accompanying blog post https://android.jlelse.eu/connectivitylivedata-6861b9591bcc
Ley me know if this doesn't work for you.
Hey, Thanks,
I modified your solution a little, by detecting network to check if there is an internet connection, using ping
class ConnectivityLiveData internal constructor(private val connectivityManager: ConnectivityManager) :
LiveData<Boolean>() {
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
constructor(application: Application) : this(
application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
)
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network?) {
postValue(isInternetOn())
}
override fun onLost(network: Network?) {
postValue(false)
}
}
override fun onActive() {
super.onActive()
postValue(isInternetOn())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.registerDefaultNetworkCallback(networkCallback)
} else {
val builder = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
}
}
override fun onInactive() {
super.onInactive()
connectivityManager.unregisterNetworkCallback(networkCallback)
}
fun isNetworkIsAvailable(): Boolean {
val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
return activeNetwork?.isConnected == true
}
fun isInternetOn(): Boolean {
if (isNetworkIsAvailable()) {
try {
val p = java.lang.Runtime.getRuntime().exec("ping -c 1 www.google.com")
val value = p.waitFor()
return value == 0
} catch (e: Exception) {
e.printStackTrace()
}
}
return false
}
}
Interesting approach. I have never seen something like this java.lang.Runtime.getRuntime().exec("ping -c 1 www.google.com")
before. Is this thread safe?
Yes, at the moment what is found works in all versions of Android, the alternative is to use .isRecheable but it does not offer the same reliability
I see in. https://stackoverflow.com/questions/9922543/why-does-inetaddress-isreachable-return-false-when-i-can-ping-the-ip-address
Very interesting. Thank you for sharing.
How to use this in viewmodel