AeroToss: кнопка обновления + исправление mDNS обнаружения
- Добавлена кнопка 'Обновить' (rescan) на главный экран - Исправлен сервисный тип: _aerotoss._tcp. (без .local.) для совместимости - JMDNS слушает оба варианта типа: _aerotoss._tcp. и _aerotoss._tcp - Android NSD: гибкое сопоставление типа сервиса (contains aerotoss) - Добавлено логирование в JmdnsDiscovery для отладки - AeroTossManager.rescan() без параметров (берёт порт из transfer)
This commit is contained in:
+1
-17
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user