mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: Implement iOS support and unify Compose Multiplatform infrastructure (#4876)
This commit is contained in:
parent
f04924ded5
commit
d136b162a4
170 changed files with 2208 additions and 2432 deletions
|
|
@ -27,6 +27,7 @@ Version catalog aliases split cleanly by fork provenance. **Use the right prefix
|
|||
|---|---|---|
|
||||
| `jetbrains-lifecycle-*` | `org.jetbrains.androidx.lifecycle:*` | `commonMain`, `androidMain` |
|
||||
| `jetbrains-navigation3-*` | `org.jetbrains.androidx.navigation3:*` | `commonMain`, `androidMain` |
|
||||
| `jetbrains-navigationevent-*` | `org.jetbrains.androidx.navigationevent:*` | `commonMain`, `androidMain` |
|
||||
| `jetbrains-compose-material3-adaptive-*` | `org.jetbrains.compose.material3.adaptive:*` | `commonMain`, `androidMain` |
|
||||
| `androidx-lifecycle-process` | `androidx.lifecycle:lifecycle-process` | `androidMain` only — `ProcessLifecycleOwner` |
|
||||
| `androidx-lifecycle-runtime-ktx` | `androidx.lifecycle:lifecycle-runtime-ktx` | `androidMain` only |
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ This document captures discoverable patterns that are already used in the reposi
|
|||
- Keep shared dialogs/components in `core:ui` where possible.
|
||||
- Put localizable UI strings in Compose Multiplatform resources: `core/resources/src/commonMain/composeResources/values/strings.xml`.
|
||||
- Use `stringResource(Res.string.key)` from shared resources in feature screens.
|
||||
- Example usage: `feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt`.
|
||||
- When retrieving strings in non-composable Coroutines, Managers, or ViewModels, use `getStringSuspend()`. Never use the blocking `getString()` inside a coroutine as it will crash iOS and freeze the UI thread.
|
||||
- Example usage: `feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt`.
|
||||
|
||||
## 5) Platform abstraction in shared UI
|
||||
|
||||
|
|
|
|||
|
|
@ -33,17 +33,18 @@ Version note: align guidance with repository-pinned versions in `gradle/libs.ver
|
|||
- Do keep route definitions in `core:navigation` and use typed route objects.
|
||||
- Don't mutate back navigation with custom stacks disconnected from app backstack.
|
||||
- Do mutate `NavBackStack<NavKey>` with `add(...)` and `removeLastOrNull()`.
|
||||
- Don't use Android's `androidx.activity.compose.BackHandler` or custom `PredictiveBackHandler` in multiplatform UI.
|
||||
- Do use the official KMP `NavigationBackHandler` from `androidx.navigationevent:navigationevent-compose` for back gestures.
|
||||
|
||||
### Current code anchors (Navigation 3)
|
||||
|
||||
- Typed routes: `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt`
|
||||
- Shared saved-state config: `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/NavigationConfig.kt`
|
||||
- App root backstack + `NavDisplay`: `app/src/main/kotlin/org/meshtastic/app/ui/Main.kt`
|
||||
- Graph entry provider pattern: `feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Feature-level Navigation 3 usage: `feature/intro/src/androidMain/kotlin/org/meshtastic/feature/intro/AppIntroductionScreen.kt`
|
||||
- Shared graph entry provider pattern: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Desktop Navigation 3 shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
||||
- Desktop nav graph entries: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
|
||||
- Desktop real feature wiring: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopSettingsNavigation.kt`
|
||||
- Desktop `SavedStateConfiguration` for polymorphic NavKey serialization: `DesktopMainScreen.kt`
|
||||
- Desktop nav graph assembly: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
|
||||
|
||||
|
||||
## Quick pre-PR checks for DI/navigation edits
|
||||
|
||||
|
|
|
|||
|
|
@ -43,16 +43,19 @@ Reference examples:
|
|||
|
||||
1. Define/extend route keys in `core:navigation`.
|
||||
2. Implement feature entry/content using Navigation 3 types (`NavKey`, `NavBackStack`, `EntryProviderScope`).
|
||||
3. Add graph entries under the relevant feature module's `navigation` package (e.g., `feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation`).
|
||||
4. Use backstack mutation (`add`, `removeLastOrNull`) instead of introducing controller-coupled APIs.
|
||||
5. Verify deep-link behavior if route is externally reachable.
|
||||
3. Add graph entries under the relevant feature module's `navigation` package (e.g., `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation`).
|
||||
4. If the entry content depends on platform-specific UI (e.g. Activity context or specific desktop wrappers), use `expect`/`actual` declarations for the content composables.
|
||||
5. Use backstack mutation (`add`, `removeLastOrNull`) instead of introducing controller-coupled APIs.
|
||||
6. Verify deep-link behavior if route is externally reachable.
|
||||
|
||||
Reference examples:
|
||||
- App graph wiring: `feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Shared graph wiring: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Android specific content: `feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt`
|
||||
- Desktop specific content: `feature/settings/src/jvmMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt`
|
||||
- Feature intro graph pattern: `feature/intro/src/androidMain/kotlin/org/meshtastic/feature/intro/IntroNavGraph.kt`
|
||||
- Desktop nav shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
||||
- Desktop nav graph entries: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
|
||||
- Desktop feature wiring: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopSettingsNavigation.kt`
|
||||
- Desktop nav graph assembly: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
|
||||
|
||||
|
||||
## Playbook E: Add flavor/platform-specific UI implementation
|
||||
|
||||
|
|
@ -82,8 +85,8 @@ Reference examples:
|
|||
- Desktop DI: `desktop/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt`
|
||||
- Desktop Navigation 3 shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
||||
- Desktop nav graph entries: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
|
||||
- Desktop real feature wiring: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopSettingsNavigation.kt`
|
||||
- Desktop-specific screen: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/settings/DesktopSettingsScreen.kt`
|
||||
- Desktop shared feature wiring: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Desktop-specific screen: `feature/settings/src/jvmMain/kotlin/org/meshtastic/feature/settings/DesktopSettingsScreen.kt`
|
||||
- Roadmap: `docs/roadmap.md`
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ Ordered by impact × effort:
|
|||
|
||||
| Priority | Extraction | Impact | Effort | Enables |
|
||||
|---:|---|---|---|---|
|
||||
| 1 | `java.*` purge from `commonMain` (B1, B2) | High | Low | iOS target declaration |
|
||||
| 1 | ~~`java.*` purge from `commonMain` (B1, B2)~~ | High | Low | ~~iOS target declaration~~ ✅ Done |
|
||||
| 2 | Radio transport interfaces to `core:repository` (A2) | High | Medium | Transport unification |
|
||||
| 3 | `core:testing` shared fixtures (D2) | Medium | Low | Feature commonTest |
|
||||
| 4 | Feature `commonTest` (D1) | Medium | Medium | KMP test coverage |
|
||||
|
|
@ -194,7 +194,7 @@ Ordered by impact × effort:
|
|||
| 7 | ~~Desktop Koin auto-wiring (C1, C2)~~ | Medium | Low | ✅ Resolved 2026-03-13 |
|
||||
| 8 | MQTT KMP (B3) | Medium | High | Desktop/iOS MQTT |
|
||||
| 9 | KMP charts (B4) | Medium | High | Desktop metrics |
|
||||
| 10 | iOS target declaration | High | Low | CI purity gate |
|
||||
| 10 | ~~iOS target declaration~~ | High | Low | ~~CI purity gate~~ ✅ Done |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ Ordered by impact × effort:
|
|||
| Shared business/data logic | 8.5/10 | **9/10** | RadioTransport interface unified; all core layers shared |
|
||||
| Shared feature/UI logic | 9.5/10 | **8.5/10** | All 7 KMP features; connections unified; Vico charts in commonMain |
|
||||
| Android decoupling | 8.5/10 | **9/10** | Connections, Navigation, Services, & Widgets extracted; GMS purged; app ~40->target 20 files |
|
||||
| Multi-target readiness | 8/10 | **8/10** | Full JVM; release-ready desktop; iOS not declared |
|
||||
| Multi-target readiness | 8/10 | **9/10** | Full JVM; release-ready desktop; iOS simulator builds compiling successfully |
|
||||
| CI confidence | 8.5/10 | **9/10** | 25 modules validated; feature:connections + desktop in CI; native release installers |
|
||||
| DI portability | 7/10 | **8/10** | Koin annotations in commonMain; supportedDeviceTypes injected per platform |
|
||||
| Test maturity | — | **8/10** | 131 commonTest + 89 platform-specific = 219 tests across all 7 features; core:testing established |
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
# Navigation 3 Parity Strategy (Android + Desktop)
|
||||
|
||||
**Date:** 2026-03-11
|
||||
**Status:** Active
|
||||
**Status:** Implemented (2026-03-21)
|
||||
**Scope:** `app` and `desktop` navigation structure using shared `core:navigation` routes
|
||||
|
||||
## Context
|
||||
|
|
@ -27,13 +27,14 @@ Both modules still define separate graph-builder files (`app/navigation/*.kt`, `
|
|||
- Both shells iterate `TopLevelDestination.entries` from `core:navigation/commonMain`.
|
||||
- Shared icon mapping lives in `core:ui` (`TopLevelDestinationExt.icon`).
|
||||
- Parity tests exist in both `core:navigation/commonTest` (`NavigationParityTest`) and `desktop/test` (`DesktopTopLevelDestinationParityTest`).
|
||||
2. **Feature coverage differs by intent and by implementation.**
|
||||
- Desktop intentionally uses placeholders for map and several node/message detail flows.
|
||||
- Android wires real implementations for map, message/share flows, and more node detail paths.
|
||||
3. **Saved-state route registration is desktop-only and manual.**
|
||||
- `DesktopMainScreen.kt` maintains a large `SavedStateConfiguration` serializer list that must stay in sync with `Routes.kt` and desktop graph entries.
|
||||
4. **Route keys are shared; graph registration is per-platform.**
|
||||
- This is the expected state — platform shells wire entries differently while consuming the same route types.
|
||||
2. **Feature coverage is unified via `commonMain` feature graphs.**
|
||||
- The `settingsGraph`, `nodesGraph`, `contactsGraph`, `connectionsGraph`, `firmwareGraph`, and `mapGraph` are now fully shared and exported from their respective feature modules' `commonMain` source sets.
|
||||
- Desktop acts as a thin shell, delegating directly to these shared graphs.
|
||||
3. **Saved-state route registration is fully shared.**
|
||||
- `MeshtasticNavSavedStateConfig` in `core:navigation/commonMain` maintains the unified `SavedStateConfiguration` serializer list.
|
||||
- Both Android and Desktop reference this shared config when instantiating `rememberNavBackStack`.
|
||||
4. **Predictive back handling is KMP native.**
|
||||
- Custom `PredictiveBackHandler` wrapper was removed in favor of Jetpack's official KMP `NavigationBackHandler` from `androidx.navigationevent:navigationevent-compose`.
|
||||
|
||||
## Alpha04 Changelog Impact Check (2026-03-13)
|
||||
|
||||
|
|
@ -147,9 +148,11 @@ Adopt a **hybrid parity model**:
|
|||
## Source Anchors
|
||||
|
||||
- Shared routes: `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt`
|
||||
- Shared saved-state config: `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/NavigationConfig.kt`
|
||||
- Android shell: `app/src/main/kotlin/org/meshtastic/app/ui/Main.kt`
|
||||
- Android graph registrations: `feature/*/src/androidMain/kotlin/org/meshtastic/feature/*/navigation/`
|
||||
- Shared graph registrations: `feature/*/src/commonMain/kotlin/org/meshtastic/feature/*/navigation/`
|
||||
- Platform graph content: `feature/*/src/{androidMain,jvmMain}/kotlin/org/meshtastic/feature/*/navigation/`
|
||||
- Desktop shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
||||
- Desktop graph registrations: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/`
|
||||
- Desktop graph assembly: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ Modules that share JVM-specific code between Android and desktop now standardize
|
|||
|
||||
| Module | UI in commonMain? | Desktop wired? |
|
||||
|---|:---:|:---:|
|
||||
| `feature:settings` | ✅ | ✅ ~35 real screens; shared `ChannelScreen` & `ViewModel` |
|
||||
| `feature:node` | ✅ | ✅ Adaptive list-detail; shared `NodeContextMenu` |
|
||||
| `feature:messaging` | ✅ | ✅ Adaptive contacts + messages; 17 shared files in commonMain (ViewModels, MessageBubble, MessageItem, QuickChat, Reactions, DeliveryInfo, actions, events) |
|
||||
| `feature:settings` | ✅ | ✅ ~35 real screens; fully shared `settingsGraph` and UI |
|
||||
| `feature:node` | ✅ | ✅ Adaptive list-detail; fully shared `nodesGraph`, `PositionLogScreen`, and `NodeContextMenu` |
|
||||
| `feature:messaging` | ✅ | ✅ Adaptive contacts + messages; fully shared `contactsGraph`, `MessageScreen`, `ContactsScreen`, and `MessageListPaged` |
|
||||
| `feature:connections` | ✅ | ✅ Shared `ConnectionsScreen` with dynamic transport detection |
|
||||
| `feature:intro` | ✅ | — |
|
||||
| `feature:map` | ✅ | Placeholder; shared `NodeMapViewModel` |
|
||||
|
|
@ -63,7 +63,7 @@ Working Compose Desktop application with:
|
|||
- **Desktop language picker** backed by `UiPreferencesDataSource.locale`, with immediate Compose Multiplatform resource updates
|
||||
- **Navigation-preserving locale switching** via `Main.kt` `staticCompositionLocalOf` recomposition instead of recreating the Nav3 backstack
|
||||
- Node detail metrics screens (Device, Environment, Signal, Power, Pax) wired with shared KMP + Vico charts
|
||||
- 6 desktop-specific screens (Settings, Device, Position, Network, Security, ExternalNotification)
|
||||
- **Feature-driven Architecture:** Desktop navigation completely relies on feature modules via `commonMain` exported graphs (`settingsGraph`, `nodesGraph`, `contactsGraph`, etc.), reducing the desktop module to a simple host shell.
|
||||
- **Native notifications and system tray icon** wired via `DesktopNotificationManager`
|
||||
- **Native release pipeline** generating `.dmg` (macOS), `.msi` (Windows), and `.deb` (Linux) installers in CI
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ Working Compose Desktop application with:
|
|||
| Shared business/data logic | **9/10** | All core layers shared; RadioTransport interface unified |
|
||||
| Shared feature/UI logic | **8.5/10** | All 7 KMP; feature:connections unified with dynamic transport detection |
|
||||
| Android decoupling | **9/10** | No known `java.*` calls in `commonMain`; app module extraction in progress (navigation, connections, background services, and widgets extracted) |
|
||||
| Multi-target readiness | **8/10** | Full JVM; release-ready desktop; iOS not declared |
|
||||
| Multi-target readiness | **9/10** | Full JVM; release-ready desktop; iOS simulator builds compiling successfully |
|
||||
| CI confidence | **9/10** | 25 modules validated (including feature:connections); native release installers automated |
|
||||
| DI portability | **8/10** | Koin annotations in commonMain; supportedDeviceTypes injected per platform |
|
||||
| Test maturity | **9/10** | Mokkery, Turbine, and Kotest integrated; property-based testing established; broad coverage across all 8 features |
|
||||
|
|
@ -88,8 +88,8 @@ Working Compose Desktop application with:
|
|||
| Android-first structural KMP | ~100% |
|
||||
| Shared business logic | ~98% |
|
||||
| Shared feature/UI | ~95% |
|
||||
| True multi-target readiness | ~75% |
|
||||
| "Add iOS without surprises" | ~65% |
|
||||
| True multi-target readiness | ~85% |
|
||||
| "Add iOS without surprises" | ~100% |
|
||||
|
||||
## Proposed Next Steps for KMP Migration
|
||||
|
||||
|
|
@ -97,7 +97,8 @@ Based on the latest codebase investigation, the following steps are proposed to
|
|||
|
||||
1. **Wire Desktop Features:** Complete desktop UI wiring for `feature:intro` and implement a shared fallback for `feature:map` (which is currently a placeholder on desktop).
|
||||
2. **Decouple Firmware DFU:** `feature:firmware` relies on Android-only DFU libraries. Evaluate wrapping this in a shared KMP interface or extracting it into a separate plugin to allow the core `feature:firmware` module to be fully utilized on desktop/iOS.
|
||||
3. **Prepare for iOS Target:** Set up an initial skeleton Xcode project to start validating `commonMain` compilation on Kotlin/Native (iOS).
|
||||
3. **Flesh out iOS Actuals:** Complete the actual implementations for iOS UI stubs (e.g., `AboutLibrariesLoader`, `rememberOpenMap`, `SettingsMainScreen`) that were recently added to unblock iOS compilation.
|
||||
4. **Boot iOS Target:** Set up an initial skeleton Xcode project to start running the now-compiling `iosSimulatorArm64` / `iosArm64` binaries on a real simulator/device.
|
||||
|
||||
## Key Architecture Decisions
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ These items address structural gaps identified in the March 2026 architecture re
|
|||
| Add feature module `commonTest` (settings, node, messaging) | Medium | Medium | ✅ |
|
||||
| Desktop Koin `checkModules()` integration test | Medium | Low | ✅ |
|
||||
| Auto-wire Desktop ViewModels via K2 Compiler (eliminate manual wiring) | Medium | Low | ✅ |
|
||||
here| **Migrate to JetBrains Compose Multiplatform dependencies** | High | Low | ✅ |
|
||||
| **Migrate to JetBrains Compose Multiplatform dependencies** | High | Low | ✅ |
|
||||
| **iOS CI gate (compile-only validation)** | High | Medium | ✅ |
|
||||
|
||||
## Active Work
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ here| **Migrate to JetBrains Compose Multiplatform dependencies** | High | Low |
|
|||
|
||||
| Feature | Status |
|
||||
|---|---|
|
||||
| Settings | ✅ ~35 real screens (6 desktop-specific) + desktop locale picker with in-place recomposition |
|
||||
| Settings | ✅ ~35 real screens (fully shared) + desktop locale picker with in-place recomposition |
|
||||
| Node list | ✅ Adaptive list-detail with real `NodeDetailContent` |
|
||||
| Messaging | ✅ Adaptive contacts with real message view + send |
|
||||
| Connections | ✅ Unified shared UI with dynamic transport detection |
|
||||
|
|
@ -85,11 +86,11 @@ here| **Migrate to JetBrains Compose Multiplatform dependencies** | High | Low |
|
|||
- Implement a **Web Mercator Projection** helper in `feature:map/commonMain` to translate GPS coordinates to the 2D image plane.
|
||||
- Leverage the existing `BaseMapViewModel` contract.
|
||||
3. **Unify `MapViewModel`** — Collapse the remaining Google and F-Droid specific `MapViewModel` classes in the `:app` module into a single `commonMain` implementation by isolating platform-specific settings (styles, tile sources) behind a repository interface.
|
||||
4. **iOS CI gate** — add `iosArm64()`/`iosSimulatorArm64()` to convention plugins and CI (compile-only, no implementations) to ensure `commonMain` remains pure.
|
||||
4. **iOS CI gate** — ✅ **Done:** added `iosArm64()`/`iosSimulatorArm64()` to convention plugins and CI. `commonMain` successfully compiles on iOS.
|
||||
|
||||
## Medium-Term Priorities (60 days)
|
||||
|
||||
1. **iOS proof target** — Begin stubbing iOS target implementations (`NoopStubs.kt` equivalent) and setup an Xcode skeleton project.
|
||||
1. **iOS proof target** — ✅ **Done (Stubbing):** Stubbed iOS target implementations (`NoopStubs.kt` equivalent) to successfully pass compile-time checks. **Next:** Setup an Xcode skeleton project and launch the iOS app.
|
||||
2. **`core:api` contract split** — separate transport-neutral service contracts from the Android AIDL packaging to support iOS/Desktop service layers.
|
||||
3. **Decouple Firmware DFU** — `feature:firmware` relies on Android-only DFU libraries. Evaluate wrapping this in a shared KMP interface or extracting it to allow the core `feature:firmware` module to be utilized on desktop/iOS.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue