Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gsrathoreniks/26ad123653b3e3115a00d4feedaabdbe to your computer and use it in GitHub Desktop.
Save gsrathoreniks/26ad123653b3e3115a00d4feedaabdbe to your computer and use it in GitHub Desktop.
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.map
class MainActivity : AppCompatActivity() {
private val viewModel: NetworkStatusViewModel by lazy {
ViewModelProvider(
this,
object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val networkStatusTracker = NetworkStatusTracker(this@MainActivity)
return NetworkStatusViewModel(networkStatusTracker) as T
}
},
).get(NetworkStatusViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.state.observe(this) { state ->
findViewById<TextView>(R.id.textView).text = when (state) {
MyState.Fetched -> "Fetched"
MyState.Error -> "Error"
}
}
}
}
sealed class MyState {
object Fetched : MyState()
object Error : MyState()
}
class NetworkStatusViewModel(
networkStatusTracker: NetworkStatusTracker,
) : ViewModel() {
val state =
networkStatusTracker.networkStatus
.map(
onAvailable = { MyState.Fetched },
onUnavailable = { MyState.Error },
)
.asLiveData(Dispatchers.IO)
}
sealed class NetworkStatus {
object Available : NetworkStatus()
object Unavailable : NetworkStatus()
}
class NetworkStatusTracker(context: Context) {
private val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val validNetworks: MutableSet<Network> = HashSet()
val networkStatus = callbackFlow<NetworkStatus> {
val networkStatusCallback = object : ConnectivityManager.NetworkCallback() {
override fun onUnavailable() {
validNetworks.remove(network)
if(validNetworks.size==0)
trySend(NetworkStatus.Unavailable).isSuccess
}
override fun onAvailable(network: Network) {
validNetworks.add(network)
trySend(NetworkStatus.Available).isSuccess
}
override fun onLost(network: Network) {
validNetworks.remove(network)
if(validNetworks.size==0)
trySend(NetworkStatus.Unavailable).isSuccess
}
}
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(request, networkStatusCallback)
awaitClose {
connectivityManager.unregisterNetworkCallback(networkStatusCallback)
}
}
.distinctUntilChanged()
}
@FlowPreview
inline fun <Result> Flow<NetworkStatus>.map(
crossinline onUnavailable: suspend () -> Result,
crossinline onAvailable: suspend () -> Result,
): Flow<Result> = map { status ->
when (status) {
NetworkStatus.Unavailable -> onUnavailable()
NetworkStatus.Available -> onAvailable()
}
}
@FlowPreview
inline fun <Result> Flow<NetworkStatus>.flatMap(
crossinline onUnavailable: suspend () -> Flow<Result>,
crossinline onAvailable: suspend () -> Flow<Result>,
): Flow<Result> = flatMapConcat { status ->
when (status) {
NetworkStatus.Unavailable -> onUnavailable()
NetworkStatus.Available -> onAvailable()
}
}
@amsavarthan
Copy link

I think there is no need of removing valid network in onUnavailable method and also the network parameter is not declared anywhere for line 79

override fun onUnavailable() { trySend(NetworkStatus.UNAVAILABLE).isSuccess }

@gsrathoreniks
Copy link
Author

gsrathoreniks commented Aug 31, 2022

@amsavarthan

We've multiple source for network, if we don't remove valid network in onUnavailable then if only mobile data is active and wifi became unavailable, then it will trigger that there's no network available.
The above code takes care of that, it only emit the un-available state when there's no network in either mobile or wifi.

@amsavarthan
Copy link

amsavarthan commented Aug 31, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment