I've decided to check on the solution here, and made some improvements and sample. I also made it avoid multiple setting of the same value so it won't cause the observer to keep getting the same value, one after another :
ConnectionLiveData.kt
class ConnectionLiveData(private val context: Context) : LiveData<Boolean>() {
private val handler = Handler(Looper.getMainLooper())
private lateinit var connectivityManagerCallback: ConnectivityManager.NetworkCallback
private lateinit var networkReceiver: BroadcastReceiver
init {
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
connectivityManagerCallback = object : ConnectivityManager.NetworkCallback() {
@AnyThread
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
setNewValue(true)
}
}
override fun onAvailable(network: Network) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1)
setNewValue(true)
}
override fun onLost(network: Network) {
setNewValue(false)
}
}
}
else -> {
networkReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
updateConnection()
}
}
}
}
}
/**this prevents observers to get multiple times the same value*/
@AnyThread
private fun setNewValue(isConnected: Boolean) {
handler.removeCallbacksAndMessages(null)
if (isUiThread()) {
if (value != isConnected)
@SuppressLint("WrongThread")
value = isConnected
return
}
handler.post {
if (value != isConnected)
value = isConnected
}
}
@UiThread
override fun onActive() {
super.onActive()
val connectivityManager: ConnectivityManager = context.getSystemService()!!
updateConnection()
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> connectivityManager.registerDefaultNetworkCallback(connectivityManagerCallback, handler)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> connectivityManager.registerDefaultNetworkCallback(connectivityManagerCallback)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
val networkRequest =
NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build()
connectivityManager.registerNetworkCallback(networkRequest, connectivityManagerCallback)
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
val networkRequest =
NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build()
connectivityManager.registerNetworkCallback(networkRequest, connectivityManagerCallback)
}
else -> {
@Suppress("DEPRECATION") context.registerReceiver(networkReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}
}
}
override fun onInactive() {
super.onInactive()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val connectivityManager: ConnectivityManager = context.getSystemService()!!
connectivityManager.unregisterNetworkCallback(connectivityManagerCallback)
} else {
context.unregisterReceiver(networkReceiver)
}
}
@Suppress("DEPRECATION")
private fun updateConnection() {
setNewValue(isConnectedToInternet(context))
}
companion object {
@JvmStatic
fun isUiThread(): Boolean =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Looper.getMainLooper().isCurrentThread else Thread.currentThread() === Looper.getMainLooper().thread
private fun isConnectedToInternet(context: Context): Boolean {
val connectivityManager: ConnectivityManager = context.getSystemService()!!
val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
return activeNetwork?.isConnectedOrConnecting == true
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.textView)
ConnectionLiveData(this).observe(this) {
textView.text = if (it) "connected" else "disconnected"
Log.d("AppLog", "connected?$it")
}
val internetSettings = findViewById<View>(R.id.internetSettings)
internetSettings.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
internetSettings.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
startActivity(Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY))
}
findViewById<View>(R.id.wifiSettings).setOnClickListener {
startActivity(Intent(Settings.ACTION_WIFI_SETTINGS))
}
findViewById<View>(R.id.mobileDataSettings).setOnClickListener {
startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
}
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"
android:orientation="vertical" tools:context=".MainActivity">
<TextView
android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" />
<Button
android:id="@+id/internetSettings" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="internet settings" />
<Button
android:id="@+id/wifiSettings" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="wifi settings" />
<Button
android:id="@+id/mobileDataSettings" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="mobile-data settings" />
</LinearLayout>
manifest requires:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Note that it has a rare issue when airplane-mode gets turned on, that temporarily it thinks there is no connection, and then the opposite, but then back again that there is no connection. Reported about this here.