diff --git a/main/assets/js/search-data.json b/main/assets/js/search-data.json index 9a5a1fe0bc..5a64164101 100644 --- a/main/assets/js/search-data.json +++ b/main/assets/js/search-data.json @@ -71,7 +71,7 @@ },"10": { "doc": "Adding a Feature Module", "title": "Checklist", - "content": ". | Module directory created | build.gradle.kts with correct plugins and dependencies | Added to settings.gradle.kts | DI module created with @ComponentScan | DI module registered in app and desktop roots | Routes added to Routes.kt | Navigation entries registered | ./gradlew kmpSmokeCompile passes | ./gradlew :feature:my-feature:allTests passes | . ", + "content": ". | Module directory created | build.gradle.kts with correct plugins and dependencies | Added to settings.gradle.kts | Added to ALL_MODULES_FULL in build-logic/convention/src/main/kotlin/RootConventionPlugin.kt; python3 scripts/check-module-list.py fails when a non-exempt module is missing from it, when an entry is absent from the settings.gradle.kts includes, or when an exempt module is re-added, and a non-exempt module missing from it is absent from Dokka and Kover aggregation and kmpSmokeCompile | Added to a test shard in .github/workflows/reusable-check.yml (shard-feature for a feature module): its :feature:my-feature:allTests task in tasks and its koverXmlReport in kover; python3 scripts/check-test-shards.py fails for a module with no test task in any shard, unless the module is listed in the script’s COVERED_ELSEWHERE or NO_TESTS_YET sets | If the module is user-facing, a page under docs/en/user/ and a MODULE_TO_DOCS entry for it in scripts/check-doc-coverage.js; the check fails when a listed module’s page is missing, and a module with no entry needs no page | DI module created with @ComponentScan | DI module registered in app and desktop roots | Routes added to Routes.kt | Navigation entries registered | ./gradlew kmpSmokeCompile passes | ./gradlew :feature:my-feature:allTests passes | . ", "url": "/Meshtastic-Android/main/en/developer/adding-a-feature-module.html#checklist", "relUrl": "/en/developer/adding-a-feature-module.html#checklist" @@ -1240,14 +1240,14 @@ },"177": { "doc": "Architecture", "title": "Module Categories", - "content": "androidApp/ — Android Application . The Android application entry point: . | Activity, Application, and Manifest definitions | Koin DI module composition (AppKoinModule) | Flavor-specific bindings (google/, fdroid/) | Android-only integrations (widgets, services) | . desktopApp/ — Desktop JVM Application . The Desktop (Linux/macOS/Windows) entry point: . | Compose Desktop window management | Desktop-specific DI (DesktopKoinModule) | Platform stubs for Android-only capabilities | DesktopRadioTransportFactory and a jSerialComm-based serial transport; the BLE and TCP transport implementations it wires up are shared code — they live in core:network, built on core:ble’s BLE primitives — not desktopApp-owned | . feature/* — Feature Modules . Each feature/ module owns a vertical slice of functionality: . | Module | Responsibility | . | feature:intro | Onboarding/welcome flow | . | feature:messaging | Messages, channels, contacts, quick chat | . | feature:connections | Bluetooth/USB/TCP connection management | . | feature:map | Map display, waypoints — shared state, policy and the waypoint editor | . | feature:map-maplibre | MapLibre map surfaces — used by the fdroid flavor and Desktop; the google flavor uses Google Maps instead. Tile-source definitions and the custom-source editor are in feature:map, so both renderers share them | . | feature:node | Node list, node detail, metrics | . | feature:settings | All configuration screens | . | feature:firmware | Firmware update flow | . | feature:docs | In-app documentation browser | . | feature:wifi-provision | Wi-Fi provisioning | . | feature:widget | Android home screen widgets | . | feature:discovery | Mesh network discovery | . Feature modules: . | Use the meshtastic.kmp.feature convention plugin | Depend on core modules, never on other feature modules | Own their navigation entries and DI registrations | Contain platform-specific implementations in androidMain/jvmMain/iosMain | . core/* — Core Modules . Shared infrastructure used by all features: . | Module | Responsibility | . | core:common | Utilities, extensions, build config | . | core:navigation | Routes, deep links, Navigation 3 | . | core:ui | Shared Compose components, icons, theme | . | core:resources | Shared string resources | . | core:model | Domain models | . | core:data | Data layer abstractions | . | core:domain | Use cases / business logic | . | core:database | Room KMP database | . | core:datastore | DataStore preferences | . | core:prefs | App preferences | . | core:repository | Repository interfaces | . | core:service | Mesh service layer | . | core:di | DI utilities | . | core:network | HTTP/serial/transport | . | core:ble | Bluetooth LE abstractions | . | core:barcode | QR / barcode scanning (channel-share QR codes) | . | core:nfc | NFC read/write support | . | core:takserver | Embedded TAK server integration | . | core:testing | Test utilities | . | core:konsist | Konsist architecture/convention tests | . Protobuf models come from the external org.meshtastic:protobufs Maven artifact (pinned in gradle/libs.versions.toml). ", + "content": "androidApp/ — Android Application . The Android application entry point: . | Activity, Application, and Manifest definitions | Koin DI module composition (AppKoinModule) | Flavor-specific bindings (google/, fdroid/) | Android-only integrations (widgets, services) | . desktopApp/ — Desktop JVM Application . The Desktop (Linux/macOS/Windows) entry point: . | Compose Desktop window management | Desktop-specific DI (DesktopKoinModule) | Platform stubs for Android-only capabilities | DesktopRadioTransportFactory and a jSerialComm-based serial transport; the BLE and TCP transport implementations it wires up are shared code — they live in core:network, built on core:ble’s BLE primitives — not desktopApp-owned | . feature/* — Feature Modules . Each feature/ module owns a vertical slice of functionality: . | Module | Responsibility | . | feature:intro | Onboarding/welcome flow | . | feature:messaging | Messages, channels, contacts, quick chat | . | feature:connections | Bluetooth/USB/TCP connection management | . | feature:map | Map display, waypoints — shared state, policy and the waypoint editor | . | feature:map-maplibre | MapLibre map surfaces — used by the fdroid flavor and Desktop; the google flavor uses Google Maps instead. Tile-source definitions and the custom-source editor are in feature:map, so both renderers share them | . | feature:map-terrain | Offline terrain: elevation decode, hillshade shading and contour lines, shared by both map flavors | . | feature:node | Node list, node detail, metrics | . | feature:settings | All configuration screens | . | feature:firmware | Firmware update flow | . | feature:docs | In-app documentation browser | . | feature:wifi-provision | Wi-Fi provisioning | . | feature:widget | Android home screen widgets | . | feature:discovery | Mesh network discovery | . Feature modules: . | Use the meshtastic.kmp.feature convention plugin | Depend on core modules, not on other feature modules; the one exception is feature:map-maplibre, which builds on feature:map and feature:map-terrain | Own their navigation entries and DI registrations | Contain platform-specific implementations in androidMain/jvmMain/iosMain | . core/* — Core Modules . Shared infrastructure used by all features: . | Module | Responsibility | . | core:common | Utilities, extensions, build config | . | core:navigation | Routes, deep links, Navigation 3 | . | core:ui | Shared Compose components, icons, theme | . | core:resources | Shared string resources | . | core:model | Domain models | . | core:data | Data layer abstractions | . | core:domain | Use cases / business logic | . | core:database | Room KMP database | . | core:datastore | DataStore preferences | . | core:prefs | App preferences | . | core:repository | Repository interfaces | . | core:service | Mesh service layer | . | core:di | DI utilities | . | core:network | HTTP/serial/transport | . | core:ble | Bluetooth LE abstractions | . | core:barcode | QR / barcode scanning (channel-share QR codes) | . | core:nfc | NFC read/write support | . | core:takserver | Embedded TAK server integration | . | core:testing | Test utilities | . | core:konsist | Konsist architecture/convention tests | . Protobuf models come from the external org.meshtastic:protobufs Maven artifact (pinned in gradle/libs.versions.toml). ", "url": "/Meshtastic-Android/main/en/developer/architecture.html#module-categories", "relUrl": "/en/developer/architecture.html#module-categories" },"178": { "doc": "Architecture", "title": "KMP Source Sets", - "content": "Each module uses the standard KMP source set hierarchy: . src/ ├── commonMain/ ← Shared code (all platforms) ├── commonTest/ ← Shared tests ├── androidMain/ ← Android-specific ├── jvmMain/ ← Desktop JVM-specific ├── iosMain/ ← iOS-specific └── jvmTest/ ← Desktop test host . Golden Rules: . | No android.* imports in commonMain | Platform-specific code goes in appropriate source set | Prefer interfaces + DI over expect/actual for complex behaviors | Use expect/actual only for simple declarations | . ", + "content": "Each module uses the standard KMP source set hierarchy: . src/ ├── commonMain/ ← Shared code (all platforms) ├── commonTest/ ← Shared tests ├── androidMain/ ← Android-specific ├── jvmMain/ ← Desktop JVM-specific ├── jvmAndroidMain/ ← Shared by Android and desktop JVM ├── iosMain/ ← iOS-specific ├── nativeMain/ ← Native-target code shared across iOS targets ├── jvmTest/ ← Desktop test host ├── androidHostTest/ ← Android host (JVM) unit tests └── androidDeviceTest/ ← Instrumented tests (core:database, core:model) . jvmAndroidMain exists only in modules that apply meshtastic.kmp.jvm.android. nativeMain, androidHostTest (modules that call withHostTest) and androidDeviceTest exist only in the modules that need them. Golden Rules: . | No android.* imports in commonMain | Platform-specific code goes in appropriate source set | Prefer interfaces + DI over expect/actual for complex behaviors | Use expect/actual only for simple declarations | . ", "url": "/Meshtastic-Android/main/en/developer/architecture.html#kmp-source-sets", "relUrl": "/en/developer/architecture.html#kmp-source-sets" @@ -1289,7 +1289,7 @@ },"184": { "doc": "Codebase", "title": "Repository Structure", - "content": "Meshtastic-Android/ ├── androidApp/ # Android application module │ ├── src/main/ # Shared Android code │ ├── src/google/ # Google Play flavor (proprietary Google integrations — Gemini, Maps, Play services) │ └── src/fdroid/ # F-Droid flavor (FOSS-only) ├── desktopApp/ # Desktop JVM application ├── feature/ # Feature modules (KMP) │ ├── intro/ │ ├── messaging/ │ ├── connections/ │ ├── map/ │ ├── map-maplibre/ │ ├── node/ │ ├── settings/ │ ├── firmware/ │ ├── docs/ │ ├── wifi-provision/ │ ├── widget/ │ └── discovery/ ├── core/ # Core infrastructure modules (KMP) │ ├── barcode/ │ ├── ble/ │ ├── common/ │ ├── data/ │ ├── database/ │ ├── datastore/ │ ├── di/ │ ├── domain/ │ ├── konsist/ │ ├── model/ │ ├── navigation/ │ ├── network/ │ ├── nfc/ │ ├── prefs/ │ ├── repository/ │ ├── resources/ │ ├── service/ │ ├── takserver/ │ ├── testing/ │ └── ui/ ├── baselineprofile/ # Baseline Profile generation for :androidApp ├── screenshot-tests/ # Compose Preview screenshot tests (visual-regression gate) ├── docs-screenshots/ # Doc-framed composition screenshots (generate-only, not CI-gated) ├── build-logic/ # Convention plugins and build helpers │ └── convention/ ├── docs/ # Documentation source (markdown) │ └── en/ # English source; other locales live under docs/<locale>/user/ │ ├── user/ │ └── developer/ ├── gradle/ # Gradle wrapper and version catalog │ └── libs.versions.toml ├── specs/ # Feature specifications └── .github/workflows/ # CI/CD workflows . ", + "content": "Meshtastic-Android/ ├── androidApp/ # Android application module │ ├── src/main/ # Shared Android code │ ├── src/google/ # Google Play flavor (proprietary Google integrations — Gemini, Maps, Play services) │ └── src/fdroid/ # F-Droid flavor (FOSS-only) ├── desktopApp/ # Desktop JVM application ├── feature/ # Feature modules (KMP) │ ├── intro/ │ ├── messaging/ │ ├── connections/ │ ├── map/ │ ├── map-maplibre/ │ ├── map-terrain/ │ ├── node/ │ ├── settings/ │ ├── firmware/ │ ├── docs/ │ ├── wifi-provision/ │ ├── widget/ │ └── discovery/ ├── core/ # Core infrastructure modules (KMP) │ ├── barcode/ │ ├── ble/ │ ├── common/ │ ├── data/ │ ├── database/ │ ├── datastore/ │ ├── di/ │ ├── domain/ │ ├── konsist/ │ ├── model/ │ ├── navigation/ │ ├── network/ │ ├── nfc/ │ ├── prefs/ │ ├── repository/ │ ├── resources/ │ ├── service/ │ ├── takserver/ │ ├── testing/ │ └── ui/ ├── baselineprofile/ # Baseline Profile generation for :androidApp ├── screenshot-tests/ # Compose Preview screenshot tests (visual-regression gate) ├── docs-screenshots/ # Doc-framed composition screenshots (generate-only, not CI-gated) ├── build-logic/ # Convention plugins and build helpers │ └── convention/ ├── docs/ # Documentation source (markdown) │ └── en/ # English source; other locales live under docs/<locale>/user/ │ ├── user/ │ └── developer/ ├── gradle/ # Gradle wrapper and version catalog │ └── libs.versions.toml ├── specs/ # Feature specifications └── .github/workflows/ # CI/CD workflows . ", "url": "/Meshtastic-Android/main/en/developer/codebase.html#repository-structure", "relUrl": "/en/developer/codebase.html#repository-structure" @@ -1303,7 +1303,7 @@ },"186": { "doc": "Codebase", "title": "Build System", - "content": "Gradle Kotlin DSL . All build files use Kotlin DSL (.gradle.kts). Configuration: . | Version catalog: gradle/libs.versions.toml | Convention plugins: build-logic/convention/ | Settings: settings.gradle.kts | . Convention Plugins . Located in build-logic/convention/src/main/kotlin/. The full set is registered in build-logic/convention/build.gradle.kts; these are the ones a module build applies most often: . | Plugin | Purpose | . | meshtastic.kmp.feature | Standard feature module setup | . | meshtastic.kmp.library | Shared KMP library module | . | meshtastic.kmp.library.compose | KMP library that also ships Compose UI | . | meshtastic.kmp.jvm.android | JVM + Android target configuration | . | meshtastic.koin | Koin Annotations + K2 compiler plugin | . | meshtastic.kotlinx.serialization | Serialization plugin setup | . | meshtastic.android.room | Room KMP setup and schema location | . | meshtastic.android.screenshot | Compose Preview screenshot testing | . The rest cover the application and library variants, lint, detekt, spotless, Dokka, Kover, AboutLibraries, analytics, secrets, the docs tasks and the root aggregate — read the register(…) block rather than assuming a plugin does or does not exist. Build Variants (Android) . | Flavor | Description | . | google | Google Play distribution; includes proprietary APIs | . | fdroid | F-Droid distribution; FOSS-only dependencies | . Key Gradle Tasks . # Compile check across all KMP targets ./gradlew kmpSmokeCompile # Run all tests ./gradlew allTests # Code quality ./gradlew spotlessCheck detekt # Android build ./gradlew assembleGoogleDebug assembleFdroidDebug # Desktop run ./gradlew :desktopApp:run # Desktop native installers for the current OS (DMG / MSI+EXE / DEB+RPM+AppImage) ./gradlew :desktopApp:packageReleaseDistributionForCurrentOS # API reference (Dokka HTML → build/dokka/html) ./gradlew :dokkaGeneratePublicationHtml . ", + "content": "Gradle Kotlin DSL . All build files use Kotlin DSL (.gradle.kts). Configuration: . | Version catalog: gradle/libs.versions.toml | Convention plugins: build-logic/convention/ | Settings: settings.gradle.kts | . Convention Plugins . Located in build-logic/convention/src/main/kotlin/. The full set is registered in build-logic/convention/build.gradle.kts; these are the ones a module build applies most often: . | Plugin | Purpose | . | meshtastic.kmp.feature | Standard feature module setup | . | meshtastic.kmp.library | Shared KMP library module | . | meshtastic.kmp.library.compose | KMP library that also ships Compose UI | . | meshtastic.kmp.jvm.android | Adds the jvmAndroidMain source set shared by the Android and desktop JVM targets | . | meshtastic.koin | Koin Annotations + K2 compiler plugin | . | meshtastic.kotlinx.serialization | Serialization plugin setup | . | meshtastic.android.room | Room KMP setup and schema location | . | meshtastic.android.screenshot | Compose Preview screenshot testing | . The rest cover the application and library variants, lint, detekt, spotless, Dokka, Kover, AboutLibraries, analytics, secrets, the docs tasks and the root aggregate — read the register(…) block rather than assuming a plugin does or does not exist. Build Variants (Android) . | Flavor | Description | . | google | Google Play distribution; includes proprietary APIs | . | fdroid | F-Droid distribution; FOSS-only dependencies | . Key Gradle Tasks . # Compile check of every KMP module for JVM and iosSimulatorArm64 (excludes :desktopApp) ./gradlew kmpSmokeCompile # Run all tests: allTests covers KMP modules, test covers Android/JVM-only modules; run both ./gradlew test allTests # Code quality ./gradlew spotlessCheck detekt # Android build ./gradlew assembleGoogleDebug assembleFdroidDebug # Desktop run ./gradlew :desktopApp:run # Desktop native installers for the current OS (DMG / MSI+EXE / DEB+RPM+AppImage) ./gradlew :desktopApp:packageReleaseDistributionForCurrentOS # API reference (Dokka HTML → build/dokka/html) ./gradlew :dokkaGeneratePublicationHtml . ", "url": "/Meshtastic-Android/main/en/developer/codebase.html#build-system", "relUrl": "/en/developer/codebase.html#build-system" @@ -31354,7 +31354,7 @@ },"4479": { "doc": "Persistence", "title": "Room KMP Database", - "content": "Module: core:database . The primary structured data store: . | Node information and history | Message history | Waypoints | Telemetry data | Channel set configuration (channel names and LoRa config) | . Key Points . | Uses Room KMP for cross-platform compatibility | Migrations managed through Room’s built-in migration system | DAO interfaces live in core:database | Repository layer in core:repository provides the public API | Full-text message search is backed by an FTS5 content table (PacketFts) over Packet, kept in sync by Room-managed triggers | . What’s Stored in Room . | Entity | Description | . | NodeEntity | All known mesh nodes and their metadata | . | MyNodeEntity | The local node’s own info | . | Packet | Message history (channel and direct), waypoints, and telemetry data | . | PacketFts | FTS5 virtual table mirroring Packet.messageText for full-text message search (Room-managed INSERT/UPDATE/DELETE triggers keep it in sync) | . | ContactSettings | Per-contact mute and read-state | . | ReactionEntity | Emoji reactions on messages | . | MeshLog | Raw mesh protocol logs | . | MetadataEntity | Device metadata (firmware version, hardware model) | . | ChannelSetEntity | The connected radio’s channel set — channel names and LoRa config — one row per device | . | QuickChatAction | User-configured quick-chat messages | . | DeviceHardwareEntity | Cached device hardware catalog | . | FirmwareReleaseEntity | Cached firmware release info | . | TracerouteNodePositionEntity | Traceroute hop position data | . | DiscoverySessionEntity | A Local Mesh Discovery scan session (timestamp, presets scanned, home preset) | . | DiscoveryPresetResultEntity | Per-preset result within a discovery session | . | DiscoveredNodeEntity | Nodes found during a discovery preset scan | . | DeviceLinkEntity | Cached msh.to device links from the Meshtastic API | . ℹ️ Note: Waypoints and telemetry are stored within the Packet entity (the port_num field distinguishes packet types), alongside a channel index recording which channel each packet used. Channel configuration — names and LoRa settings — lives separately, in ChannelSetEntity. ", + "content": "Module: core:database . The primary structured data store: . | Node information and history | Message history | Waypoints | Telemetry data | Channel set configuration (channel names and LoRa config) | . Key Points . | Uses Room KMP for cross-platform compatibility | Migrations managed through Room’s built-in migration system | DAO interfaces live in core:database | Repository layer in core:repository provides the public API | Full-text message search is backed by an FTS5 content table (PacketFts) over Packet, kept in sync by Room-managed triggers | . What’s Stored in Room . | Entity | Description | . | NodeEntity | All known mesh nodes and their metadata | . | MyNodeEntity | The local node’s own info | . | Packet | Message history (channel and direct), waypoints, and telemetry data | . | PacketFts | FTS5 virtual table mirroring Packet.messageText for full-text message search (Room-managed INSERT/UPDATE/DELETE triggers keep it in sync) | . | ContactSettings | Per-contact mute and read-state | . | ReactionEntity | Emoji reactions on messages | . | MeshLog | Raw mesh protocol logs | . | MetadataEntity | Device metadata (firmware version, hardware model) | . | ChannelSetEntity | The connected radio’s channel set — channel names and LoRa config — one row per device | . | QuickChatAction | User-configured quick-chat messages | . | DeviceHardwareEntity | Cached device hardware catalog | . | FirmwareReleaseEntity | Cached firmware release info | . | TracerouteNodePositionEntity | Traceroute hop position data | . | DiscoverySessionEntity | A Local Mesh Discovery scan session (timestamp, presets scanned, home preset) | . | DiscoveryPresetResultEntity | Per-preset result within a discovery session | . | DiscoveredNodeEntity | Nodes found during a discovery preset scan | . | DeviceLinkEntity | Cached msh.to device links from the Meshtastic API | . | EventFirmwareEditionEntity | Event-firmware display records cached from the Meshtastic API (/resource/eventFirmware) | . | BootloaderOtaQuirksCacheEntity | Single-row cache of the nRF52 bootloader/OTA quirk catalog (/resource/bootloaderOtaQuirks), stored as one serialized envelope | . | MaintenanceUf2CacheEntity | Single-row cache of the maintenance-UF2 manifest (/resource/maintenanceUf2), stored as one serialized envelope | . | MergeMarkerEntity | Marks a completed DatabaseMerger merge so a re-run on the next connection skips it instead of duplicating rows | . ℹ️ Note: Waypoints and telemetry are stored within the Packet entity (the port_num field distinguishes packet types), alongside a channel index recording which channel each packet used. Channel configuration — names and LoRa settings — lives separately, in ChannelSetEntity. ", "url": "/Meshtastic-Android/main/en/developer/persistence.html#room-kmp-database", "relUrl": "/en/developer/persistence.html#room-kmp-database" @@ -42001,7 +42001,7 @@ },"6000": { "doc": "Testing", "title": "Test categories", - "content": "KMP unit tests (commonTest) . Shared tests that run on all platforms: ./gradlew allTests . | Business logic tests | Data model validation | Search/ranking algorithm tests | Route serialization tests | . Android host tests . Android-specific tests that run on JVM: ./gradlew test . | ViewModel tests | Repository tests with Room fakes | Android-specific integration tests | . Compose UI tests . Compose Multiplatform UI test framework: . @Test fun myScreenTest() = runComposeUiTest { setContent { MyScreen() } onNodeWithText(\"Expected\").assertIsDisplayed() } . Located in commonTest or jvmTest source sets. Screenshot tests . Uses Android Gradle Plugin’s native (layoutlib) screenshot testing framework, split across two modules: . | :screenshot-tests — the visual-regression gate. CI runs validateDebugScreenshotTest on it; reframing one of these baselines is a real diff to review. Holds atomic, dual-purpose components. | :docs-screenshots — generate-only, not validated in CI. Holds doc-framed compositions whose framing is tuned for the docs site, so reframing a doc image never churns the regression gate. | ./gradlew :screenshot-tests:updateDebugScreenshotTest # record regression goldens ./gradlew :screenshot-tests:validateDebugScreenshotTest # compare against goldens (CI gate) ./gradlew :docs-screenshots:updateDebugScreenshotTest # record doc-framed composition images ./gradlew :screenshot-tests:copyDocsScreenshots # copy doc images from BOTH modules into docs/assets . Rendering is host-deterministic here (layoutlib): a local update produces references byte-identical to CI, so locally-recorded goldens pass validate. See docs/assets/screenshots/README.md for which module a new screenshot belongs in. Store screenshots . The store-listing screenshots (Play, F-Droid, IzzyOnDroid, and the desktop app’s Flathub listing) are taken from the real apps, connected to Demo Mode’s hidden showcase mesh (/connections?address=mshowcase, MockScenario.SHOWCASE in :core:network), rather than drawn. Every screen is reached by its deep link, so the flow does not depend on the display language, and each shot is kept once the window has stopped changing. | Android: :store-screenshots, a UiAutomator 2.4 test module targeting :androidApp. For each surface fastlane supply uploads it sets the display size and density, relaunches the debug app through its shell-only AutomationLauncher alias with skip_onboarding and skip_connect_confirm, and saves the five listing shots, full screen with a SystemUI demo-mode status bar, to /data/local/tmp/store-screenshots/<flavor> on the device. | Desktop: store-screenshots/capture-desktop.sh runs the real desktop debug build on an Xvfb display, one launch per screen with that screen’s deep link, and saves the five Flathub shots. The map needs Skiko’s OpenGL renderer and Skiko refuses any GL adapter named llvmpipe or virgl, so Mesa runs GL through zink over lavapipe. | . On an emulator or device, one flavor at a time: ./gradlew :store-screenshots:connectedFdroidDebugAndroidTest adb pull /data/local/tmp/store-screenshots/fdroid/. fastlane/metadata/android/en-US/images/ . | Folder | Size | Window | Uploaded by | . | phoneScreenshots/ | 1080×1920 @400 dpi | compact: bottom navigation bar | fastlane supply | . | sevenInchScreenshots/ | 1080×1920 @288 dpi | medium: navigation rail, one pane | fastlane supply | . | tenInchScreenshots/ | 2560×1440 @320 dpi | expanded: rail, list beside detail | fastlane supply | . | desktopApp/packaging/linux/screenshots/ | 1280×800 | expanded: rail, list beside detail | Flathub, through the release assets metainfo.xml names | .github/workflows/store-screenshots.yml runs both on hosted runners, with both Android flavors in one job on one emulator (google for the Play listing, fdroid for the committed tree), on every internal release, on demand, and on pull requests that touch the renderer or the showcase mesh. The release pipeline attaches the captures to the release, publishes the Play listing from them on production, and opens a self-merging PR that writes the fdroid and desktop sets back here (RELEASE_PROCESS.md). Baseline Profile / startup performance . The :baselineprofile module (#5735) generates a Baseline Profile for :androidApp, AOT-compiling the hot startup paths so ART doesn’t pay the JIT cost on first launch. It targets the google flavor (the variant most users run). The Macrobenchmark generator (BaselineProfileGenerator) and the before/after benchmark (StartupBenchmark) live in baselineprofile/src/main/kotlin/org/meshtastic/baselineprofile/. Both run on a device/emulator: ./gradlew :androidApp:generateGoogleReleaseBaselineProfile # Generate the profile (commit the output) ./gradlew :androidApp:benchmarkGoogleReleaseBaselineProfile # Quantify the cold-start win . The generated profile is merged into androidApp/src/googleRelease/generated/baselineProfiles/ and packaged into release builds via androidx.profileinstaller. ℹ️ Note: The journey covers cold start only (launch → first frame), because CI has no paired node. Post-connection screens (node list, map, message thread) aren’t yet AOT-compiled. Extending the journey past cold start needs a fake transport or a connected node wired into the harness. ", + "content": "KMP unit tests (commonTest) . Shared tests written once and run on the JVM and, in modules that declare withHostTest, as Android host tests. commonTest also compiles for iOS, but iOS test execution is disabled, so no iOS test runs: ./gradlew allTests . | Business logic tests | Data model validation | Search/ranking algorithm tests | Route serialization tests | . Android host tests . Android-specific tests that run on the JVM. In pure-Android/JVM modules (such as androidApp) test runs them; in KMP modules that declare withHostTest {}, allTests runs them through testAndroidHostTest: ./gradlew test # pure Android/JVM modules ./gradlew allTests # KMP modules . | ViewModel tests | Repository tests with Room fakes | Android-specific integration tests | . Compose UI tests . Compose Multiplatform UI test framework: . @Test fun myScreenTest() = runComposeUiTest { setContent { MyScreen() } onNodeWithText(\"Expected\").assertIsDisplayed() } . Located in commonTest or jvmTest source sets. Screenshot tests . Uses Android Gradle Plugin’s native (layoutlib) screenshot testing framework, split across two modules: . | :screenshot-tests — the visual-regression gate. CI runs validateDebugScreenshotTest on it; reframing one of these baselines is a real diff to review. Holds atomic, dual-purpose components. | :docs-screenshots — generate-only, not validated in CI. Holds doc-framed compositions whose framing is tuned for the docs site, so reframing a doc image never churns the regression gate. | ./gradlew :screenshot-tests:updateDebugScreenshotTest # record regression goldens ./gradlew :screenshot-tests:validateDebugScreenshotTest # compare against goldens (CI gate) ./gradlew :docs-screenshots:updateDebugScreenshotTest # record doc-framed composition images ./gradlew :screenshot-tests:copyDocsScreenshots # copy doc images from BOTH modules into docs/assets . Rendering is host-deterministic here (layoutlib): a local update produces references byte-identical to CI, so locally-recorded goldens pass validate. See docs/assets/screenshots/README.md for which module a new screenshot belongs in. Store screenshots . The store-listing screenshots (Play, F-Droid, IzzyOnDroid, and the desktop app’s Flathub listing) are taken from the real apps, connected to Demo Mode’s hidden showcase mesh (/connections?address=mshowcase, MockScenario.SHOWCASE in :core:network), rather than drawn. Every screen is reached by its deep link, so the flow does not depend on the display language, and each shot is kept once the window has stopped changing. | Android: :store-screenshots, a UiAutomator 2.4 test module targeting :androidApp. For each surface fastlane supply uploads it sets the display size and density, relaunches the debug app through its shell-only AutomationLauncher alias with skip_onboarding and skip_connect_confirm, and saves the five listing shots, full screen with a SystemUI demo-mode status bar, to /data/local/tmp/store-screenshots/<flavor> on the device. | Desktop: store-screenshots/capture-desktop.sh runs the real desktop debug build on an Xvfb display, one launch per screen with that screen’s deep link, and saves the five Flathub shots. The map needs Skiko’s OpenGL renderer and Skiko refuses any GL adapter named llvmpipe or virgl, so Mesa runs GL through zink over lavapipe. | . On an emulator or device, one flavor at a time: ./gradlew :store-screenshots:connectedFdroidDebugAndroidTest adb pull /data/local/tmp/store-screenshots/fdroid/. fastlane/metadata/android/en-US/images/ . | Folder | Size | Window | Uploaded by | . | phoneScreenshots/ | 1080×1920 @400 dpi | compact: bottom navigation bar | fastlane supply | . | sevenInchScreenshots/ | 1080×1920 @288 dpi | medium: navigation rail, one pane | fastlane supply | . | tenInchScreenshots/ | 2560×1440 @320 dpi | expanded: rail, list beside detail | fastlane supply | . | desktopApp/packaging/linux/screenshots/ | 1280×800 | expanded: rail, list beside detail | Flathub, through the release assets metainfo.xml names | .github/workflows/store-screenshots.yml runs both on hosted runners, with both Android flavors in one job on one emulator (google for the Play listing, fdroid for the committed tree), on every internal release, on demand, and on pull requests that touch the renderer or the showcase mesh. The release pipeline attaches the captures to the release, publishes the Play listing from them on production, and opens a self-merging PR that writes the fdroid and desktop sets back here (RELEASE_PROCESS.md). Baseline Profile / startup performance . The :baselineprofile module (#5735) generates a Baseline Profile for :androidApp, AOT-compiling the hot startup paths so ART doesn’t pay the JIT cost on first launch. It targets the google flavor (the variant most users run). The Macrobenchmark generator (BaselineProfileGenerator) and the before/after benchmark (StartupBenchmark) live in baselineprofile/src/main/kotlin/org/meshtastic/baselineprofile/. Both run on a device/emulator: ./gradlew :androidApp:generateGoogleReleaseBaselineProfile # Generate the profile (commit the output) ./gradlew :androidApp:benchmarkGoogleReleaseBaselineProfile # Quantify the cold-start win . The generated profile is merged into androidApp/src/googleRelease/generated/baselineProfiles/ and packaged into release builds via androidx.profileinstaller. ℹ️ Note: The journey covers cold start only (launch → first frame), because CI has no paired node. Post-connection screens (node list, map, message thread) aren’t yet AOT-compiled. Extending the journey past cold start needs a fake transport or a connected node wired into the harness. ", "url": "/Meshtastic-Android/main/en/developer/testing.html#test-categories", "relUrl": "/en/developer/testing.html#test-categories" @@ -42015,14 +42015,14 @@ },"6002": { "doc": "Testing", "title": "Testing guidelines", - "content": "DO . | Write tests in commonTest when possible (runs everywhere) | Test business logic independently from UI | Use fakes/stubs instead of mocks where practical | Test edge cases: empty states, error states, boundary values | Test deep link routing in DeepLinkRouterTest | Keep tests fast — no network, no disk I/O in unit tests | . DON’T . | Don’t test framework behavior (Compose internals, Room queries) | Don’t create tests that depend on other feature modules | Don’t use Thread.sleep — use coroutine test dispatchers | Don’t rely on test execution order | . ", + "content": "DO . | Write tests in commonTest when possible (runs on the JVM, and as an Android host test where the module declares withHostTest) | Test business logic independently from UI | Use fakes/stubs instead of mocks where practical | Test edge cases: empty states, error states, boundary values | Test deep link routing in DeepLinkRouterTest | Keep tests fast — no network, no disk I/O in unit tests | . DON’T . | Don’t test framework behavior (Compose internals, Room queries) | Don’t create tests that depend on other feature modules | Don’t use Thread.sleep — use coroutine test dispatchers | Don’t rely on test execution order | . ", "url": "/Meshtastic-Android/main/en/developer/testing.html#testing-guidelines", "relUrl": "/en/developer/testing.html#testing-guidelines" },"6003": { "doc": "Testing", "title": "Running tests", - "content": "# All KMP tests ./gradlew allTests # Specific module ./gradlew :feature:docs:allTests # Code quality ./gradlew spotlessCheck detekt # Full verification ./gradlew spotlessCheck detekt kmpSmokeCompile test allTests . ", + "content": "# All tests: allTests covers KMP modules, test covers Android/JVM-only modules; run both ./gradlew test allTests # Specific module ./gradlew :feature:docs:allTests # Code quality ./gradlew spotlessCheck detekt # Full verification ./gradlew spotlessCheck detekt kmpSmokeCompile test allTests . ", "url": "/Meshtastic-Android/main/en/developer/testing.html#running-tests", "relUrl": "/en/developer/testing.html#running-tests" diff --git a/main/en/developer/adding-a-feature-module.html b/main/en/developer/adding-a-feature-module.html index 36c03fbbe8..0d3f8c7118 100644 --- a/main/en/developer/adding-a-feature-module.html +++ b/main/en/developer/adding-a-feature-module.html @@ -56,4 +56,4 @@ MyFeatureScreen(onNavigateUp = { backStack.removeLastOrNull() }) } } -
Both the graph sentinel (MyFeatureRoute.MyFeatureGraph) and the primary screen (MyFeatureRoute.MyFeatureHome) navigate to the same composable, so the feature is reachable via either a top-level push or a deep-link graph push — the same pattern feature:wifi-provision and feature:firmware use.
Then wire it up: call myFeatureGraph(backStack) from the shared entryProvider<NavKey> { } block in androidApp’s Main.kt and desktopApp’s DesktopNavigation.kt, alongside the other features’ entries functions. See Navigation Entry Registration.
| Source Set | Contains |
|---|---|
commonMain | Models, ViewModels, shared UI, DI module, navigation |
androidMain | Android-specific implementations (e.g., platform APIs) |
jvmMain | Desktop-specific implementations |
iosMain | iOS-specific implementations |
commonTest | Shared unit tests |
Every feature module should have:
commonTest for business logiccompose-multiplatform-ui-test where appropriatebuild.gradle.kts with correct plugins and dependenciessettings.gradle.kts@ComponentScanRoutes.kt./gradlew kmpSmokeCompile passes./gradlew :feature:my-feature:allTests passes