- Добавлен activeSockets для хранения сокетов при передаче
- cancelTransfer закрывает сокет + отменяет корутину + обновляет состояние
- Исправлено на обеих платформах (Desktop + Android)
- Добавлена проверка discoveredDevices.containsKey(remoteId)
- Устройство не запрашивается повторно если уже известно
- Desktop находит телефон один раз, не обновляет UI каждую секунду
- Убран авто-пересканирование каждые 5 сек (вызывало hard reload)
- TcpDiscovery уже отправляет broadcast каждую 1 секунду
- Устройства обновляются автоматически без перезапуска discovery
- Кнопка 'Обновить' делает полный перезапуск только по нажатию
1. Авто-обновление:
- Discovery пересканируется каждые 5 секунд автоматически
- Устройства снова появляются после потери связи
2. Foreground service (Android):
- AeroTossService: уведомление + wake lock
- Разрешения: FOREGROUND_SERVICE, POST_NOTIFICATIONS, WAKE_LOCK
- Приложение работает в фоне, передача не останавливается
3. Кнопка отмены:
- HomeScreen: кнопка 'Отменить отправку' при передаче
- SendScreen: кнопка 'Отменить отправку' с отменой передачи
4. Разрешения:
- Запрос POST_NOTIFICATIONS при первом запуске (Android 13+)
- Запрос ACCESS_FINE_LOCATION
- 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
- UDP socket теперь слушает на порту 9999 (а не на случайном)
- Оба устройства отправляют и слушают broadcast на одном порту
- findNetworkAddress() определяет приватный IP автоматически
- hostAddress возвращает реальный IP而非127.0.0.1
- handleDiscoveryRequest теперь возвращает реальный IP через findNetworkAddress()
- findNetworkAddress() ищет приватный IPv4 адрес на не-loopback интерфейсе
- IP адрес теперь корректно отображается на обоих устройствах
- Заменён mDNS на TCP discovery (надёжнее, работает через NAT)
- Desktop: TCP сервер на порту 9998 + UDP broadcast на порту 9999
- Phone: TCP клиент подключается к desktop для получения инфы
- Protocol: DISCOVER → JSON response (deviceId, name, type, port, host)
- DeviceCard: отображает IP адрес и порт под именем
- Убрана зависимость от mDNS (может быть добавлена позже как fallback)
- Android NSD: строгая проверка serviceType (точное совпадение)
- Android NSD: проверка TXT record на app=aerotoss для идентификации
- Чужие устройства (TV, мониторы) больше не показываются
- Desktop TXT record: добавлен app=aerotoss для идентификации
- Исправлен type=phone → type из TXT record
- Добавлен UDP broadcast discovery (порт 9999) как fallback
- Desktop отправляет broadcast каждые 3 секунды
- Android использует mDNS + UDP broadcast + Wi-Fi Direct
- mDNS: добавлена A-запись для разрешения IP
- Имена: Desktop=user@host, Android=manufacturer model
- TXT record: id, name, port, type для определения устройства
- Автоперерегистрация mDNS каждые 30 секунд
- mDNS: отправка query каждые 5 секунд
- Desktop отправляет mDNS-запросы каждые 5 секунд для обнаружения
- При обнаружении читается TXT record: name, id, type, port
- Тип устройства определяется из TXT (phone/desktop)
- Android NSD: setAttribute для TXT записей (id, name, port, type)
- Имена: Desktop = user@hostname, Android = manufacturer model
- Исправлен тип устройства: DESKTOP → PHONE/DESKTOP из TXT
- Desktop автоматически находит все доступные сетевые интерфейсы
- Приоритет: приватные IP (192.168.x.x, 10.x.x.x) выше публичных
- Фильтрация: loopback, vmnet, veth, utun, awdl исключены
- Если интерфейс не работает — пробует следующий
- Автоперерегистрация сервиса каждые 30 секунд
- Логирование: какой интерфейс выбран, какой порт зарегистрирован
- Добавлена кнопка 'Обновить' (rescan) на главный экран
- Исправлен сервисный тип: _aerotoss._tcp. (без .local.) для совместимости
- JMDNS слушает оба варианта типа: _aerotoss._tcp. и _aerotoss._tcp
- Android NSD: гибкое сопоставление типа сервиса (contains aerotoss)
- Добавлено логирование в JmdnsDiscovery для отладки
- AeroTossManager.rescan() без параметров (берёт порт из transfer)
Безопасность:
- Сервер bind на loopback (127.0.0.1) вместо 0.0.0.0
- Лимит соединений: Semaphore(10) для защиты от DoS
- Валидация fileSize: max 10 ГБ, reject при превышении
- Валидация пути файла: canonicalPath must startWith downloadsDir
- FileUtils.resolveUniqueFile: max counter 1000
- FileUtils.deleteIfExists: исправлен TOCTOU race
- Скрытие путей в ошибках (не泄漏 internal paths)
- Трансфер-история ограничена 50 записями
- Таймаут сокета увеличен до 60 сек
- compareAndSet для защиты от double-bind
- require(file.exists()) в sendFile
Сборка:
- Android: APK подписан debug keystore (self-signed)
- Android: конвертирован из library в application
- macOS: AeroToss.app (113 МБ) + JAR (28 МБ)
- LICENSE: GPLv3
Тесты: все 71+ проходят