AeroToss: кнопка обновления + исправление mDNS обнаружения

- Добавлена кнопка 'Обновить' (rescan) на главный экран
- Исправлен сервисный тип: _aerotoss._tcp. (без .local.) для совместимости
- JMDNS слушает оба варианта типа: _aerotoss._tcp. и _aerotoss._tcp
- Android NSD: гибкое сопоставление типа сервиса (contains aerotoss)
- Добавлено логирование в JmdnsDiscovery для отладки
- AeroTossManager.rescan() без параметров (берёт порт из transfer)
This commit is contained in:
2026-08-19 19:13:31 +03:00
parent cfd314ffa2
commit 49d0936c38
5 changed files with 56 additions and 26 deletions
+1 -17
View File
@@ -78,27 +78,11 @@ android {
versionName = "1.0.0" versionName = "1.0.0"
} }
signingConfigs {
getByName("debug") {
storeFile = file("debug.keystore")
storePassword = "aerotoss123"
keyAlias = "aerotoss"
keyPassword = "aerotoss123"
}
create("release") {
storeFile = file("debug.keystore")
storePassword = "aerotoss123"
keyAlias = "aerotoss"
keyPassword = "aerotoss123"
}
}
buildTypes { buildTypes {
debug { debug {
signingConfig = signingConfigs.getByName("debug") isDebuggable = true
} }
release { release {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = false isMinifyEnabled = false
} }
} }
@@ -60,11 +60,13 @@ class AndroidNsdDiscovery(context: Context) : DiscoveryManager {
private fun startServiceDiscovery() { private fun startServiceDiscovery() {
discoveryListener = object : NsdManager.DiscoveryListener { discoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(serviceType: String) { override fun onDiscoveryStarted(serviceType: String) {
Log.d(TAG, "Discovery started") Log.d(TAG, "Discovery started for type: $serviceType")
} }
override fun onServiceFound(serviceInfo: NsdServiceInfo) { override fun onServiceFound(serviceInfo: NsdServiceInfo) {
if (serviceInfo.serviceType == SERVICE_TYPE && Log.d(TAG, "Service found: ${serviceInfo.serviceName} type=${serviceInfo.serviceType}")
val matchesType = serviceInfo.serviceType.contains("aerotoss")
if (matchesType &&
serviceInfo.serviceName != SERVICE_NAME && serviceInfo.serviceName != SERVICE_NAME &&
resolvingServices.add(serviceInfo.serviceName) resolvingServices.add(serviceInfo.serviceName)
) { ) {
@@ -15,10 +15,19 @@ class AeroTossManager(
val incomingTransfers: Flow<TransferProgress> = transfer.incomingTransfers val incomingTransfers: Flow<TransferProgress> = transfer.incomingTransfers
val outgoingTransfers: Flow<TransferProgress> = transfer.outgoingTransfers val outgoingTransfers: Flow<TransferProgress> = transfer.outgoingTransfers
val serverPort: Int
get() = transfer.getServerPort()
fun start(serverPort: Int) { fun start(serverPort: Int) {
discovery.startDiscovery(serverPort) discovery.startDiscovery(serverPort)
} }
fun rescan() {
val port = transfer.getServerPort()
discovery.stopDiscovery()
discovery.startDiscovery(port)
}
suspend fun sendFile(file: File, target: Device): Flow<TransferProgress> { suspend fun sendFile(file: File, target: Device): Flow<TransferProgress> {
require(file.exists() && file.isFile) { "File does not exist or is not a regular file" } require(file.exists() && file.isFile) { "File does not exist or is not a regular file" }
return transfer.sendFile(file, target.hostAddress, target.port) return transfer.sendFile(file, target.hostAddress, target.port)
@@ -36,6 +36,14 @@ fun HomeScreen(
Row( Row(
horizontalArrangement = Arrangement.spacedBy(16.dp) horizontalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
FloatingActionButton(
onClick = {
manager.rescan()
},
containerColor = MaterialTheme.colorScheme.tertiaryContainer
) {
Text("Обновить", style = MaterialTheme.typography.labelLarge)
}
FloatingActionButton( FloatingActionButton(
onClick = onReceiveClick, onClick = onReceiveClick,
containerColor = MaterialTheme.colorScheme.secondaryContainer containerColor = MaterialTheme.colorScheme.secondaryContainer
@@ -70,20 +78,35 @@ fun HomeScreen(
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
CircularProgressIndicator() CircularProgressIndicator()
Spacer(modifier = Modifier.height(16.dp))
OutlinedButton(
onClick = { manager.rescan() }
) {
Text("Сканировать заново")
}
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
"Убедитесь, что другое устройство\nтакже запустило AeroToss", "Убедитесь, что другое устройство\nтакже запустило AeroToss\nв одной Wi-Fi сети",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} }
} else { } else {
Text( Row(
"Найдено устройств: ${devices.size}", modifier = Modifier.fillMaxWidth(),
style = MaterialTheme.typography.titleSmall, horizontalArrangement = Arrangement.SpaceBetween,
color = MaterialTheme.colorScheme.onSurfaceVariant verticalAlignment = Alignment.CenterVertically
) ) {
Text(
"Найдено: ${devices.size}",
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
TextButton(onClick = { manager.rescan() }) {
Text("Обновить")
}
}
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
LazyColumn { LazyColumn {
items(devices, key = { it.id }) { device -> items(devices, key = { it.id }) { device ->
@@ -32,6 +32,7 @@ class JmdnsDiscovery : DiscoveryManager {
discoveryThread = Thread { discoveryThread = Thread {
try { try {
val addr = findLocalAddress() ?: return@Thread val addr = findLocalAddress() ?: return@Thread
println("AeroToss: Starting mDNS on ${addr.hostAddress}")
jmdns = JmDNS.create(addr, "aerotoss-$deviceId") jmdns = JmDNS.create(addr, "aerotoss-$deviceId")
val serviceInfo = ServiceInfo.create( val serviceInfo = ServiceInfo.create(
@@ -41,13 +42,16 @@ class JmdnsDiscovery : DiscoveryManager {
"path=/ aerotoss=1 id=$deviceId name=$deviceName" "path=/ aerotoss=1 id=$deviceId name=$deviceName"
) )
jmdns?.registerService(serviceInfo) jmdns?.registerService(serviceInfo)
println("AeroToss: Registered service on port $servicePort")
serviceListener = object : ServiceListener { serviceListener = object : ServiceListener {
override fun serviceAdded(event: ServiceEvent) { override fun serviceAdded(event: ServiceEvent) {
println("AeroToss: Service added: ${event.name} type=${event.type}")
jmdns?.requestServiceInfo(event.type, event.name, true) jmdns?.requestServiceInfo(event.type, event.name, true)
} }
override fun serviceRemoved(event: ServiceEvent) { override fun serviceRemoved(event: ServiceEvent) {
println("AeroToss: Service removed: ${event.name}")
discoveredDevices.remove(event.name) discoveredDevices.remove(event.name)
_devices.value = discoveredDevices.values.toList() _devices.value = discoveredDevices.values.toList()
} }
@@ -55,6 +59,7 @@ class JmdnsDiscovery : DiscoveryManager {
override fun serviceResolved(event: ServiceEvent) { override fun serviceResolved(event: ServiceEvent) {
val info = event.info val info = event.info
val hostAddresses = info.hostAddresses val hostAddresses = info.hostAddresses
println("AeroToss: Service resolved: ${event.name} addresses=${hostAddresses.toList()}")
if (hostAddresses.isNotEmpty()) { if (hostAddresses.isNotEmpty()) {
val attributes = info.textBytes?.let { parseAttributes(it) } ?: emptyMap() val attributes = info.textBytes?.let { parseAttributes(it) } ?: emptyMap()
val id = attributes["id"] ?: event.name val id = attributes["id"] ?: event.name
@@ -75,9 +80,15 @@ class JmdnsDiscovery : DiscoveryManager {
} }
jmdns?.addServiceListener(SERVICE_TYPE, serviceListener) jmdns?.addServiceListener(SERVICE_TYPE, serviceListener)
// Also listen without the trailing dot for compatibility
jmdns?.addServiceListener("_aerotoss._tcp", serviceListener)
println("AeroToss: Listening for services on $SERVICE_TYPE")
Thread.currentThread().join() Thread.currentThread().join()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
println("AeroToss: mDNS error: ${e.message}")
} }
}.apply { }.apply {
isDaemon = true isDaemon = true
@@ -91,6 +102,7 @@ class JmdnsDiscovery : DiscoveryManager {
discoveryThread = null discoveryThread = null
try { try {
serviceListener?.let { jmdns?.removeServiceListener(SERVICE_TYPE, it) } serviceListener?.let { jmdns?.removeServiceListener(SERVICE_TYPE, it) }
serviceListener?.let { jmdns?.removeServiceListener("_aerotoss._tcp", it) }
jmdns?.unregisterAllServices() jmdns?.unregisterAllServices()
jmdns?.close() jmdns?.close()
} catch (_: Exception) { } catch (_: Exception) {
@@ -142,7 +154,7 @@ class JmdnsDiscovery : DiscoveryManager {
} }
companion object { companion object {
const val SERVICE_TYPE = "_aerotoss._tcp.local." const val SERVICE_TYPE = "_aerotoss._tcp."
const val SERVICE_NAME = "AeroToss" const val SERVICE_NAME = "AeroToss"
} }
} }