feat(device-initialization, core): mDNS-Discovery erweitert, Geräte- und UI-Interaktion optimiert

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-04-30 15:58:19 +02:00
parent 8ab6ab1c2a
commit 022ffccccd
10 changed files with 307 additions and 400 deletions
@@ -11,7 +11,6 @@ import androidx.compose.ui.unit.dp
import java.awt.FileDialog
import java.awt.Frame
import java.io.File
import javax.swing.JFileChooser
@Composable
actual fun MsFilePicker(
@@ -45,19 +44,26 @@ actual fun MsFilePicker(
MsButton(
onClick = {
if (directoryOnly) {
// JFileChooser ist für Verzeichnisse auf dem Desktop oft stabiler/einfacher
val chooser = JFileChooser().apply {
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
dialogTitle = label
// AWT FileDialog für nativen Look auch bei Verzeichnissen (Windows/Linux/macOS)
// unter macOS erzwingt dies die Verzeichnisauswahl. Unter Windows/Linux ist es der Standard-Dialog.
System.setProperty("apple.awt.fileDialogForDirectories", "true")
val dialog = FileDialog(null as Frame?, label, FileDialog.LOAD).apply {
selectedPath?.let {
val currentDir = File(it)
if (currentDir.exists()) currentDirectory = currentDir
if (currentDir.exists()) {
directory = currentDir.absolutePath
}
}
}
val result = chooser.showOpenDialog(null)
if (result == JFileChooser.APPROVE_OPTION) {
onFileSelected(chooser.selectedFile.absolutePath)
dialog.isVisible = true
if (dialog.directory != null && dialog.file != null) {
// Bei FileDialog.LOAD unter Windows/Linux wählt man oft eine Datei im Ordner,
// aber wir wollen den Ordner. Wir nehmen also das Verzeichnis.
onFileSelected(File(dialog.directory, dialog.file).parentFile.absolutePath)
} else if (dialog.directory != null) {
onFileSelected(dialog.directory)
}
System.setProperty("apple.awt.fileDialogForDirectories", "false")
} else {
// AWT FileDialog für nativen Look bei Dateiauswahl (wie vom User gewünscht)
val dialog = FileDialog(null as Frame?, label, FileDialog.LOAD).apply {
@@ -38,8 +38,9 @@ interface NetworkDiscoveryService {
* Registriert den eigenen Dienst, damit andere Instanzen ihn finden können.
* @param port Der Port, auf dem der lokale WebSocket-Server lauscht.
* @param preferredIp Optional eine IP-Adresse, an die der Discovery-Dienst gebunden werden soll.
* @param deviceName Der Name des Geräts, das im Netzwerk angezeigt werden soll.
*/
fun registerService(port: Int, preferredIp: String? = null)
fun registerService(port: Int, preferredIp: String? = null, deviceName: String? = null)
/**
* Gibt die Liste der aktuell entdeckten Dienste zurück (Snapshot).
@@ -63,20 +63,29 @@ class JmDnsDiscoveryService : NetworkDiscoveryService {
_discoveredServices.value = emptyList()
}
override fun registerService(port: Int, preferredIp: String?) {
override fun registerService(port: Int, preferredIp: String?, deviceName: String?) {
if (jmdns == null) {
val addr = preferredIp?.let { InetAddress.getByName(it) } ?: InetAddress.getLocalHost()
println("[Discovery] Registriere Dienst gebunden an: $addr")
jmdns = JmDNS.create(addr)
}
// Wir nutzen den übergebenen Namen, den vom System gesetzten oder einen sprechenden Default
val name = deviceName ?: System.getProperty("meldestelle.device.name") ?: "Meldestelle-${System.getProperty("user.name")}"
val serviceInfo = ServiceInfo.create(
SERVICE_TYPE,
"Meldestelle-${System.getProperty("user.name")}",
name,
port,
"Offline-First Sync Node"
0, 0, // weight, priority
mapOf(
"version" to "1.0.0",
"type" to "master",
"nodeId" to name
)
)
jmdns?.registerService(serviceInfo)
println("[Discovery] Eigenen Dienst registriert auf Port $port")
println("[Discovery] Eigenen Dienst '$name' registriert auf Port $port")
}
override fun getDiscoveredServices(): List<DiscoveredService> {
@@ -19,6 +19,6 @@ class NoOpDiscoveryService : NetworkDiscoveryService {
override fun startDiscovery(preferredIp: String?) {}
override fun stopDiscovery() {}
override fun registerService(port: Int, preferredIp: String?) {}
override fun registerService(port: Int, preferredIp: String?, deviceName: String?) {}
override fun getDiscoveredServices(): List<DiscoveredService> = emptyList()
}