mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: add network availability monitoring
This commit is contained in:
parent
dabbcf6ef4
commit
2d30fd89bc
5 changed files with 62 additions and 21 deletions
|
|
@ -210,7 +210,7 @@ class BTScanModel @Inject constructor(
|
|||
}
|
||||
|
||||
// Include Network Service Discovery
|
||||
nsdRepository.resolvedList?.forEach { service ->
|
||||
nsdRepository.resolvedList.value.forEach { service ->
|
||||
addDevice(TCPDeviceListEntry(service))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
package com.geeksville.mesh.repository.nsd
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkRequest
|
||||
import android.net.nsd.NsdManager
|
||||
import android.net.nsd.NsdServiceInfo
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.CoroutineDispatchers
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -17,18 +22,38 @@ import javax.inject.Singleton
|
|||
class NsdRepository @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val nsdManagerLazy: dagger.Lazy<NsdManager?>,
|
||||
private val connectivityManager: dagger.Lazy<ConnectivityManager>,
|
||||
) : Logging {
|
||||
|
||||
private val resolveQueue = Semaphore(1)
|
||||
private var hostsList: ArrayList<NsdServiceInfo>? = null
|
||||
private val availableNetworks: HashSet<Network> = HashSet()
|
||||
val networkAvailable: Flow<Boolean> = callbackFlow {
|
||||
val callback = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
availableNetworks.add(network)
|
||||
trySend(availableNetworks.isNotEmpty()).isSuccess
|
||||
}
|
||||
|
||||
val resolvedList: List<NsdServiceInfo>? get() = hostsList
|
||||
override fun onLost(network: Network) {
|
||||
availableNetworks.remove(network)
|
||||
trySend(availableNetworks.isNotEmpty()).isSuccess
|
||||
}
|
||||
}
|
||||
val networkRequest = NetworkRequest.Builder().build()
|
||||
connectivityManager.get().registerNetworkCallback(networkRequest, callback)
|
||||
awaitClose { connectivityManager.get().unregisterNetworkCallback(callback) }
|
||||
}
|
||||
|
||||
private val resolveQueue = Semaphore(1)
|
||||
private val hostsList = mutableListOf<NsdServiceInfo>()
|
||||
|
||||
private val _resolvedList = MutableStateFlow<List<NsdServiceInfo>>(emptyList())
|
||||
val resolvedList: StateFlow<List<NsdServiceInfo>> get() = _resolvedList
|
||||
|
||||
private val _networkDiscovery: Flow<NsdServiceInfo> = callbackFlow {
|
||||
val discoveryListener = object : NsdManager.DiscoveryListener {
|
||||
override fun onDiscoveryStarted(regType: String) {
|
||||
debug("Service discovery started: $regType")
|
||||
hostsList?.clear()
|
||||
hostsList.clear()
|
||||
}
|
||||
|
||||
override fun onServiceFound(service: NsdServiceInfo) {
|
||||
|
|
@ -39,7 +64,8 @@ class NsdRepository @Inject constructor(
|
|||
val resolveListener = object : NsdManager.ResolveListener {
|
||||
override fun onServiceResolved(service: NsdServiceInfo) {
|
||||
debug("Resolve Succeeded: $service")
|
||||
hostsList?.add(service)
|
||||
hostsList.add(service)
|
||||
_resolvedList.value = hostsList
|
||||
trySend(service)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.geeksville.mesh.repository.nsd
|
|||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.nsd.NsdManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -12,9 +13,14 @@ import dagger.hilt.components.SingletonComponent
|
|||
@InstallIn(SingletonComponent::class)
|
||||
class NsdRepositoryModule {
|
||||
companion object {
|
||||
@Provides
|
||||
fun provideConnectivityManager(application: Application): ConnectivityManager {
|
||||
return application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideNsdManager(application: Application): NsdManager? {
|
||||
return application.getSystemService(Context.NSD_SERVICE) as NsdManager?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.geeksville.mesh.android.Logging
|
|||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.CoroutineDispatchers
|
||||
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
|
||||
import com.geeksville.mesh.repository.nsd.NsdRepository
|
||||
import com.geeksville.mesh.repository.usb.UsbRepository
|
||||
import com.geeksville.mesh.util.anonymize
|
||||
import com.geeksville.mesh.util.ignoreException
|
||||
|
|
@ -21,6 +22,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -38,7 +42,8 @@ import javax.inject.Singleton
|
|||
class RadioInterfaceService @Inject constructor(
|
||||
private val context: Application,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val bluetoothRepository: BluetoothRepository,
|
||||
bluetoothRepository: BluetoothRepository,
|
||||
nsdRepository: NsdRepository,
|
||||
private val processLifecycle: Lifecycle,
|
||||
private val usbRepository: UsbRepository,
|
||||
@RadioRepositoryQualifier private val prefs: SharedPreferences
|
||||
|
|
@ -72,15 +77,15 @@ class RadioInterfaceService @Inject constructor(
|
|||
private var isConnected = false
|
||||
|
||||
init {
|
||||
processLifecycle.coroutineScope.launch {
|
||||
bluetoothRepository.state.collect { state ->
|
||||
if (state.enabled) {
|
||||
startInterface()
|
||||
} else if (radioIf is BluetoothInterface) {
|
||||
stopInterface()
|
||||
}
|
||||
}
|
||||
}
|
||||
bluetoothRepository.state.onEach { state ->
|
||||
if (state.enabled) startInterface()
|
||||
else if (radioIf is BluetoothInterface) stopInterface()
|
||||
}.launchIn(processLifecycle.coroutineScope)
|
||||
|
||||
nsdRepository.networkAvailable.onEach { state ->
|
||||
if (state) startInterface()
|
||||
else if (radioIf is TCPInterface) stopInterface()
|
||||
}.launchIn(processLifecycle.coroutineScope)
|
||||
}
|
||||
|
||||
companion object : Logging {
|
||||
|
|
@ -174,14 +179,14 @@ class RadioInterfaceService @Inject constructor(
|
|||
fun onConnect() {
|
||||
if (!isConnected) {
|
||||
isConnected = true
|
||||
broadcastConnectionChanged(true, false)
|
||||
broadcastConnectionChanged(isConnected = true, isPermanent = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun onDisconnect(isPermanent: Boolean) {
|
||||
if (isConnected) {
|
||||
isConnected = false
|
||||
broadcastConnectionChanged(false, isPermanent)
|
||||
broadcastConnectionChanged(isConnected = false, isPermanent = isPermanent)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,14 +85,18 @@ class TCPInterface(service: RadioInterfaceService, private val address: String)
|
|||
BufferedInputStream(socket.getInputStream()).use { inputStream ->
|
||||
super.connect()
|
||||
|
||||
while (true) try {
|
||||
var timeoutCount = 0
|
||||
while (timeoutCount < 180) try { // close after 90s of inactivity
|
||||
val c = inputStream.read()
|
||||
if (c == -1) {
|
||||
warn("Got EOF on TCP stream")
|
||||
break
|
||||
} else
|
||||
} else {
|
||||
timeoutCount = 0
|
||||
readChar(c.toByte())
|
||||
}
|
||||
} catch (ex: SocketTimeoutException) {
|
||||
timeoutCount++
|
||||
// Ignore and start another read
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue