10 KiB
Meshtastic Android - Agent Guide
This file serves as a comprehensive guide for AI agents and developers working on the Meshtastic-Android codebase. Use this as your primary reference for understanding the architecture, conventions, and strict rules of this project.
For execution-focused recipes, see docs/agent-playbooks/README.md.
1. Project Vision
We are incrementally migrating Meshtastic-Android to a Kotlin Multiplatform (KMP) architecture. The goal is to decouple business logic from the Android framework, enabling future expansion to iOS and other platforms while maintaining a high-performance native Android experience.
2. Codebase Map
| Directory | Description |
|---|---|
app/ |
Main application module. Contains MainActivity, Koin DI modules, and app-level logic. Uses package org.meshtastic.app. |
build-logic/ |
Convention plugins for shared build configuration (e.g., meshtastic.kmp.library, meshtastic.koin). |
config/ |
Detekt static analysis rules (config/detekt/detekt.yml) and Spotless formatting config (config/spotless/.editorconfig). |
docs/ |
Architecture docs and agent playbooks. See docs/agent-playbooks/README.md for version baseline and task recipes. |
core/model |
Domain models and common data structures. |
core:proto |
Protobuf definitions (Git submodule). |
core:common |
Low-level utilities, I/O abstractions (Okio), and common types. |
core:database |
Room KMP database implementation. |
core:datastore |
Multiplatform DataStore for preferences. |
core:repository |
High-level domain interfaces (e.g., NodeRepository, LocationRepository). |
core:domain |
Pure KMP business logic and UseCases. |
core:data |
Core manager implementations and data orchestration. |
core:network |
KMP networking layer using Ktor, MQTT abstractions, and shared transport (StreamFrameCodec in commonMain, TcpTransport in jvmAndroidMain). |
core:di |
Common DI qualifiers and dispatchers. |
core:navigation |
Shared navigation keys/routes for Navigation 3. |
core:ui |
Shared Compose UI components (EmptyDetailPlaceholder, MainAppBar, dialogs, preferences) and platform abstractions, including jvmAndroidMain bridges for shared JVM/Android actuals. |
core:service |
KMP service layer; Android bindings stay in androidMain. |
core:api |
Public AIDL/API integration module for external clients. |
core:prefs |
KMP preferences layer built on DataStore abstractions. |
core:barcode |
Barcode scanning (Android-only). Shared UI in main/; only the decoder (createBarcodeAnalyzer) differs per flavor (ML Kit / ZXing). Shared contract in core:ui. |
core:nfc |
NFC abstractions (KMP). Android NFC hardware implementation in androidMain; shared contract via LocalNfcScannerProvider in core:ui. |
core/ble/ |
Bluetooth Low Energy stack using Nordic libraries. |
core/resources/ |
Centralized string and image resources (Compose Multiplatform). |
core/testing/ |
Shared test doubles, fakes, and utilities for commonTest across all KMP modules. Lightweight with minimal dependencies (only core:model, core:repository, + test libs). Keeps module dependency graph clean by centralizing test consolidation. See core/testing/README.md. |
feature/ |
Feature modules (e.g., settings, map, messaging, node, intro, connections). All are KMP with jvm() target. |
feature/connections |
Connections UI — device discovery, BLE/TCP/USB scanning, shared composables in commonMain; Android BLE bonding/NSD/USB in androidMain. |
feature/firmware |
Firmware update flow (KMP module with Android DFU in androidMain). |
desktop/ |
Compose Desktop application — first non-Android KMP target. Nav 3 shell, full Koin DI graph, TCP transport with want_config handshake, adaptive list-detail screens for nodes/messaging, ~35 real settings screens, connections UI. See docs/kmp-status.md. |
mesh_service_example/ |
Sample app showing core:api service integration. |
3. Development Guidelines
A. UI Development (Jetpack Compose)
- Material 3: The app uses Material 3.
- Strings:
- Rule: MUST use the Compose Multiplatform Resource library in
core:resources. - Location:
core/resources/src/commonMain/composeResources/values/strings.xml.
- Rule: MUST use the Compose Multiplatform Resource library in
- Dialogs: Use centralized components in
core:ui. - Platform/Flavor UI: Inject platform-specific behavior (e.g., map providers) via
CompositionLocalfromapp. Seecore/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.ktfor the contract pattern andapp/src/main/kotlin/org/meshtastic/app/MainActivity.ktfor provider wiring.
B. Logic & Data Layer
- KMP Focus: All business logic must reside in
commonMainof the respectivecoremodule. - Platform purity: Never import
java.*orandroid.*incommonMain. Use KMP alternatives:java.util.Locale→ Kotlinuppercase()/lowercase()(locale-independent for ASCII) orexpect/actual.java.util.concurrent.ConcurrentHashMap→atomicfuorMutex-guardedmutableMapOf().java.util.concurrent.locks.*→kotlinx.coroutines.sync.Mutex.java.io.*→ Okio (BufferedSource/BufferedSink).
- I/O: Use Okio (
BufferedSource/BufferedSink) for stream operations. Never usejava.ioincommonMain. - Concurrency: Use Kotlin Coroutines and Flow.
- Thread-Safety: Use
atomicfuandkotlinx.collections.immutablefor shared state incommonMain. Avoidsynchronizedor JVM-specific atomics. - Dependency Injection:
- Use Koin Annotations with the K2 compiler plugin (0.4.0+).
- Keep root graph assembly in
app(module inclusion inAppKoinModuleand startup wiring inMeshUtilApplication). - Use
@Module,@ComponentScan, and@KoinViewModelannotations directly incommonMainshared modules. - Note on Koin 0.4.0 compile safety: Koin's A1 (per-module) validation is globally disabled in
build-logic. Because Meshtastic employs Clean Architecture dependency inversion (interfaces incore:repository, implementations incore:data), enforcing A1 resolution per-module fails. Validation occurs at the full-graph (A3) level instead.
- ViewModels: Follow the MVI/UDF pattern. Use the multiplatform
androidx.lifecycle.ViewModelincommonMainto maintain a single source of truth for UI state, relying heavily onStateFlow. - BLE: All Bluetooth communication must route through
core:bleusing Nordic Semiconductor's Android Common Libraries and Kotlin Coroutines/Flows. Never use legacy Android Bluetooth callbacks directly. - Dependencies: Check
gradle/libs.versions.tomlbefore assuming a library is available. New dependencies MUST be added to the version catalog, not directly to abuild.gradle.ktsfile. - Shared JVM + Android code: If a KMP module needs a
jvmAndroidMainsource set for code shared between desktop JVM and Android, apply themeshtastic.kmp.jvm.androidconvention plugin. Do not hand-wiresourceSets.dependsOn(...)edges in modulebuild.gradle.ktsfiles—the convention uses Kotlin's hierarchy template API and avoids default hierarchy warnings. - Room KMP: Always use
factory = { MeshtasticDatabaseConstructor.initialize() }inRoom.databaseBuilderandinMemoryDatabaseBuilder. DAOs and Entities reside incommonMain. - Testing: Write ViewModel and business logic tests in
commonTest(nottest/Robolectric) so every target runs them. Usecore:testingshared fakes when available. Test framework dependencies (kotlin("test")for bothcommonTestandandroidHostTestsource sets) are automatically provided by themeshtastic.kmp.libraryconvention plugin—no need to add them manually to individual modulebuild.gradle.ktsfiles. Seebuild-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt::configureKmpTestDependencies()for details.
C. Namespacing
- Standard: Use the
org.meshtastic.*namespace for all code. - Legacy: Maintain the
com.geeksville.meshApplication ID and specific intent strings for backward compatibility.
4. Execution Protocol
A. Build and Verify
Prerequisite: JDK 17 is required. Copy secrets.defaults.properties to local.properties before building.
- Clean:
./gradlew clean - Format:
./gradlew spotlessCheckthen./gradlew spotlessApply - Lint:
./gradlew detekt - Build + Unit Tests:
./gradlew assembleDebug test(CI also runstestDebugUnitTest) - Flavor/CI Parity (when relevant):
./gradlew lintFdroidDebug lintGoogleDebug testFdroidDebug testGoogleDebug - Desktop (when touched):
./gradlew :desktop:test :desktop:run
B. Documentation Sync
- If you change architecture, module boundaries, target declarations, CI tasks, validation commands, or agent workflow rules, update the corresponding docs in the same slice.
- KMP status:
docs/kmp-status.md. Roadmap:docs/roadmap.md. Decisions:docs/decisions/. Architecture review:docs/decisions/architecture-review-2026-03.md. - At minimum, review and update the relevant source of truth among
AGENTS.md,.github/copilot-instructions.md,GEMINI.md,docs/agent-playbooks/*, anddocs/kmp-status.mdwhen those areas are affected.
C. Expect/Actual Patterns
Use expect/actual sparingly for platform-specific types (e.g., Location, platform utilities) to keep core logic pure. For navigation, prefer shared Navigation 3 backstack state (List<NavKey>) over platform controller types.
5. Troubleshooting
- Build Failures: Always check
gradle/libs.versions.tomlfor dependency conflicts. - Missing Secrets: Copy
secrets.defaults.properties→local.propertieswith valid (or dummy) values forMAPS_API_KEY,datadogApplicationId, anddatadogClientToken. - JDK Version: JDK 17 is required. Mismatched JDK versions cause Gradle sync/build failures.
- Configuration Cache: Add
--no-configuration-cacheflag if cache-related issues persist. - Koin Injection Failures: Verify the KMP component is included in
approot module wiring (AppKoinModule) and thatstartKoinloads that module at app startup. - Desktop
Dispatchers.Mainmissing: JVM/Desktop requireskotlinx-coroutines-swingforDispatchers.Main. Without it, any code usinglifecycle.coroutineScopeorDispatchers.Mainwill crash at runtime. The desktop module already includes this dependency.