package com.craigvg.lichun_android.utils import android.content.Context import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.callbackFlow import javax.inject.Inject import javax.inject.Singleton /** * Network monitor for tracking connectivity status */ @Singleton class NetworkMonitor @Inject constructor( @ApplicationContext private val context: Context ) { private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager private val _isConnected = MutableStateFlow(checkCurrentConnectivity()) val isConnected: StateFlow = _isConnected.asStateFlow() private val _connectionType = MutableStateFlow(getCurrentConnectionType()) val connectionType: StateFlow = _connectionType.asStateFlow() companion object { private const val TAG = "NetworkMonitor" } enum class ConnectionType { WIFI, CELLULAR, ETHERNET, VPN, NONE } private val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { Log.d(TAG, "Network available") _isConnected.value = true _connectionType.value = getCurrentConnectionType() } override fun onLost(network: Network) { Log.d(TAG, "Network lost") _isConnected.value = checkCurrentConnectivity() _connectionType.value = getCurrentConnectionType() } override fun onCapabilitiesChanged(network: Network, capabilities: NetworkCapabilities) { _connectionType.value = getConnectionTypeFromCapabilities(capabilities) } } /** * Start monitoring network connectivity */ fun startMonitoring() { val request = NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build() try { connectivityManager.registerNetworkCallback(request, networkCallback) Log.d(TAG, "Network monitoring started") } catch (e: Exception) { Log.e(TAG, "Failed to register network callback: ${e.message}") } } /** * Stop monitoring network connectivity */ fun stopMonitoring() { try { connectivityManager.unregisterNetworkCallback(networkCallback) Log.d(TAG, "Network monitoring stopped") } catch (e: Exception) { Log.e(TAG, "Failed to unregister network callback: ${e.message}") } } /** * Check current connectivity status */ private fun checkCurrentConnectivity(): Boolean { val network = connectivityManager.activeNetwork ?: return false val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) } /** * Get current connection type */ private fun getCurrentConnectionType(): ConnectionType { val network = connectivityManager.activeNetwork ?: return ConnectionType.NONE val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return ConnectionType.NONE return getConnectionTypeFromCapabilities(capabilities) } private fun getConnectionTypeFromCapabilities(capabilities: NetworkCapabilities): ConnectionType { return when { capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> ConnectionType.WIFI capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> ConnectionType.CELLULAR capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> ConnectionType.ETHERNET capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> ConnectionType.VPN else -> ConnectionType.NONE } } /** * Observe network connectivity as a Flow */ fun observeConnectivity(): Flow = callbackFlow { val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { trySend(true) } override fun onLost(network: Network) { trySend(checkCurrentConnectivity()) } } val request = NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build() connectivityManager.registerNetworkCallback(request, callback) // Emit current state trySend(checkCurrentConnectivity()) awaitClose { connectivityManager.unregisterNetworkCallback(callback) } } /** * Check if currently connected to WiFi */ fun isWifiConnected(): Boolean = connectionType.value == ConnectionType.WIFI /** * Check if currently connected to cellular */ fun isCellularConnected(): Boolean = connectionType.value == ConnectionType.CELLULAR /** * Check if network is available (synchronous check) */ fun isNetworkAvailable(): Boolean = checkCurrentConnectivity() }