diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 843b00f..879d934 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -78,27 +78,11 @@ android { 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 { debug { - signingConfig = signingConfigs.getByName("debug") + isDebuggable = true } release { - signingConfig = signingConfigs.getByName("release") isMinifyEnabled = false } } diff --git a/app/src/androidMain/kotlin/com/aerotoss/discovery/AndroidNsdDiscovery.kt b/app/src/androidMain/kotlin/com/aerotoss/discovery/AndroidNsdDiscovery.kt index 5caf881..251663e 100644 --- a/app/src/androidMain/kotlin/com/aerotoss/discovery/AndroidNsdDiscovery.kt +++ b/app/src/androidMain/kotlin/com/aerotoss/discovery/AndroidNsdDiscovery.kt @@ -60,11 +60,13 @@ class AndroidNsdDiscovery(context: Context) : DiscoveryManager { private fun startServiceDiscovery() { discoveryListener = object : NsdManager.DiscoveryListener { override fun onDiscoveryStarted(serviceType: String) { - Log.d(TAG, "Discovery started") + Log.d(TAG, "Discovery started for type: $serviceType") } 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 && resolvingServices.add(serviceInfo.serviceName) ) { diff --git a/app/src/commonMain/kotlin/com/aerotoss/core/AeroTossManager.kt b/app/src/commonMain/kotlin/com/aerotoss/core/AeroTossManager.kt index 5821dd3..23111ad 100644 --- a/app/src/commonMain/kotlin/com/aerotoss/core/AeroTossManager.kt +++ b/app/src/commonMain/kotlin/com/aerotoss/core/AeroTossManager.kt @@ -15,10 +15,19 @@ class AeroTossManager( val incomingTransfers: Flow = transfer.incomingTransfers val outgoingTransfers: Flow = transfer.outgoingTransfers + val serverPort: Int + get() = transfer.getServerPort() + fun start(serverPort: Int) { discovery.startDiscovery(serverPort) } + fun rescan() { + val port = transfer.getServerPort() + discovery.stopDiscovery() + discovery.startDiscovery(port) + } + suspend fun sendFile(file: File, target: Device): Flow { require(file.exists() && file.isFile) { "File does not exist or is not a regular file" } return transfer.sendFile(file, target.hostAddress, target.port) diff --git a/app/src/commonMain/kotlin/com/aerotoss/ui/screens/HomeScreen.kt b/app/src/commonMain/kotlin/com/aerotoss/ui/screens/HomeScreen.kt index 8d5dc4f..f87f534 100644 --- a/app/src/commonMain/kotlin/com/aerotoss/ui/screens/HomeScreen.kt +++ b/app/src/commonMain/kotlin/com/aerotoss/ui/screens/HomeScreen.kt @@ -36,6 +36,14 @@ fun HomeScreen( Row( horizontalArrangement = Arrangement.spacedBy(16.dp) ) { + FloatingActionButton( + onClick = { + manager.rescan() + }, + containerColor = MaterialTheme.colorScheme.tertiaryContainer + ) { + Text("Обновить", style = MaterialTheme.typography.labelLarge) + } FloatingActionButton( onClick = onReceiveClick, containerColor = MaterialTheme.colorScheme.secondaryContainer @@ -70,20 +78,35 @@ fun HomeScreen( ) Spacer(modifier = Modifier.height(16.dp)) CircularProgressIndicator() + Spacer(modifier = Modifier.height(16.dp)) + OutlinedButton( + onClick = { manager.rescan() } + ) { + Text("Сканировать заново") + } Spacer(modifier = Modifier.height(8.dp)) Text( - "Убедитесь, что другое устройство\nтакже запустило AeroToss", + "Убедитесь, что другое устройство\nтакже запустило AeroToss\nв одной Wi-Fi сети", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } else { - Text( - "Найдено устройств: ${devices.size}", - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + 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)) LazyColumn { items(devices, key = { it.id }) { device -> diff --git a/app/src/desktopMain/kotlin/com/aerotoss/discovery/JmdnsDiscovery.kt b/app/src/desktopMain/kotlin/com/aerotoss/discovery/JmdnsDiscovery.kt index 1caa6ea..1827cd1 100644 --- a/app/src/desktopMain/kotlin/com/aerotoss/discovery/JmdnsDiscovery.kt +++ b/app/src/desktopMain/kotlin/com/aerotoss/discovery/JmdnsDiscovery.kt @@ -32,6 +32,7 @@ class JmdnsDiscovery : DiscoveryManager { discoveryThread = Thread { try { val addr = findLocalAddress() ?: return@Thread + println("AeroToss: Starting mDNS on ${addr.hostAddress}") jmdns = JmDNS.create(addr, "aerotoss-$deviceId") val serviceInfo = ServiceInfo.create( @@ -41,13 +42,16 @@ class JmdnsDiscovery : DiscoveryManager { "path=/ aerotoss=1 id=$deviceId name=$deviceName" ) jmdns?.registerService(serviceInfo) + println("AeroToss: Registered service on port $servicePort") serviceListener = object : ServiceListener { override fun serviceAdded(event: ServiceEvent) { + println("AeroToss: Service added: ${event.name} type=${event.type}") jmdns?.requestServiceInfo(event.type, event.name, true) } override fun serviceRemoved(event: ServiceEvent) { + println("AeroToss: Service removed: ${event.name}") discoveredDevices.remove(event.name) _devices.value = discoveredDevices.values.toList() } @@ -55,6 +59,7 @@ class JmdnsDiscovery : DiscoveryManager { override fun serviceResolved(event: ServiceEvent) { val info = event.info val hostAddresses = info.hostAddresses + println("AeroToss: Service resolved: ${event.name} addresses=${hostAddresses.toList()}") if (hostAddresses.isNotEmpty()) { val attributes = info.textBytes?.let { parseAttributes(it) } ?: emptyMap() val id = attributes["id"] ?: event.name @@ -75,9 +80,15 @@ class JmdnsDiscovery : DiscoveryManager { } 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() } catch (e: Exception) { e.printStackTrace() + println("AeroToss: mDNS error: ${e.message}") } }.apply { isDaemon = true @@ -91,6 +102,7 @@ class JmdnsDiscovery : DiscoveryManager { discoveryThread = null try { serviceListener?.let { jmdns?.removeServiceListener(SERVICE_TYPE, it) } + serviceListener?.let { jmdns?.removeServiceListener("_aerotoss._tcp", it) } jmdns?.unregisterAllServices() jmdns?.close() } catch (_: Exception) { @@ -142,7 +154,7 @@ class JmdnsDiscovery : DiscoveryManager { } companion object { - const val SERVICE_TYPE = "_aerotoss._tcp.local." + const val SERVICE_TYPE = "_aerotoss._tcp." const val SERVICE_NAME = "AeroToss" } }