Второй этап выхода из офлайн режима

This commit is contained in:
Халимов Рустам
2026-04-20 10:46:09 +03:00
parent b2c29958b5
commit c839a9bc03
19 changed files with 899 additions and 84 deletions
@@ -0,0 +1,152 @@
package core.network
import android.content.Context
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.util.Log
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
import javax.inject.Singleton
/**
* Менеджер состояния сети
* Отслеживает подключение к интернету через NetworkCallback + BroadcastReceiver
*/
@Singleton
class NetworkManager @Inject constructor(
private val context: Context
) {
private val _isOnline = MutableStateFlow(isNetworkAvailable())
val isOnline: StateFlow<Boolean> = _isOnline.asStateFlow()
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
private var networkReceiver: NetworkReceiver? = null
private var isMonitoring = false
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
Log.d("NetworkManager", "Network available - callback")
_isOnline.value = true
}
override fun onLost(network: Network) {
Log.d("NetworkManager", "Network lost - callback")
_isOnline.value = false
}
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
val hasInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
val hasValidated = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
Log.d("NetworkManager", "Capabilities changed: hasInternet=$hasInternet, hasValidated=$hasValidated")
_isOnline.value = hasInternet && hasValidated
}
override fun onUnavailable() {
Log.d("NetworkManager", "Network unavailable - callback")
_isOnline.value = false
}
}
fun startMonitoring() {
if (isMonitoring) {
Log.w("NetworkManager", "Already monitoring, skipping")
return
}
isMonitoring = true
val cm = connectivityManager ?: run {
Log.e("NetworkManager", "ConnectivityManager is null")
return
}
try {
// 1. Регистрируем NetworkCallback для активного отслеживания
val networkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.build()
cm.registerNetworkCallback(networkRequest, networkCallback)
Log.d("NetworkManager", "Registered NetworkCallback")
// 2. Регистрируем BroadcastReceiver как запасной механизм
// (на Android 10+ работает только для foreground приложений)
val receiver = NetworkReceiver(this)
networkReceiver = receiver
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
try {
@Suppress("DEPRECATION")
context.registerReceiver(receiver, filter)
Log.d("NetworkManager", "Registered NetworkReceiver")
} catch (e: Exception) {
Log.w("NetworkManager", "Failed to register BroadcastReceiver: ${e.message}")
}
// 3. Инициализируем текущее состояние
_isOnline.value = isNetworkAvailable()
Log.d("NetworkManager", "Initial network state: ${_isOnline.value}")
} catch (e: Exception) {
Log.e("NetworkManager", "Error setting up network monitoring", e)
_isOnline.value = isNetworkAvailable()
}
}
fun stopMonitoring() {
if (!isMonitoring) return
isMonitoring = false
try {
connectivityManager?.unregisterNetworkCallback(networkCallback)
Log.d("NetworkManager", "Unregistered NetworkCallback")
} catch (e: Exception) {
Log.e("NetworkManager", "Error unregistering NetworkCallback", e)
}
try {
networkReceiver?.let {
context.unregisterReceiver(it)
it.cleanup()
networkReceiver = null
Log.d("NetworkManager", "Unregistered NetworkReceiver")
}
} catch (e: Exception) {
Log.e("NetworkManager", "Error unregistering NetworkReceiver", e)
}
}
private fun isNetworkAvailable(): Boolean {
return try {
val network = connectivityManager?.activeNetwork
val capabilities = connectivityManager?.getNetworkCapabilities(network)
val available = capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
Log.d("NetworkManager", "isNetworkAvailable check: $available")
available
} catch (e: Exception) {
Log.e("NetworkManager", "Error checking network", e)
false
}
}
fun notifyNetworkRestored() {
Log.d("NetworkManager", "Network restored notified - forcing state update")
_isOnline.value = true
}
/**
* Принудительно проверяет текущее состояние сети и уведомляет подписчиков
*/
fun refreshNetworkState() {
val currentState = isNetworkAvailable()
Log.d("NetworkManager", "Refreshed network state: $currentState")
_isOnline.value = currentState
}
}
@@ -0,0 +1,68 @@
package core.network
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton
/**
* BroadcastReceiver для надежного отслеживания изменений сети
* Используется как дополнение к NetworkCallback для лучшей надежности
*/
@Singleton
class NetworkReceiver @Inject constructor(
private val networkManager: NetworkManager
) : BroadcastReceiver() {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private var lastKnownState: Boolean = false
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
val isConnected = isNetworkAvailable(context)
Log.d("NetworkReceiver", "Broadcast received: isConnected=$isConnected, lastKnown=$lastKnownState")
// Отправляем уведомление только если состояние изменилось
if (isConnected != lastKnownState) {
lastKnownState = isConnected
scope.launch {
if (isConnected) {
Log.d("NetworkReceiver", "Network connected - notifying NetworkManager")
// Небольшая задержка для стабилизации сети
kotlinx.coroutines.delay(500)
networkManager.notifyNetworkRestored()
} else {
Log.d("NetworkReceiver", "Network disconnected")
}
}
}
}
}
private fun isNetworkAvailable(context: Context): Boolean {
return try {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
val network = cm?.activeNetwork
val capabilities = cm?.getNetworkCapabilities(network)
capabilities?.hasCapability(android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
} catch (e: Exception) {
Log.e("NetworkReceiver", "Error checking network", e)
false
}
}
fun cleanup() {
scope.cancel()
}
}