feat: introduce Desktop target and expand Kotlin Multiplatform (KMP) architecture#4761
Merged
jamesarich merged 13 commits intomainfrom Mar 12, 2026
Merged
feat: introduce Desktop target and expand Kotlin Multiplatform (KMP) architecture#4761jamesarich merged 13 commits intomainfrom
jamesarich merged 13 commits intomainfrom
Conversation
…architecture This commit marks a major milestone by introducing the `:desktop` module, the first full non-Android target, and significantly expanding the KMP shared module graph. Core business logic, ViewModels, and UI components have been migrated to `commonMain` to support JVM targets, facilitating platform parity and code reuse. Key changes include: - **Desktop Module Implementation:** - Created the `:desktop` module using Compose for Desktop and Navigation 3. - Implemented a `NavigationRail` shell with adaptive list-detail layouts for Nodes and Messaging using Material 3 Adaptive. - Developed `DesktopRadioInterfaceService` using a shared `TcpTransport` with auto-reconnect and backoff logic. - Added `DesktopMeshServiceController` to manage the handshaking and message-processing lifecycle in-process. - **Feature & ViewModel Refactoring:** - Extracted `:feature:connections` from the `app` module to share device discovery and connection UI. - Moved core ViewModels (`MessageViewModel`, `NodeListViewModel`, `SettingsViewModel`, etc.) to `commonMain` and annotated them with `@KoinViewModel`. - Introduced `BaseUIViewModel` to share application-level state (alerts, unread counts, firmware checks) across platforms. - Migrated feature screens (Metrics, Administration, About, Filter Settings) and components (Message bubbles, Node items) to `commonMain`. - **Core Infrastructure & Transport:** - Replaced the Android-specific `IRadioInterface` with a platform-agnostic `RadioTransport` interface. - Introduced `StreamFrameCodec` and `TcpTransport` in `core:network` to share framing and socket logic between Android and JVM. - Added `DirectRadioControllerImpl` for in-process service interaction on Desktop and future targets. - Abstracted platform-specific capabilities (NFC, Barcode decoding, Map providers) behind `CompositionLocal` providers. - **Shared Testing Framework:** - Created the `:core:testing` module to provide centralized test doubles (`FakeNodeRepository`, `FakeRadioController`, `FakePacketRepository`) for use in `commonTest` across all modules. - **Utilities & Maintenance:** - Replaced platform-dependent utilities (`Base64`, `NumberFormatter`, `UrlUtils`) with pure Kotlin implementations in `core:common`. - Introduced `CommonUri` and `CommonParcelable` abstractions to handle platform-specific URI and serialization logic. - Added support for runtime locale switching via `UiPreferencesDataSource`. - Updated build logic and CI workflows to include JVM smoke compilation and Desktop verification. Specific changes: - Deleted Android-specific ViewModel wrappers in the `app` module. - Relocated and refactored dozens of UI components to align with KMP source set structures. - Updated `AGENTS.md` and `README.md` to reflect the multi-target architecture and development guidelines. Signed-off-by: James Rich <[email protected]>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4761 +/- ##
==========================================
+ Coverage 10.77% 12.17% +1.39%
==========================================
Files 517 531 +14
Lines 16067 17726 +1659
Branches 2586 2647 +61
==========================================
+ Hits 1732 2158 +426
- Misses 14040 15255 +1215
- Partials 295 313 +18 ☔ View full report in Codecov by Sentry. |
…tings
This commit introduces a centralized mechanism for resolving Android product flavors within the build logic and refactors the settings feature to support locale configuration. It also improves test stability and resource handling in the connections feature.
Specific changes include:
- **Build Logic**:
- Introduced `FlavorResolution.kt` to provide a unified `configureAndroidMarketplaceFallback` strategy, allowing configurations to automatically infer the correct marketplace flavor (e.g., Google or F-Droid) based on the configuration name.
- Applied the new flavor resolution logic to `KmpLibraryConventionPlugin`.
- Removed redundant manual attribute configuration from `feature/settings/build.gradle.kts`.
- **Settings Feature**:
- Updated `AndroidSettingsViewModel` to include `SetLocaleUseCase`, enabling locale management within the application settings.
- Added `@Suppress("LongParameterList")` to the ViewModel to accommodate necessary dependency growth.
- **Connections Feature**:
- Refactored `ScannerViewModelTest` to use `mockk` for `RadioController` instead of a fake implementation and replaced the mock `GetDiscoveredDevicesUseCase` with an anonymous object for better reliability.
- Improved `CommonGetDiscoveredDevicesUseCase` by adding a safety `runCatching` block when fetching the "demo_mode" string resource to prevent crashes during initialization.
- Added `kotlin("test")` dependency to `commonTest` in `feature/connections`.
Signed-off-by: James Rich <[email protected]>
This commit enables Gradle Build Scans for the core publication, dependency submission, and reusable check workflows to improve build observability and debugging. Specific changes include: - **`publish-core.yml`**: Configured `gradle/actions/setup-gradle` to enable Build Scan publishing and accepted the terms of use. - **`reusable-check.yml`**: Added the `--scan` flag to Gradle execution commands for code style analysis, unit tests, and KMP JVM compilation. - **`dependency-submission.yml`**: Updated the `build-scan-terms-of-use-url` to the current Gradle terms of service URL. Signed-off-by: James Rich <[email protected]>
…dules
This commit performs a widespread refactoring to improve code conciseness and leverage Kotlin's null safety more effectively. It primarily focuses on removing redundant null-coalescing operators (`?:`) where the underlying property is already non-nullable or has a sensible default, and simplifying logic in UI components and data managers.
Specific changes include:
- **Settings Feature**:
- Removed redundant `?: 0`, `?: false`, and `?: ""` defaults in various configuration screens (Audio, Display, Telemetry, Serial, User, Network, MQTT, etc.) where form state values are already guaranteed to be non-null.
- Updated `DeviceConfigScreen` and `DisplayConfigScreen` to suppress deprecation warnings where necessary.
- Simplified logic for interval dropdown selections by removing unnecessary null checks.
- **Node Feature**:
- Refactored `TracerouteLog`, `NeighborInfoLog`, and `NodeDetailActions` to remove redundant null checks on `long_name` and `short_name`.
- Cleaned up metric displays (Pax, Power, Signal, Device, Environment) by removing unnecessary null safety operators on timestamps and telemetry values.
- Updated `NodeStatusIcons` to explicitly set `TooltipAnchorPosition.Above`.
- Fixed a permission annotation in `AndroidPhoneLocationProvider`.
- **Core Modules**:
- **Data**: Simplified property access in `NodeManagerImpl` and `MqttManagerImpl`.
- **Model**: Removed redundant null checks in `Node`, `NodeInfo`, and `Channel` models. Updated `hasPKC` and `mismatchKey` logic for clarity.
- **UI**: Refactored `SecurityIcon`, `ProtoExtensions`, and `EditListPreference` to streamline property access and remove unnecessary null handling.
- **Database**: Cleaned up key existence checks in `NodeInfoDao` and simplified name matching logic.
- **Build Logic**:
- Removed the deprecated `android.newDsl=false` property from `gradle.properties`.
- Migrated `ExportDataUseCase` to use `kotlin.time.Instant` instead of `kotlinx.datetime.Instant`.
Signed-off-by: James Rich <[email protected]>
…gation This commit introduces a dedicated provider for the AboutLibraries JSON data to decouple resource loading from the navigation logic and suppresses linting warnings in complex configuration screens. Specific changes include: - **Infrastructure**: Introduced `AboutLibrariesJsonProvider` in the `app` module to handle loading the `aboutlibraries.json` resource from the classpath with graceful error handling and logging via Kermit. - **Navigation**: Updated `SettingsNavigation.kt` to use the new `AboutLibrariesJsonProvider` for the `AboutScreen`, removing the direct dependency on Android-specific `LocalContext` and raw resources. - **Linting**: Added `LongMethod` suppression to several configuration screens (`DisplayConfigScreen`, `DeviceConfigScreen`, `CannedMessageConfigScreen`) to allow for complex Composable structures. - **Maintenance**: Updated copyright headers for 2026 in new files. Signed-off-by: James Rich <[email protected]>
This commit refactors how test dependencies are managed across Kotlin Multiplatform (KMP) modules by introducing centralized configuration in the build logic. It also updates several unit and integration tests to align with recent API changes and improved mocking patterns.
Key changes include:
- **Build Logic & Infrastructure:**
- Introduced `configureKmpTestDependencies()` in `KotlinAndroid.kt` to automatically provide `kotlin("test")` to `commonTest` and `androidHostTest` source sets.
- Updated `KmpLibraryConventionPlugin` to apply these shared test dependencies, reducing boilerplate in individual `build.gradle.kts` files.
- Updated `AGENTS.md` documentation to reflect that test framework dependencies are now managed via the convention plugin.
- **Test Refactoring & Mocking:**
- Updated `MessageViewModelTest` and `NodeListViewModelTest` with explicit type hints and proper flow mocking for `RadioConfigRepository`, `ServiceRepository`, and other dependencies to improve type safety and test stability.
- Fixed parameter names and types in `SettingsViewModelTest` and `TestDataFactory` (e.g., adding `setLocaleUseCase` and `lastHeard`).
- Adjusted `NodeErrorHandlingTest` to match `FakeNodeRepository` behavior where a fallback node is returned instead of null.
- Renamed `nodeRepository.clear()` to `clearNodeDB()` in `MessagingIntegrationTest` to match the updated repository API.
- **Dependency Cleanup:**
- Removed manual `kotlin("test")` declarations from various `build.gradle.kts` files in `core` and `feature` modules, as they are now handled by the convention plugin.
Signed-off-by: James Rich <[email protected]>
This commit updates several Android convention plugins to use more specific extension types and adds documentation explaining the architectural rationale behind the current plugin structure. Specific changes include: - Updated `AndroidApplicationComposeConventionPlugin` to use `ApplicationExtension` instead of `CommonExtension`. - Updated `AndroidLibraryComposeConventionPlugin` to use `LibraryExtension` instead of `CommonExtension`. - Added KDoc to Compose and Flavor convention plugins to clarify why separate application and library plugins are maintained despite implementation similarities. - Cleaned up unused imports and added missing Gradle DSL imports in `KotlinAndroid.kt`. Signed-off-by: James Rich <[email protected]>
… plugin
This commit refactors how shared code between JVM and Android targets is handled across the project. It replaces manual `sourceSets` configuration with a new convention plugin that leverages Kotlin's `HierarchyTemplate` DSL to safely define a `jvmAndroid` group.
Specific changes include:
- **Build Logic**: Created `KmpJvmAndroidConventionPlugin` and associated logic in `KotlinAndroid.kt` to automate the creation of the `jvmAndroid` source set hierarchy.
- **Convention Registration**: Registered the `meshtastic.kmp.jvm.android` plugin ID in `build-logic/convention/build.gradle.kts`.
- **Module Migration**: Applied the new plugin and removed boilerplate `dependsOn` configurations in `core:ui`, `core:network`, `core:common`, and `core:model`.
- **Cleanup**: Removed redundant `kotlin("test")` dependencies from various `build.gradle.kts` files as they are now handled by the base KMP library convention.
- **Documentation**: Updated `AGENTS.md` to instruct developers to use the new convention plugin instead of hand-wiring source set edges.
Signed-off-by: James Rich <[email protected]>
This commit updates the static mock path in `MeshConnectionManagerImplTest` to reflect the relocation of the `getString` utility. Specific changes: - Updated `mockkStatic` and `unmockkStatic` calls to reference `org.meshtastic.core.resources.GetStringKt` instead of `org.meshtastic.core.resources.ContextExtKt`. Signed-off-by: James Rich <[email protected]>
This commit removes Android-specific logging mocks from the `MeshDataHandlerTest` setup. This cleanup ensures the `commonTest` suite remains platform-agnostic and avoids unnecessary dependencies on the Android framework. Specific changes include: - Removed `mockkStatic(android.util.Log::class)` and the associated stubs for `Log.d`, `Log.i`, `Log.w`, and `Log.e` in the `setUp` method. - Removed the unused `io.mockk.mockkStatic` import. Signed-off-by: James Rich <[email protected]>
…ViewModel
This commit updates several integration and unit tests in the `firmware` and `map` features to align with recent ViewModel API changes and dependency updates.
Specific changes include:
- **Firmware Feature Tests:**
- Updated `FirmwareUpdateViewModel` instantiation in tests to include missing mocked dependencies: `FirmwareUpdateManager`, `FirmwareUsbManager`, and `FirmwareFileHandler`.
- Migrated test assertions from `viewModel.updateState` to the renamed `viewModel.state`.
- Refactored `myNodeInfo` test checks to access the value directly from `nodeRepository` instead of the ViewModel.
- Simplified mock configurations by removing redundant `every { ... } returns emptyFlow()` calls on relaxed mocks.
- **Map Feature Tests:**
- Cleaned up `BaseMapViewModelTest` and `MapFeatureIntegrationTest` by removing unused `emptyFlow` imports and simplifying `packetRepository` mocking logic.
Signed-off-by: James Rich <[email protected]>
This commit enhances the test coverage and reliability by providing more realistic mock data for `BaseMapViewModel`, `FirmwareUpdateViewModel`, and related integration tests. Previously, many mocks were using purely relaxed behavior without defined flows, which could lead to unexpected nullability issues or silent failures during testing.
Specific changes include:
- **Map Feature Tests**:
- Defined default `MutableStateFlow` values for `MapPrefs` properties including `showOnlyFavorites`, `showWaypointsOnMap`, `showPrecisionCircleOnMap`, and filters.
- Mocked `packetRepository.getWaypoints()` to return an empty flow instead of a default null/relaxed value.
- **Firmware Feature Tests**:
- Initialized `nodeRepository` with concrete `MyNodeInfo` and `Node` mocks, including hardware model (`TBEAM`), firmware version (`2.5.0`), and node numbers.
- Configured `radioPrefs`, `firmwareReleaseRepository`, and `firmwareUpdateManager` with appropriate flows (e.g., `devAddr`, `stableRelease`, `dfuProgressFlow`).
- Added successful `Result` returns for `deviceHardwareRepository` and dismissed status for `bootloaderWarningDataSource`.
- **Test Logic**:
- Updated `testMyNodeInfoAccessible` in `FirmwareUpdateViewModelTest` to reflect that `myNodeInfo` is now properly initialized in the test setup.
Signed-off-by: James Rich <[email protected]>
Signed-off-by: James Rich <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This commit marks a major milestone by introducing the
:desktopmodule, the first full non-Android target, and significantly expanding the KMP shared module graph. Core business logic, ViewModels, and UI components have been migrated tocommonMainto support JVM targets, facilitating platform parity and code reuse.Key changes include:
Desktop Module Implementation:
:desktopmodule using Compose for Desktop and Navigation 3.NavigationRailshell with adaptive list-detail layouts for Nodes and Messaging using Material 3 Adaptive.DesktopRadioInterfaceServiceusing a sharedTcpTransportwith auto-reconnect and backoff logic.DesktopMeshServiceControllerto manage the handshaking and message-processing lifecycle in-process.Feature & ViewModel Refactoring:
:feature:connectionsfrom theappmodule to share device discovery and connection UI.MessageViewModel,NodeListViewModel,SettingsViewModel, etc.) tocommonMainand annotated them with@KoinViewModel.BaseUIViewModelto share application-level state (alerts, unread counts, firmware checks) across platforms.commonMain.Core Infrastructure & Transport:
IRadioInterfacewith a platform-agnosticRadioTransportinterface.StreamFrameCodecandTcpTransportincore:networkto share framing and socket logic between Android and JVM.DirectRadioControllerImplfor in-process service interaction on Desktop and future targets.CompositionLocalproviders.Shared Testing Framework:
:core:testingmodule to provide centralized test doubles (FakeNodeRepository,FakeRadioController,FakePacketRepository) for use incommonTestacross all modules.Utilities & Maintenance:
Base64,NumberFormatter,UrlUtils) with pure Kotlin implementations incore:common.CommonUriandCommonParcelableabstractions to handle platform-specific URI and serialization logic.UiPreferencesDataSource.Specific changes:
appmodule.AGENTS.mdandREADME.mdto reflect the multi-target architecture and development guidelines.