From 099aea2d81655d355b5ffdc8a7a2fac447861a09 Mon Sep 17 00:00:00 2001
From: James Rich <2199651+jamesarich@users.noreply.github.com>
Date: Tue, 14 Apr 2026 10:16:10 -0500
Subject: [PATCH] feat(desktop): add entitlements and wire
MeshConnectionManager into orchestrator (#5127)
---
.../core/service/MeshServiceOrchestrator.kt | 3 +++
.../core/service/MeshServiceOrchestratorTest.kt | 3 +++
desktop/build.gradle.kts | 5 +++++
desktop/entitlements.plist | 14 ++++++++++++++
.../main/kotlin/org/meshtastic/desktop/Main.kt | 15 +++++++--------
5 files changed, 32 insertions(+), 8 deletions(-)
create mode 100644 desktop/entitlements.plist
diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt
index e651d95ce..50e88cc3f 100644
--- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt
+++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/MeshServiceOrchestrator.kt
@@ -26,6 +26,7 @@ import org.koin.core.annotation.Named
import org.koin.core.annotation.Single
import org.meshtastic.core.common.database.DatabaseManager
import org.meshtastic.core.common.util.handledLaunch
+import org.meshtastic.core.repository.MeshConnectionManager
import org.meshtastic.core.repository.MeshMessageProcessor
import org.meshtastic.core.repository.MeshRouter
import org.meshtastic.core.repository.MeshServiceNotifications
@@ -57,6 +58,7 @@ class MeshServiceOrchestrator(
private val takMeshIntegration: TAKMeshIntegration,
private val takPrefs: TakPrefs,
private val databaseManager: DatabaseManager,
+ private val connectionManager: MeshConnectionManager,
@Named("ServiceScope") private val scope: CoroutineScope,
) {
private var serviceJob: Job? = null
@@ -87,6 +89,7 @@ class MeshServiceOrchestrator(
serviceJob = job
serviceNotifications.initChannels()
+ connectionManager.updateStatusNotification()
// Observe TAK server pref to start/stop
takJob =
diff --git a/core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt b/core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt
index 48be7dbf6..ddb7b148f 100644
--- a/core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt
+++ b/core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt
@@ -35,6 +35,7 @@ import org.meshtastic.core.model.service.ServiceAction
import org.meshtastic.core.repository.CommandSender
import org.meshtastic.core.repository.MeshActionHandler
import org.meshtastic.core.repository.MeshConfigHandler
+import org.meshtastic.core.repository.MeshConnectionManager
import org.meshtastic.core.repository.MeshMessageProcessor
import org.meshtastic.core.repository.MeshRouter
import org.meshtastic.core.repository.MeshServiceNotifications
@@ -67,6 +68,7 @@ class MeshServiceOrchestratorTest {
private val takPrefs: TakPrefs = mock(MockMode.autofill)
private val cotHandler: CoTHandler = mock(MockMode.autofill)
private val databaseManager: DatabaseManager = mock(MockMode.autofill)
+ private val connectionManager: MeshConnectionManager = mock(MockMode.autofill)
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = CoroutineScope(testDispatcher)
@@ -111,6 +113,7 @@ class MeshServiceOrchestratorTest {
takMeshIntegration = takMeshIntegration,
takPrefs = takPrefs,
databaseManager = databaseManager,
+ connectionManager = connectionManager,
scope = testScope,
)
}
diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts
index df5122a4d..fdf7cee5c 100644
--- a/desktop/build.gradle.kts
+++ b/desktop/build.gradle.kts
@@ -158,9 +158,14 @@ compose.desktop {
iconFile.set(project.file("src/main/resources/icon.icns"))
minimumSystemVersion = "12.0"
bundleID = "org.meshtastic.desktop"
+ entitlementsFile.set(project.file("entitlements.plist"))
infoPlist {
extraKeysRawXml =
"""
+ NSBluetoothAlwaysUsageDescription
+ Meshtastic uses Bluetooth to communicate with your Meshtastic radio device.
+ NSLocalNetworkUsageDescription
+ Meshtastic uses your local network to discover Meshtastic devices connected via WiFi.
NSUserNotificationAlertStyle
alert
CFBundleURLTypes
diff --git a/desktop/entitlements.plist b/desktop/entitlements.plist
new file mode 100644
index 000000000..f799a66e9
--- /dev/null
+++ b/desktop/entitlements.plist
@@ -0,0 +1,14 @@
+
+
+
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-library-validation
+
+ com.apple.security.device.bluetooth
+
+
+
diff --git a/desktop/src/main/kotlin/org/meshtastic/desktop/Main.kt b/desktop/src/main/kotlin/org/meshtastic/desktop/Main.kt
index 25a5b8ce3..8b33a3612 100644
--- a/desktop/src/main/kotlin/org/meshtastic/desktop/Main.kt
+++ b/desktop/src/main/kotlin/org/meshtastic/desktop/Main.kt
@@ -60,9 +60,7 @@ import kotlinx.coroutines.flow.first
import okio.Path.Companion.toPath
import org.jetbrains.compose.resources.decodeToSvgPainter
import org.koin.compose.koinInject
-import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.context.startKoin
-import org.koin.core.context.stopKoin
import org.meshtastic.core.common.BuildConfigProvider
import org.meshtastic.core.common.util.MeshtasticUri
import org.meshtastic.core.database.desktopDataDir
@@ -107,12 +105,13 @@ private fun svgPainterResource(path: String, density: Density): Painter = rememb
@OptIn(ExperimentalCoilApi::class)
fun main(args: Array) = application(exitProcessOnExit = false) {
- Logger.i { "Meshtastic Desktop — Starting" }
-
- remember { startKoin { modules(desktopPlatformModule(), desktopModule()) } }
- DisposableEffect(Unit) { onDispose { stopKoin() } }
-
- val uiViewModel = koinViewModel()
+ val koinApp = remember {
+ Logger.i { "Meshtastic Desktop — Starting" }
+ startKoin { modules(desktopPlatformModule(), desktopModule()) }
+ }
+ val systemLocale = remember { Locale.getDefault() }
+ val uiViewModel = remember { koinApp.koin.get() }
+ val httpClient = remember { koinApp.koin.get() }
DeepLinkHandler(args, uiViewModel)
MeshServiceLifecycle()