Refactor NsdManager and improve service display (#2292)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
pull/2309/head
James Rich 2025-06-29 14:18:14 +00:00 zatwierdzone przez GitHub
rodzic ec74bbfe19
commit 8b095aba09
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
5 zmienionych plików z 30 dodań i 23 usunięć

Wyświetl plik

@ -68,8 +68,9 @@ class BTScanModel @Inject constructor(
val devices = MutableLiveData<MutableMap<String, DeviceListEntry>>(mutableMapOf())
val errorText = MutableLiveData<String?>(null)
private val showMockInterface: StateFlow<Boolean> get() =
MutableStateFlow(radioInterfaceService.isMockInterface()).asStateFlow()
private val showMockInterface: StateFlow<Boolean>
get() =
MutableStateFlow(radioInterfaceService.isMockInterface()).asStateFlow()
init {
combine(
@ -84,7 +85,13 @@ class BTScanModel @Inject constructor(
}
// Include a placeholder for "None"
addDevice(DeviceListEntry(context.getString(R.string.none), NO_DEVICE_SELECTED, true))
addDevice(
DeviceListEntry(
context.getString(R.string.none),
NO_DEVICE_SELECTED,
true
)
)
if (showMockInterface) {
addDevice(DeviceListEntry("Demo Mode", "m", true))
@ -97,7 +104,19 @@ class BTScanModel @Inject constructor(
// Include Network Service Discovery
tcp.forEach { service ->
val address = service.toAddressString()
addDevice(DeviceListEntry(address, "t$address", true))
val txtRecords = service.attributes // Map<String, ByteArray?>
val shortNameBytes = txtRecords["shortname"]
val idBytes = txtRecords["id"]
val shortName = shortNameBytes?.let { String(it, Charsets.UTF_8) }
?: context.getString(R.string.meshtastic)
val deviceId =
idBytes?.let { String(it, Charsets.UTF_8) }?.replace("!", "")
var displayName = shortName
if (deviceId != null) {
displayName += "_$deviceId"
}
addDevice(DeviceListEntry(displayName, "t$address", true))
}
usb.forEach { (_, d) ->
@ -160,10 +179,6 @@ class BTScanModel @Inject constructor(
private var scanJob: Job? = null
val selectedAddress get() = radioInterfaceService.getDeviceAddress()
val selectedBluetooth: Boolean get() = selectedAddress?.getOrNull(0) == 'x'
// / Use the string for the NopInterface
val selectedAddressFlow: StateFlow<String?> = radioInterfaceService.currentDeviceAddressFlow
val selectedNotNullFlow: StateFlow<String> = selectedAddressFlow

Wyświetl plik

@ -41,16 +41,13 @@ class NetworkRepository @Inject constructor(
.conflate()
val resolvedList: Flow<List<NsdServiceInfo>>
get() = nsdManagerLazy.get().serviceList(SERVICE_TYPES, SERVICE_NAME)
get() = nsdManagerLazy.get().serviceList(SERVICE_TYPE)
.flowOn(dispatchers.io)
.conflate()
companion object {
// To find all available services use SERVICE_TYPE = "_services._dns-sd._udp"
internal const val SERVICE_NAME = "Meshtastic"
internal const val SERVICE_PORT = 4403
private const val SERVICE_TYPE = "_meshtastic._tcp"
internal val SERVICE_TYPES = setOf("_http._tcp", SERVICE_TYPE)
fun NsdServiceInfo.toAddressString() = buildString {
append(@Suppress("DEPRECATION") host.toString().substring(1))

Wyświetl plik

@ -24,27 +24,16 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.suspendCancellableCoroutine
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.coroutines.resume
internal fun NsdManager.serviceList(
serviceTypes: Set<String>,
serviceName: String,
): Flow<List<NsdServiceInfo>> {
val flows = serviceTypes.map { serviceType -> serviceList(serviceType, serviceName) }
return combine(flows) { lists -> lists.flatMap { it } }
}
@OptIn(ExperimentalCoroutinesApi::class)
internal fun NsdManager.serviceList(
serviceType: String,
serviceName: String,
): Flow<List<NsdServiceInfo>> = discoverServices(serviceType).mapLatest { serviceList ->
serviceList
.filter { it.serviceName.contains(serviceName) }
.mapNotNull { resolveService(it) }
}

Wyświetl plik

@ -81,6 +81,11 @@ fun DeviceListItem(
contentDescription
)
},
supportingContent = {
if (device.isTCP) {
Text(device.address)
}
},
trailingContent = {
if (selected) {
Icon(

Wyświetl plik

@ -705,4 +705,5 @@
<string name="no_network_devices">No Network devices found.</string>
<string name="no_usb_devices">No USB Serial devices found.</string>
<string name="scroll_to_bottom">Scroll to bottom</string>
<string name="meshtastic">Meshtastic</string>
</resources>