Network Connection

ConnectionManager.kt

class ConnectionManager(context: Context, private val externalScope: CoroutineScope) {

    private val connectivityManager = context.getSystemService(ConnectivityManager::class.java)

    val connectionAsStateFlow: StateFlow<Boolean>
        get() = _connectionFlow
            .stateIn(
                scope = externalScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = isConnected
            )

    private val _connectionFlow = callbackFlow {
        val networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onLost(network: Network) {
                trySend(false)
            }

            override fun onCapabilitiesChanged(
                network: Network,
                networkCapabilities: NetworkCapabilities
            ) {
                if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
                ) {
                    trySend(true)
                }
            }
        }
        subscribe(networkCallback)
        awaitClose {
            unsubscribe(networkCallback)
        }
    }

    private val isConnected: Boolean
        get() {
            val activeNetwork = connectivityManager.activeNetwork
            return if (activeNetwork == null) {
                false
            } else {
                val netCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
                (netCapabilities != null
                        && netCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                        && netCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
            }
        }

    private fun subscribe(networkCallback: ConnectivityManager.NetworkCallback) {
        connectivityManager.registerDefaultNetworkCallback(networkCallback)
    }

    private fun unsubscribe(networkCallback: ConnectivityManager.NetworkCallback) {
        connectivityManager.unregisterNetworkCallback(networkCallback)
    }
}

MainActivity.kt


class MainActivity : ComponentActivity() {

    private val myConnectivityManager by lazy { ConnectionManager(this, lifecycleScope) }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            val connectionState by myConnectivityManager.connectionAsStateFlow.collectAsState()

            NetworkConnectionTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    ConnectivityUiView(
                        isOnline = connectionState,
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }

HomeScreen.kt

@Composable
fun HomeScreen(isOnline: Boolean, modifier: Modifier = Modifier) {
    val transition = updateTransition(targetState = isOnline, label = "Online Status Transition")

    val bgColor by transition.animateColor(label = "Background Color") { online ->
        if (online) Color(0xFF4CAF50) else Color(0xFF9E9E9E)
    }

    val msgStr = stringResource(
        id = if (isOnline) {
            R.string.you_are_online_msg
        } else {
            R.string.you_are_offline_msg
        }
    )

    Card(
        modifier = modifier
            .fillMaxWidth()
            .padding(16.dp),
        shape = RoundedCornerShape(8.dp),
        colors = androidx.compose.material3.CardDefaults.cardColors(
            containerColor = bgColor
        ),
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.Center
        ) {
            Crossfade(targetState = isOnline, label = "") { isOnline ->
                val icon = if (isOnline) R.drawable.online else R.drawable.offline

                Icon(
                    contentDescription = null,
                    tint = Color.White,
                    modifier = Modifier.padding(end = 8.dp),
                    painter = painterResource(id = icon)
                )
            }
            Text(
                text = msgStr,
                style = TextStyle(color = Color.White, fontWeight = FontWeight.Bold),
                textAlign = TextAlign.Center
            )
        }
    }
}

Demo

Github

Last updated