AeroToss: полный рефакторинг discovery
- TcpDiscovery принимает DeviceType в конструкторе (PHONE/DESKTOP) - Desktop: type=DESKTOP, Phone: type=PHONE (больше не захардкожено) - Удалён NSD discovery из Android (только TCP + Wi-Fi Direct) - CompositeAndroidDiscovery больше не использует AndroidNsdDiscovery - Удалён UdpBroadcastDiscovery (дублировал TcpDiscovery) - deviceType теперь корректно передаётся в DiscoveryResponse - hostAddress теперь всегда содержит реальный IP
This commit is contained in:
@@ -1,16 +1,10 @@
|
|||||||
package com.aerotoss
|
package com.aerotoss
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.aerotoss.core.AeroTossManager
|
import com.aerotoss.core.AeroTossManager
|
||||||
import com.aerotoss.discovery.AndroidNsdDiscovery
|
|
||||||
import com.aerotoss.discovery.CompositeAndroidDiscovery
|
import com.aerotoss.discovery.CompositeAndroidDiscovery
|
||||||
import com.aerotoss.transfer.AndroidFileTransferManager
|
import com.aerotoss.transfer.AndroidFileTransferManager
|
||||||
import com.aerotoss.ui.App
|
import com.aerotoss.ui.App
|
||||||
@@ -20,73 +14,24 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
private var manager: AeroTossManager? = null
|
private var manager: AeroTossManager? = null
|
||||||
|
|
||||||
private val requiredPermissions: Array<String>
|
|
||||||
get() {
|
|
||||||
val perms = mutableListOf(
|
|
||||||
Manifest.permission.INTERNET,
|
|
||||||
Manifest.permission.ACCESS_WIFI_STATE,
|
|
||||||
Manifest.permission.CHANGE_WIFI_STATE,
|
|
||||||
Manifest.permission.ACCESS_NETWORK_STATE,
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
|
||||||
Manifest.permission.ACCESS_COARSE_LOCATION,
|
|
||||||
Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
|
|
||||||
)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
perms.add(Manifest.permission.NEARBY_WIFI_DEVICES)
|
|
||||||
}
|
|
||||||
return perms.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val permissionLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.RequestMultiplePermissions()
|
|
||||||
) { permissions ->
|
|
||||||
val allGranted = permissions.values.all { it }
|
|
||||||
if (allGranted && manager == null) {
|
|
||||||
initializeApp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
if (manager != null) return
|
|
||||||
|
|
||||||
if (hasAllPermissions()) {
|
|
||||||
initializeApp()
|
|
||||||
} else {
|
|
||||||
permissionLauncher.launch(requiredPermissions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasAllPermissions(): Boolean {
|
|
||||||
return requiredPermissions.all {
|
|
||||||
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeApp() {
|
|
||||||
if (manager != null) return
|
if (manager != null) return
|
||||||
|
|
||||||
val transferManager = AndroidFileTransferManager(applicationContext)
|
val transferManager = AndroidFileTransferManager(applicationContext)
|
||||||
val nsdDiscovery = AndroidNsdDiscovery(applicationContext)
|
|
||||||
val wifiDirectManager = WifiDirectManager(applicationContext)
|
val wifiDirectManager = WifiDirectManager(applicationContext)
|
||||||
val discoveryManager = CompositeAndroidDiscovery(nsdDiscovery, wifiDirectManager)
|
val discoveryManager = CompositeAndroidDiscovery(wifiDirectManager)
|
||||||
val mgr = AeroTossManager(discoveryManager, transferManager)
|
val mgr = AeroTossManager(discoveryManager, transferManager)
|
||||||
manager = mgr
|
manager = mgr
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val port = withContext(Dispatchers.IO) {
|
val port = withContext(Dispatchers.IO) { transferManager.startServer(0) }
|
||||||
transferManager.startServer(0)
|
|
||||||
}
|
|
||||||
mgr.start(port)
|
mgr.start(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
setContent {
|
setContent { App(mgr) }
|
||||||
App(mgr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.aerotoss.discovery
|
package com.aerotoss.discovery
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.aerotoss.model.Device
|
import com.aerotoss.model.Device
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -8,17 +7,17 @@ import kotlinx.coroutines.SupervisorJob
|
|||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import com.aerotoss.model.DeviceType
|
||||||
import com.aerotoss.wifi.WifiDirectManager
|
import com.aerotoss.wifi.WifiDirectManager
|
||||||
|
|
||||||
class CompositeAndroidDiscovery(
|
class CompositeAndroidDiscovery(
|
||||||
private val nsdDiscovery: AndroidNsdDiscovery,
|
|
||||||
private val wifiDirectManager: WifiDirectManager
|
private val wifiDirectManager: WifiDirectManager
|
||||||
) : DiscoveryManager {
|
) : DiscoveryManager {
|
||||||
|
|
||||||
private val _devices = MutableStateFlow<List<Device>>(emptyList())
|
private val _devices = MutableStateFlow<List<Device>>(emptyList())
|
||||||
override val devices: Flow<List<Device>> = _devices.asStateFlow()
|
override val devices: Flow<List<Device>> = _devices.asStateFlow()
|
||||||
|
|
||||||
private val tcpDiscovery = TcpDiscovery()
|
private val tcpDiscovery = TcpDiscovery(DeviceType.PHONE)
|
||||||
|
|
||||||
@Volatile private var scope: CoroutineScope? = null
|
@Volatile private var scope: CoroutineScope? = null
|
||||||
@Volatile private var discoveryJob: kotlinx.coroutines.Job? = null
|
@Volatile private var discoveryJob: kotlinx.coroutines.Job? = null
|
||||||
@@ -36,19 +35,16 @@ class CompositeAndroidDiscovery(
|
|||||||
tcpDiscovery.devices,
|
tcpDiscovery.devices,
|
||||||
wifiDirectManager.devices
|
wifiDirectManager.devices
|
||||||
) { tcpDevices, wifiDirectDevices ->
|
) { tcpDevices, wifiDirectDevices ->
|
||||||
val allDevices = mutableMapOf<String, Device>()
|
val all = mutableMapOf<String, Device>()
|
||||||
tcpDevices.forEach { allDevices["tcp:${it.id}"] = it }
|
tcpDevices.forEach { all["tcp:${it.id}"] = it }
|
||||||
wifiDirectDevices.forEach { allDevices["wifi:${it.id}"] = it }
|
wifiDirectDevices.forEach { all["wifi:${it.id}"] = it }
|
||||||
allDevices.values.toList()
|
all.values.toList()
|
||||||
}.collect { devices ->
|
}.collect { _devices.value = it }
|
||||||
_devices.value = devices
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopDiscovery() {
|
override fun stopDiscovery() {
|
||||||
discoveryJob?.cancel()
|
discoveryJob?.cancel(); discoveryJob = null
|
||||||
discoveryJob = null
|
|
||||||
tcpDiscovery.stopDiscovery()
|
tcpDiscovery.stopDiscovery()
|
||||||
wifiDirectManager.stopDiscovery()
|
wifiDirectManager.stopDiscovery()
|
||||||
_devices.value = emptyList()
|
_devices.value = emptyList()
|
||||||
@@ -56,8 +52,7 @@ class CompositeAndroidDiscovery(
|
|||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
stopDiscovery()
|
stopDiscovery()
|
||||||
scope?.cancel()
|
scope?.cancel(); scope = null
|
||||||
scope = null
|
|
||||||
tcpDiscovery.dispose()
|
tcpDiscovery.dispose()
|
||||||
wifiDirectManager.cleanup()
|
wifiDirectManager.cleanup()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ import java.net.ServerSocket
|
|||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class TcpDiscovery : DiscoveryManager {
|
class TcpDiscovery(
|
||||||
|
private val myDeviceType: DeviceType = DeviceType.DESKTOP
|
||||||
|
) : DiscoveryManager {
|
||||||
|
|
||||||
private val _devices = MutableStateFlow<List<Device>>(emptyList())
|
private val _devices = MutableStateFlow<List<Device>>(emptyList())
|
||||||
override val devices: Flow<List<Device>> = _devices.asStateFlow()
|
override val devices: Flow<List<Device>> = _devices.asStateFlow()
|
||||||
|
|
||||||
@@ -34,17 +37,16 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
override fun startDiscovery(servicePort: Int) {
|
override fun startDiscovery(servicePort: Int) {
|
||||||
if (serverThread != null) return
|
if (serverThread != null) return
|
||||||
|
|
||||||
// TCP server for incoming discovery requests
|
// TCP server
|
||||||
serverThread = Thread {
|
serverThread = Thread {
|
||||||
try {
|
try {
|
||||||
val ss = ServerSocket(TCP_PORT, 50, InetAddress.getByName("0.0.0.0"))
|
val ss = ServerSocket(TCP_PORT, 50, InetAddress.getByName("0.0.0.0"))
|
||||||
serverSocket = ss
|
serverSocket = ss
|
||||||
println("AeroToss: TCP server on port $TCP_PORT, service on $servicePort")
|
println("AeroToss: TCP=$TCP_PORT, service=$servicePort, type=$myDeviceType, name=$deviceName")
|
||||||
|
|
||||||
while (!Thread.currentThread().isInterrupted) {
|
while (!Thread.currentThread().isInterrupted) {
|
||||||
try {
|
try {
|
||||||
val client = ss.accept()
|
val client = ss.accept()
|
||||||
Thread { handleDiscoveryRequest(client, servicePort) }.start()
|
Thread { handleRequest(client, servicePort) }.start()
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
if (Thread.currentThread().isInterrupted) break
|
if (Thread.currentThread().isInterrupted) break
|
||||||
}
|
}
|
||||||
@@ -53,135 +55,114 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (!Thread.currentThread().isInterrupted) println("AeroToss: Server error: ${e.message}")
|
if (!Thread.currentThread().isInterrupted) println("AeroToss: Server error: ${e.message}")
|
||||||
}
|
}
|
||||||
}.apply { isDaemon = true; name = "aerotoss-server"; start() }
|
}.apply { isDaemon = true; name = "aerotoss-tcp"; start() }
|
||||||
|
|
||||||
// UDP broadcast: send AND receive on same socket
|
// UDP broadcast
|
||||||
discoveryThread = Thread {
|
discoveryThread = Thread {
|
||||||
try {
|
try {
|
||||||
val socket = DatagramSocket(UDP_PORT, InetAddress.getByName("0.0.0.0"))
|
val socket = DatagramSocket(UDP_PORT, InetAddress.getByName("0.0.0.0"))
|
||||||
socket.broadcast = true
|
socket.broadcast = true
|
||||||
socket.soTimeout = 2000
|
socket.soTimeout = 2000
|
||||||
udpSocket = socket
|
udpSocket = socket
|
||||||
|
|
||||||
var lastBroadcast = System.currentTimeMillis()
|
var lastBroadcast = System.currentTimeMillis()
|
||||||
|
|
||||||
while (!Thread.currentThread().isInterrupted) {
|
while (!Thread.currentThread().isInterrupted) {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now - lastBroadcast > 3_000) {
|
if (now - lastBroadcast > 3_000) {
|
||||||
sendBroadcast(socket, servicePort)
|
broadcast(socket, servicePort)
|
||||||
lastBroadcast = now
|
lastBroadcast = now
|
||||||
}
|
}
|
||||||
|
|
||||||
val buf = ByteArray(1024)
|
val buf = ByteArray(1024)
|
||||||
try {
|
try {
|
||||||
val packet = DatagramPacket(buf, buf.size)
|
val pkt = DatagramPacket(buf, buf.size)
|
||||||
socket.receive(packet)
|
socket.receive(pkt)
|
||||||
processBroadcast(packet, servicePort)
|
onBroadcastReceived(pkt, servicePort)
|
||||||
} catch (_: java.net.SocketTimeoutException) {}
|
} catch (_: java.net.SocketTimeoutException) {}
|
||||||
}
|
}
|
||||||
socket.close()
|
socket.close()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (!Thread.currentThread().isInterrupted) println("AeroToss: UDP error: ${e.message}")
|
if (!Thread.currentThread().isInterrupted) println("AeroToss: UDP error: ${e.message}")
|
||||||
}
|
}
|
||||||
}.apply { isDaemon = true; name = "aerotoss-broadcast"; start() }
|
}.apply { isDaemon = true; name = "aerotoss-udp"; start() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDiscoveryRequest(client: Socket, servicePort: Int) {
|
private fun handleRequest(client: Socket, servicePort: Int) {
|
||||||
try {
|
try {
|
||||||
client.use { sock ->
|
client.use { sock ->
|
||||||
sock.soTimeout = 5000
|
sock.soTimeout = 5000
|
||||||
val input = sock.getInputStream().bufferedReader()
|
val reader = sock.getInputStream().bufferedReader()
|
||||||
val output = sock.getOutputStream().bufferedWriter()
|
val writer = sock.getOutputStream().bufferedWriter()
|
||||||
|
val line = reader.readLine() ?: return
|
||||||
val request = input.readLine() ?: return
|
if (line == "DISCOVER") {
|
||||||
if (request == "DISCOVER") {
|
val resp = DiscoveryResponse(
|
||||||
val response = DiscoveryResponse(
|
id = deviceId,
|
||||||
deviceId = deviceId,
|
name = deviceName,
|
||||||
deviceName = deviceName,
|
type = myDeviceType.name.lowercase(),
|
||||||
deviceType = "desktop",
|
port = servicePort,
|
||||||
servicePort = servicePort,
|
ip = findMyIp()
|
||||||
hostAddress = findNetworkAddress()
|
|
||||||
)
|
)
|
||||||
output.write(json.encodeToString(response))
|
writer.write(json.encodeToString(resp))
|
||||||
output.newLine()
|
writer.newLine()
|
||||||
output.flush()
|
writer.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendBroadcast(socket: DatagramSocket, servicePort: Int) {
|
private fun broadcast(socket: DatagramSocket, servicePort: Int) {
|
||||||
try {
|
try {
|
||||||
val message = "AEROTOSS:$TCP_PORT:$servicePort:$deviceId:$deviceName"
|
val msg = "AEROTOSS:$TCP_PORT:$servicePort:$deviceId:$deviceName"
|
||||||
val data = message.toByteArray(Charsets.UTF_8)
|
val pkt = DatagramPacket(msg.toByteArray(Charsets.UTF_8), msg.length, InetAddress.getByName("255.255.255.255"), UDP_PORT)
|
||||||
val packet = DatagramPacket(data, data.size, InetAddress.getByName("255.255.255.255"), UDP_PORT)
|
socket.send(pkt)
|
||||||
socket.send(packet)
|
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processBroadcast(packet: DatagramPacket, myPort: Int) {
|
private fun onBroadcastReceived(pkt: DatagramPacket, myServicePort: Int) {
|
||||||
try {
|
try {
|
||||||
val message = String(packet.data, 0, packet.length, Charsets.UTF_8)
|
val msg = String(pkt.data, 0, pkt.length, Charsets.UTF_8)
|
||||||
if (!message.startsWith("AEROTOSS:")) return
|
if (!msg.startsWith("AEROTOSS:")) return
|
||||||
|
val parts = msg.split(":", limit = 5)
|
||||||
val parts = message.split(":", limit = 5)
|
|
||||||
if (parts.size < 5) return
|
if (parts.size < 5) return
|
||||||
|
|
||||||
val remoteTcpPort = parts[1].toIntOrNull() ?: return
|
val remoteTcpPort = parts[1].toIntOrNull() ?: return
|
||||||
val remoteServicePort = parts[2].toIntOrNull() ?: return
|
val remoteServicePort = parts[2].toIntOrNull() ?: return
|
||||||
val remoteId = parts[3]
|
val remoteId = parts[3]
|
||||||
val remoteName = parts[4]
|
val remoteName = parts[4]
|
||||||
|
|
||||||
if (remoteId == deviceId) return
|
if (remoteId == deviceId) return
|
||||||
if (remoteServicePort == myPort) return
|
if (remoteServicePort == myServicePort) return
|
||||||
|
|
||||||
val hostAddress = packet.address.hostAddress ?: return
|
|
||||||
val device = queryDevice(hostAddress, remoteTcpPort, remoteId, remoteName, remoteServicePort)
|
|
||||||
|
|
||||||
|
val host = pkt.address.hostAddress ?: return
|
||||||
|
val device = queryRemote(host, remoteTcpPort)
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
discoveredDevices[device.id] = device
|
discoveredDevices[device.id] = device
|
||||||
_devices.value = discoveredDevices.values.toList()
|
_devices.value = discoveredDevices.values.toList()
|
||||||
println("AeroToss: Found ${device.name} at ${device.hostAddress}:${device.port}")
|
println("AeroToss: Found ${device.name} [${device.type}] at ${device.hostAddress}:${device.port}")
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun queryDevice(host: String, tcpPort: Int, fallbackId: String, fallbackName: String, fallbackPort: Int): Device? {
|
private fun queryRemote(host: String, tcpPort: Int): Device? {
|
||||||
return try {
|
return try {
|
||||||
val socket = Socket()
|
Socket().use { sock ->
|
||||||
socket.connect(java.net.InetSocketAddress(host, tcpPort), 3000)
|
sock.connect(java.net.InetSocketAddress(host, tcpPort), 3000)
|
||||||
socket.use { sock ->
|
|
||||||
sock.soTimeout = 3000
|
sock.soTimeout = 3000
|
||||||
val output = sock.getOutputStream().bufferedWriter()
|
val writer = sock.getOutputStream().bufferedWriter()
|
||||||
val input = sock.getInputStream().bufferedReader()
|
val reader = sock.getInputStream().bufferedReader()
|
||||||
|
writer.write("DISCOVER")
|
||||||
output.write("DISCOVER")
|
writer.newLine()
|
||||||
output.newLine()
|
writer.flush()
|
||||||
output.flush()
|
val line = reader.readLine() ?: return null
|
||||||
|
val resp = json.decodeFromString<DiscoveryResponse>(line)
|
||||||
val responseLine = input.readLine() ?: return null
|
|
||||||
val response = json.decodeFromString<DiscoveryResponse>(responseLine)
|
|
||||||
|
|
||||||
Device(
|
Device(
|
||||||
id = response.deviceId,
|
id = resp.id,
|
||||||
name = response.deviceName,
|
name = resp.name,
|
||||||
type = if (response.deviceType == "desktop") DeviceType.DESKTOP else DeviceType.PHONE,
|
type = if (resp.type == "desktop") DeviceType.DESKTOP else DeviceType.PHONE,
|
||||||
hostAddress = host,
|
hostAddress = resp.ip,
|
||||||
port = response.servicePort
|
port = resp.port
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) { null }
|
||||||
Device(
|
|
||||||
id = fallbackId,
|
|
||||||
name = fallbackName,
|
|
||||||
type = DeviceType.PHONE,
|
|
||||||
hostAddress = host,
|
|
||||||
port = fallbackPort
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findNetworkAddress(): String {
|
private fun findMyIp(): String {
|
||||||
return try {
|
return try {
|
||||||
NetworkInterface.getNetworkInterfaces()?.toList()
|
NetworkInterface.getNetworkInterfaces()?.toList()
|
||||||
?.filter { it.isUp && !it.isLoopback }
|
?.filter { it.isUp && !it.isLoopback }
|
||||||
@@ -193,36 +174,31 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isPrivate(addr: java.net.InetAddress): Boolean {
|
private fun isPrivate(addr: java.net.InetAddress): Boolean {
|
||||||
val bytes = addr.address
|
val b = addr.address
|
||||||
return (bytes[0].toInt() and 0xFF == 192 && bytes[1].toInt() and 0xFF == 168) ||
|
return (b[0].toInt() and 0xFF == 192 && b[1].toInt() and 0xFF == 168) ||
|
||||||
(bytes[0].toInt() and 0xFF == 10) ||
|
(b[0].toInt() and 0xFF == 10) ||
|
||||||
(bytes[0].toInt() and 0xFF == 172 && (bytes[1].toInt() and 0xFF in 16..31))
|
(b[0].toInt() and 0xFF == 172 && b[1].toInt() and 0xFF in 16..31)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopDiscovery() {
|
override fun stopDiscovery() {
|
||||||
serverThread?.interrupt()
|
serverThread?.interrupt(); serverThread = null
|
||||||
serverThread = null
|
discoveryThread?.interrupt(); discoveryThread = null
|
||||||
discoveryThread?.interrupt()
|
|
||||||
discoveryThread = null
|
|
||||||
try { serverSocket?.close() } catch (_: Exception) {}
|
try { serverSocket?.close() } catch (_: Exception) {}
|
||||||
try { udpSocket?.close() } catch (_: Exception) {}
|
try { udpSocket?.close() } catch (_: Exception) {}
|
||||||
serverSocket = null
|
serverSocket = null; udpSocket = null
|
||||||
udpSocket = null
|
|
||||||
discoveredDevices.clear()
|
discoveredDevices.clear()
|
||||||
_devices.value = emptyList()
|
_devices.value = emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() { stopDiscovery() }
|
||||||
stopDiscovery()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class DiscoveryResponse(
|
data class DiscoveryResponse(
|
||||||
val deviceId: String,
|
val id: String,
|
||||||
val deviceName: String,
|
val name: String,
|
||||||
val deviceType: String,
|
val type: String,
|
||||||
val servicePort: Int,
|
val port: Int,
|
||||||
val hostAddress: String
|
val ip: String
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import androidx.compose.ui.window.Window
|
|||||||
import androidx.compose.ui.window.application
|
import androidx.compose.ui.window.application
|
||||||
import com.aerotoss.core.AeroTossManager
|
import com.aerotoss.core.AeroTossManager
|
||||||
import com.aerotoss.discovery.TcpDiscovery
|
import com.aerotoss.discovery.TcpDiscovery
|
||||||
|
import com.aerotoss.model.DeviceType
|
||||||
import com.aerotoss.transfer.DesktopFileTransferManager
|
import com.aerotoss.transfer.DesktopFileTransferManager
|
||||||
import com.aerotoss.ui.App
|
import com.aerotoss.ui.App
|
||||||
|
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
val transferManager = remember { DesktopFileTransferManager() }
|
val transferManager = remember { DesktopFileTransferManager() }
|
||||||
val discoveryManager = remember { TcpDiscovery() }
|
val discoveryManager = remember { TcpDiscovery(DeviceType.DESKTOP) }
|
||||||
val manager = remember { AeroTossManager(discoveryManager, transferManager) }
|
val manager = remember { AeroTossManager(discoveryManager, transferManager) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
@@ -19,10 +20,7 @@ fun main() = application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Window(
|
Window(
|
||||||
onCloseRequest = {
|
onCloseRequest = { manager.dispose(); exitApplication() },
|
||||||
manager.dispose()
|
|
||||||
exitApplication()
|
|
||||||
},
|
|
||||||
title = "AeroToss"
|
title = "AeroToss"
|
||||||
) {
|
) {
|
||||||
App(manager)
|
App(manager)
|
||||||
|
|||||||
Reference in New Issue
Block a user