Skip to content

refactor(ble): Centralize BLE logic into a core module#4550

Merged
jamesarich merged 25 commits intomainfrom
feat/nordic-common-libs
Feb 20, 2026
Merged

refactor(ble): Centralize BLE logic into a core module#4550
jamesarich merged 25 commits intomainfrom
feat/nordic-common-libs

Conversation

@jamesarich
Copy link
Copy Markdown
Collaborator

@jamesarich jamesarich commented Feb 13, 2026

feat: Migrate BLE stack to Nordic Android Common Libraries

Overview

This PR migrates the entire Bluetooth Low Energy (BLE) stack to use Nordic Semiconductor's Android Common Libraries and Kotlin BLE Library. This modernization replaces the legacy Bluetooth implementation with a more robust, Coroutine-based architecture provided by the Nordic libraries, known for their reliability in the Android BLE ecosystem.

Key Changes

1. Dependencies

  • Added no.nordicsemi.kotlin.ble:* (Kotlin BLE Library) for modern, Coroutine-based BLE communication.
  • Added no.nordicsemi.android.common:* (Common Libraries) for standardized UI components, scanning, and permission handling.

2. Core BLE Architecture (core/ble, app/src/.../radio)

  • New Interface: Introduced NordicBleInterface as the new IRadioInterface implementation for BLE, replacing the legacy BluetoothInterface. It uses BleConnection to manage connections via Nordic's CentralManager.
  • Repository Update: BluetoothRepository has been rewritten to use Nordic's CentralManager for device scanning, state management, and bonding.
  • Connection Management: Added BleConnection and BleRetry utilities to handle connection lifecycles, service discovery, and retry logic more robustly.

3. Firmware OTA (feature/firmware)

  • BleOtaTransport: Updated the ESP32 OTA transport (BleOtaTransport) to leverage the Nordic Kotlin BLE library. This improves the reliability of firmware updates over BLE, including better handling of MTU negotiation and characteristic notifications.

4. UI & Scanning (app/src/.../ui/connections)

  • Scanner View: The BLEDevices composable now utilizes the Nordic Scanner View (no.nordicsemi.android.common.scanner.view.ScannerView). This provides a standardized, feature-rich scanning interface with built-in filtering and permission handling.
  • ViewModel: ScannerViewModel and DeviceListEntry have been updated to wrap Nordic's Peripheral objects, ensuring consistent device representation across the app.

5. Integration

  • Updated RadioInterfaceService and InterfaceFactory (via RadioRepositoryModule) to bind InterfaceId.BLUETOOTH to the new NordicBleInterfaceSpec.

Benefits

  • Stability: leveraged Nordic's battle-tested libraries to reduce BLE-related crashes and connection flakes.
  • Maintainability: Simplified the codebase by removing custom low-level BLE boilerplate in favor of declarative, Flow-based APIs.
  • User Experience: Improved scanning performance and provided a more consistent UI for device discovery.

Verification

  • Bluetooth connection to radios (Pairing, Connecting, sending/receiving messages).
  • Device scanning in the Connection tab.
  • Firmware updates (OTA) via BLE.
  • Automatic reconnection logic.

This commit refactors the Bluetooth Low Energy (BLE) implementation by moving all related logic from the `app` module into a new, dedicated `:core:ble` module. This improves modularity, reduces code duplication, and simplifies dependencies.

Key changes include:
- **`core:ble` module:** Introduces a new module to encapsulate all BLE-related functionality, including scanning, connection management, and utility functions.
- **`BleConnection` class:** A new class to manage the lifecycle of a single BLE peripheral connection, including state monitoring and service discovery.
- **`BleScanner` class:** A wrapper around the Nordic BLE scanner for a consistent scanning API.
- **`NordicBleInterface` refactoring:** Updated to use the new `BleConnection` and `BleScanner` classes, simplifying its logic and making it more robust with retry mechanisms.
- **Nordic Common Libraries:** Replaces `accompanist-permissions` and custom `BroadcastReceiver` implementations with components from Nordic's `no.nordicsemi.android.common` library for handling permissions (`RequireBluetooth`, `RequireLocation`) and lifecycle-aware receivers.
- **Dependency cleanup:** Removes duplicated BLE logic and constants from the `app` and `feature` modules, which now depend on `:core:ble`.
- **`kotlin.uuid`:** Migrates from `java.util.UUID` to `kotlin.uuid.Uuid` for multiplatform compatibility.

Signed-off-by: James Rich <[email protected]>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the app’s Bluetooth Low Energy stack by extracting BLE scanning/connection logic into a new :core:ble module and updating call sites to use Nordic Common libraries for permissions, receivers, and UI components.

Changes:

  • Added :core:ble module (connection wrapper, scanner wrapper, retry helper, constants, repository/receiver wiring).
  • Replaced Accompanist permissions + custom receivers with Nordic Common permissions/receiver utilities and scanner UI.
  • Migrated many UUID usages from java.util.UUID to kotlin.uuid.Uuid.

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
settings.gradle.kts Includes the new :core:ble module in the build.
gradle/libs.versions.toml Adds Nordic Common deps; adjusts KSP plugin entry.
feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PositionConfigItemList.kt Switches location permission handling to Nordic Common RequireLocation.
feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DeviceConfigItemList.kt Replaces manual timezone receiver with Nordic Common registerReceiver.
feature/settings/build.gradle.kts Adds Nordic Common core + BLE permissions dependencies.
feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt Migrates random IDs from UUID to Uuid.
feature/intro/src/main/kotlin/org/meshtastic/feature/intro/AppIntroductionScreen.kt Replaces Accompanist permissions with Nordic Common permission composables; adds Bluetooth step.
feature/intro/build.gradle.kts Removes accompanist-permissions; adds Nordic permissions deps; enables Hilt plugin.
feature/firmware/src/test/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportTest.kt Updates UUID handling in tests to kotlin.uuid.Uuid.
feature/firmware/src/test/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportNordicMockTest.kt Updates mock OTA GATT setup + UUIDs.
feature/firmware/src/test/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportMtuTest.kt Updates UUID usage in MTU-related test.
feature/firmware/src/test/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportErrorTest.kt Updates UUID usage and mock GATT setup for error-path tests.
feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransport.kt Uses new BleConnection/BleScanner; migrates UUIDs; adjusts connect/discovery flow.
feature/firmware/build.gradle.kts Adds dependency on projects.core.ble.
core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TimeTickWithLifecycle.kt Replaces custom BroadcastReceiver management with Nordic Common registerReceiver.
core/ui/build.gradle.kts Adds Nordic Common core dependency.
core/prefs/src/main/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefs.kt Migrates install ID generation to Uuid.
core/data/src/test/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryTest.kt Migrates test UUIDs to Uuid.
core/data/src/google/kotlin/org/meshtastic/core/data/model/CustomTileProviderConfig.kt Migrates default ID to Uuid.
core/common/src/androidMain/kotlin/org/meshtastic/core/common/ContextServices.kt Adds registerReceiverCompat extension (core-level).
core/common/build.gradle.kts Exposes Nordic Common core from core:common androidMain; adds Kermit to commonMain.
core/ble/src/main/kotlin/org/meshtastic/core/ble/MeshtasticBleConstants.kt Centralizes Meshtastic BLE UUID constants using Uuid.
core/ble/src/main/kotlin/org/meshtastic/core/ble/BluetoothState.kt Moves Bluetooth state model into org.meshtastic.core.ble package.
core/ble/src/main/kotlin/org/meshtastic/core/ble/BluetoothRepository.kt Moves BLE scan/state logic into core module and uses BleScanner.
core/ble/src/main/kotlin/org/meshtastic/core/ble/BluetoothBroadcastReceiver.kt Adds core-level Bluetooth broadcast receiver refreshing repository state.
core/ble/src/main/kotlin/org/meshtastic/core/ble/BleScanner.kt Introduces a wrapper for scanning that yields Peripheral flows.
core/ble/src/main/kotlin/org/meshtastic/core/ble/BleRetry.kt Introduces generic BLE retry helper with delay/backoff.
core/ble/src/main/kotlin/org/meshtastic/core/ble/BleModule.kt Adds Hilt module for BLE (CentralManager + singleton scope).
core/ble/src/main/kotlin/org/meshtastic/core/ble/BleError.kt Moves BLE error mapping into core module.
core/ble/src/main/kotlin/org/meshtastic/core/ble/BleConnection.kt Adds reusable connection wrapper (state flow + characteristic discovery).
core/ble/build.gradle.kts New Gradle module definition + dependencies for :core:ble.
build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt Globally opts into kotlin.uuid.ExperimentalUuidApi.
app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceTest.kt Updates tests to use MeshtasticBleConstants + adjusts retry timing expectation.
app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceRetryTest.kt Updates tests to use MeshtasticBleConstants.
app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceDrainTest.kt Updates tests to use MeshtasticBleConstants.
app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt Adds RSSI display via Nordic Common UI RssiIcon.
app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt Replaces custom BLE signal UI with Nordic RssiIcon.
app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt Replaces custom permissions + lists with Nordic Common ScannerView filter UI.
app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsViewModel.kt Removes dependency on app-level Bluetooth repository.
app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt Adapts BLE device selection UI flow to new BLEDevices/ScannerView approach.
app/src/main/java/com/geeksville/mesh/ui/Main.kt Switches notification permission request to Nordic Common helper.
app/src/main/java/com/geeksville/mesh/service/PacketHandler.kt Migrates MeshLog UUID creation to Uuid.
app/src/main/java/com/geeksville/mesh/service/MeshMessageProcessor.kt Migrates MeshLog UUID creation to Uuid.
app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt Uses Nordic simpleSharedFlow; migrates BLE repo/error types to core module.
app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt Switches BluetoothRepository import to core module.
app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt Refactors to use BleConnection/BleScanner/centralized constants/retry helper.
app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothDevice.kt Removes app-level Bluetooth bonding helper (moved/removed as part of refactor).
app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothBroadcastReceiver.kt Removes app-level Bluetooth receiver (replaced by core receiver).
app/src/main/java/com/geeksville/mesh/model/DeviceListEntry.kt Uses BLE name pattern constant from core BLE; adds Peripheral.getMeshtasticShortName().
app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt Switches BluetoothRepository import to core module.
app/src/main/java/com/geeksville/mesh/MainActivity.kt Migrates activity base class to ComponentActivity.
app/build.gradle.kts Adds projects.core.ble + Nordic Common dependencies to app module.

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 41.58654% with 243 lines in your changes missing coverage. Please review.
✅ Project coverage is 15.74%. Comparing base (f012e38) to head (17f38b4).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...geeksville/mesh/ui/connections/ScannerViewModel.kt 0.00% 59 Missing ⚠️
...sville/mesh/repository/radio/NordicBleInterface.kt 67.70% 21 Missing and 10 partials ⚠️
...eshtastic/core/common/util/Utf8ByteLengthFilter.kt 0.00% 24 Missing ⚠️
.../java/com/geeksville/mesh/model/DeviceListEntry.kt 0.00% 20 Missing ⚠️
...a/com/geeksville/mesh/repository/usb/UsbManager.kt 0.00% 14 Missing ⚠️
...meshtastic/feature/firmware/ota/BleOtaTransport.kt 68.75% 8 Missing and 2 partials ⚠️
...tlin/org/meshtastic/core/common/util/Exceptions.kt 0.00% 8 Missing ⚠️
...in/kotlin/org/meshtastic/core/ble/BleConnection.kt 85.71% 0 Missing and 6 partials ⚠️
.../meshtastic/core/ble/BluetoothBroadcastReceiver.kt 0.00% 6 Missing ⚠️
...lin/org/meshtastic/core/ble/BluetoothRepository.kt 45.45% 1 Missing and 5 partials ⚠️
... and 28 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4550      +/-   ##
==========================================
+ Coverage   14.77%   15.74%   +0.97%     
==========================================
  Files         429      426       -3     
  Lines       14872    14851      -21     
  Branches     2468     2472       +4     
==========================================
+ Hits         2197     2338     +141     
+ Misses      12357    12176     -181     
- Partials      318      337      +19     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

This commit introduces a retry mechanism when sending a packet to the radio over BLE.

The `retryBleOperation` function is now used to wrap the characteristic write call, improving the reliability of packet transmission. The success log message has also been updated to reflect that the write was successful.

Signed-off-by: James Rich <[email protected]>
This commit refactors the BLE connection and error handling logic for better stability and clarity.

Key changes include:
- A new `connectAndAwait` function in `BleConnection` which attempts to connect and waits for a definitive `Connected` or `Disconnected` state, throwing a specific `BleError` on failure or timeout.
- A simplified and more robust `BleError` sealed class to provide clearer error information.
- Updated `BleOtaTransport` and `NordicBleInterface` to use `connectAndAwait`, replacing manual timeout and state handling with a more streamlined approach.
- Service discovery in `BleConnection` now includes a timeout to prevent indefinite hangs.
- Corrected a typo in `libs.versions.toml` for the `coil-svg` dependency.

Signed-off-by: James Rich <[email protected]>
This commit refactors the BLE OTA transport test to be more robust and realistic.

- Switched from `StandardTestDispatcher` to `UnconfinedTestDispatcher` for more reliable coroutine testing.
- Enhanced the mock peripheral to accurately simulate the OTA protocol, including tracking received bytes and sending the final "OK" message correctly after all data chunks are received.
- Made the test data size smaller and configurable for faster test execution.
- Changed the response channel in `BleOtaTransport` to `UNLIMITED` to prevent potential deadlocks during testing.
- Fixed a dependency scope in the `intro` feature module.

Signed-off-by: James Rich <[email protected]>
This commit updates the `nordic-ble` library from version `2.0.0-alpha12` to `2.0.0-alpha14`.

Key changes include:
- Integrating `AndroidEnvironment` to manage Bluetooth state and permissions, replacing the custom `BluetoothBroadcastReceiver`.
- Updating BLE module dependencies and providing `AndroidEnvironment` via dependency injection.
- Adding the `-Xskip-prerelease-check` Kotlin compiler option to support the alpha library version.

Signed-off-by: James Rich <[email protected]>
Adds unit tests for the `BluetoothRepository` to verify its core functionalities.

- Introduces tests for the initial state, state updates on Bluetooth power changes, device scanning, and bonded device discovery.
- Adds `androidx.lifecycle:lifecycle-runtime-testing` for testing Lifecycle-aware components.
- Updates Nordic BLE mock dependency names for consistency (`client-mock` to `client-core-mock`).
- Implements the tests using the Nordic BLE mock environment to simulate peripheral devices and Central Manager behavior.

Signed-off-by: James Rich <[email protected]>
Decouples the `BleScanner` from the `BluetoothRepository` by introducing it as a constructor dependency. This change improves testability by allowing `BleScanner` to be mocked in `BluetoothRepositoryTest`.

The tests have been refactored to use mocks for `BleScanner`, enabling more focused and reliable testing of the repository's logic without relying on the specifics of the BLE scanning implementation. The `BleScanner` class is also updated with an `@Inject` annotation to support dependency injection.

Signed-off-by: James Rich <[email protected]>
The test `drainPacketQueueAndDispatch` was previously ignored due to intermittent failures related to timing in the Nordic BLE mock library. This commit re-enables the test by removing the `@Ignore` annotation.

Signed-off-by: James Rich <[email protected]>
Signed-off-by: James Rich <[email protected]>
This commit introduces several modern Android development practices to improve the `MainActivity` and related components:

-   **Enable Predictive Back Gesture**: Enables the new predictive back gesture introduced in Android 13 by setting `enableOnBackInvokedCallback="true"` in the `AndroidManifest.xml`.
-   **Modernize Intent Handling**: Replaces the deprecated `onNewIntent` override with `addOnNewIntentListener`. Uses `IntentCompat` for type-safe handling of `Parcelable` extras from NFC intents.
-   **Improve Edge-to-Edge Display**: Updates `enableEdgeToEdge` to provide theme-aware (dark/light) system bar styling for a more seamless UI.
-   **Optimize Startup**: Implements `ReportDrawnWhen` to signal to the system when the UI is fully drawn, improving startup performance measurement.
-   **Clean Up Splash Screen**: Simplifies the splash screen theme (`styles.xml`) and removes the obsolete `colors.xml` file by referencing `android:color/black` directly in the splash icon drawable.
-   **Use Lifecycle-Aware Coroutines**: Migrates from `collectAsState` to `collectAsStateWithLifecycle` for more efficient and safer UI state collection.
-   **Fix Bitwise Operation**: Corrects a logical error in `MeshServiceClient` by using the bitwise `or` operator instead of `+` for combining service binding flags.

Signed-off-by: James Rich <[email protected]>
Closes the MeshServiceClient connection when the app enters the stopped state. This is done by implementing the `onStop` lifecycle callback to call `close()`.

Signed-off-by: James Rich <[email protected]>
@jamesarich jamesarich marked this pull request as draft February 14, 2026 01:56
…libs

# Conflicts:
#	app/src/main/java/com/geeksville/mesh/MainActivity.kt
#	app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt
#	app/src/main/java/com/geeksville/mesh/service/MeshMessageProcessor.kt
#	app/src/main/java/com/geeksville/mesh/service/PacketHandler.kt
#	build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt
#	core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TimeTickWithLifecycle.kt
#	feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DeviceConfigItemList.kt
#	gradle/libs.versions.toml
# Conflicts:
#	app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt
This commit introduces significant improvements to the stability of Bluetooth Low Energy (BLE) connections and refactors the testing infrastructure.

A race condition in the Firmware Over-the-Air (OTA) update process has been resolved. Previously, a very fast device could send a response before the client was ready to listen, causing the update to fail. By waiting for the notification subscription to be fully established before proceeding, this issue is now fixed.

Similarly, the main radio connection logic has been hardened. The connection process now explicitly waits for all required characteristic notifications to be successfully subscribed before considering the connection complete. This prevents race conditions and ensures the link is fully operational.

The testing framework has been updated to use the more advanced `MockAndroidEnvironment` from the Nordic BLE library. This provides a more realistic and robust simulation of the BLE stack, improving the reliability of our tests.

Finally, a deprecated `ZoneIdExtensions.kt` file has been removed, and the dependencies for the Nordic BLE library have been updated.

**BREAKING CHANGE:** The `nordic-log` dependency has been removed. All tests now use the `MockAndroidEnvironment` for BLE simulation. This might require adjustments in downstream modules or test configurations that relied on the previous setup.

Signed-off-by: James Rich <[email protected]>
This commit introduces several UI enhancements to the Connections screen for a more polished and consistent user experience. It also adds foundational UI tests for this screen.

Key changes include:

-   **Consistent Sizing and Spacing:** The layout of the `ConnectingDeviceInfo` and `DeviceListItem` components has been refined with standardized spacing, padding, and typography to align with the Material Design style. Progress indicators and icons have also been resized for better visual balance.
-   **Improved BLE Device Display:** BLE devices in the list are now wrapped in `Card` elements for better visual separation.
-   **Throttled RSSI Updates:** The RSSI value displayed for BLE devices is now updated at a throttled rate to reduce visual noise and align with the device's polling frequency.
-   **Scrollable Device Lists:** The device lists for TCP and USB connections are now independently scrollable, preventing the entire screen from scrolling and keeping the connection type tabs visible.
-   **Refactored NetworkDevices Composable:** The logic for displaying network devices has been extracted into an internal, previewable composable function (`NetworkDevicesInternal`), improving testability and code organization.
-   **UI Test Infrastructure:** Added new instrumentation tests for the `ConnectionsScreen` using `MockAndroidEnvironment` to simulate different Bluetooth states (enabled/disabled) and verify the UI's response. Corresponding test dependencies have been added to the build configuration.

Signed-off-by: James Rich <[email protected]>
This commit enhances the device connection screen by displaying associated node information (long name and short name) for each discovered or saved device.

When a device is found via BLE, USB, or TCP, the app now attempts to find a matching node from the current node database. If a match is found, the node's chip, showing its long and short name, is displayed next to the device name in the list. This provides users with more context about the devices they are connecting to.

The matching logic is specific to the connection type:
- **BLE:** Matches using the device's short name from its advertisement data.
- **TCP (mDNS):** Matches using the node ID from the mDNS TXT record.
- **USB & Recent TCP:** Attempts to match using the last part of the device name (e.g., the `_xxxx` suffix).

To support this, the `DeviceListEntry` data structure has been updated to include an optional `Node` property.

Signed-off-by: James Rich <[email protected]>
This commit refactors the device connection screen for a cleaner and more consistent user experience, and improves how Bluetooth devices are named.

The "Available Bluetooth Devices" and "Recent Network Devices" lists have been updated to use a consistent card-based layout with a shared headline style. The previous `TitledCard` has been replaced with a standard `Card` and `Text` combination for better visual alignment with the rest of the UI.

In the string resources, "BLE Devices" has been renamed to "Available Bluetooth Devices" for clarity. Additionally, a new "Friendly address" string has been introduced.

Signed-off-by: James Rich <[email protected]>
This commit refactors the connection management logic by separating the concerns of BLE device scanning from the broader connection and device management.

A new `ScannerViewModel` has been created to handle the presentation logic for all connection types (BLE, USB, TCP), replacing the monolithic `BTScanModel`. The responsibility for initiating and managing BLE scans has been moved from the `BluetoothRepository` to the `ScannerView` composable, leveraging the Nordic `ScannerView` component for a more standard and encapsulated implementation.

This change simplifies the `BluetoothRepository` by removing scanning-related flows and logic, allowing it to focus solely on managing Bluetooth state and bonding.

Key changes:
- `BTScanModel` has been deleted and replaced by `ScannerViewModel`.
- `BluetoothRepository` no longer handles BLE scanning; this is now managed by the `ScannerView`.
- The `ConnectionsScreen` and related composables have been updated to use `ScannerViewModel`.
- Unused flows and properties in `UsbRepository` and `UIViewModel` have been removed.

BREAKING CHANGE: The `BTScanModel` class has been removed and its responsibilities have been refactored into `ScannerViewModel` and the `ScannerView` UI component. The `BluetoothRepository` API has changed, as it no longer provides BLE scanning functionality. Downstream dependencies on `BTScanModel` or the scanning methods in `BluetoothRepository` will need to be updated.

Signed-off-by: James Rich <[email protected]>
This commit centralizes common utility classes and functions from the `app` module into a new `core/common` module. This consolidation removes duplicated code, improves modularity, and simplifies dependencies across the feature modules.

Key changes include:
- **Creation of `core/common` module**: A new module to house shared, platform-agnostic and platform-specific utilities.
- **Moved utility classes**:
    - `BuildUtils`, `DebugLogFile`, `BinaryLogFile`, `DateUtils` and `Utf8ByteLengthFilter` moved to `core/common`.
    - Concurrency helpers like `ServiceClient`, `SequentialJob`, `SyncContinuation`, `DeferredExecution`, and `handledLaunch` are now in `core/common`.
    - Exception handling utilities (`Exceptions`, `exceptionReporter`, `toRemoteExceptions`) have been centralized.
- **Relocated extension functions**:
    - `Modifier.thenIf` moved to `core/ui`.
    - Time-related extensions (`nowMillis`, `nowSeconds`, etc.) are now in `core/common`.
    - `getInitials` logic moved into `core/model`.
- **Dependency updates**: `build.gradle.kts` files across various modules (`core/data`, `core/service`, `core/ui`, `feature/node`, etc.) have been updated to depend on the new `core/common` module.
- **Code cleanup**:
    - Removed `ExperimentalMaterial3ExpressiveApi` annotation as it is no longer needed. Replaced `CircularWavyProgressIndicator` with `CircularProgressIndicator`.
    - Renamed `UIState.kt` to `UIViewModel.kt` for clarity.
    - Simplified Paging 3 related code in `ContactsScreen` for better readability.

This refactoring reduces code duplication and establishes a clearer architectural pattern for shared utilities, making the codebase more maintainable and scalable.

Signed-off-by: James Rich <[email protected]>
This commit introduces `ServiceClient`, a new class designed to manage the lifecycle of a bound Android service connection in a more robust and testable way.

The `ServiceClient` handles the complexities of binding to a service, including:
- Retrying the bind operation on initial failure.
- Throwing a `BindFailedException` after multiple failed attempts.
- Providing a `waitConnect()` method that blocks until the service is successfully connected, which is useful for ensuring the service is ready before use.
- Managing unbinding when `close()` is called.

Comprehensive unit tests (`ServiceClientTest`) have been added to validate this new functionality, covering successful connections, retry logic, connection waiting, and the unbinding process.

Additionally, a minor refactoring was done in `MigrationTest.kt` to update an import path for `nowMillis`, moving it to the `core.common` package.

Signed-off-by: James Rich <[email protected]>
This commit introduces comprehensive documentation for the project's architecture and the modernized Bluetooth Low Energy (BLE) stack.

The main `README.md` has been updated to include a high-level overview of the app's architecture, covering UI, state management, dependency injection, navigation, and the data layer. It also now points to the new BLE documentation.

A detailed `README.md` has been added to the `core/ble` module. This new file explains the move to Nordic Semiconductor's Coroutine-based BLE libraries, replacing the legacy implementation. It outlines the key components of the new stack, including `NordicBleInterface`, `BluetoothRepository`, and `BleConnection`, and provides guidance on architecture and testing within the module.

Finally, the `AGENTS.md` file, which serves as a guide for contributors, has been updated to reflect these architectural changes. It now includes a dedicated section for the new BLE stack, directing developers to use the modern abstractions and avoid legacy APIs.

Signed-off-by: James Rich <[email protected]>
This commit populates the `README.md` file for each module with a detailed overview, a description of its key components, and an auto-generated Mermaid dependency graph.

This documentation provides a high-level understanding of each module's role within the application architecture.

Additionally, the `build-logic` for graph generation has been improved to correctly identify Android Feature modules and ensure the README files are updated only when the graph content changes.

The `CONTRIBUTING.md` has also been updated to reflect the new, centralized string resource management in the `:core:strings` module.

Signed-off-by: James Rich <[email protected]>
This commit refactors the project's testing infrastructure to improve consistency and maintainability.

Key changes include:
- A legacy UI test, `ConnectionScreenTest.kt`, has been removed as its functionality is now covered by more modern tests utilizing `MockAndroidEnvironment`.
- The `getInitials` utility function has been moved from the `app` module to `core.model.util` to be accessible across the project.
- Time-related utility functions like `nowMillis`, `nowSeconds`, etc., have been relocated from `core.model.util` to a new `core.common.util` module.
- The project's Android Gradle Plugin has been updated, enabling `isIncludeAndroidResources = true` for unit tests, which allows them to access Android resources.
- String resource fetching in tests has been standardized to use `org.meshtastic.core.strings.getString`.
- A dummy Maps API key has been added to the "google" product flavor's manifest placeholders to prevent build failures in environments without a `secrets.properties` file.
- The SDK version for `MeshServiceBroadcastsTest` has been explicitly set to 34.

**BREAKING CHANGE:** The `getInitials()` and various time utility functions (`nowMillis`, `toInstant`, etc.) have moved. Downstream modules and tests must update their imports to use the new locations in `org.meshtastic.core.model.util.getInitials` and `org.meshtastic.core.common.util` respectively.

Signed-off-by: James Rich <[email protected]>
This commit updates the internal (`AGENTS.md`) and external (`CONTRIBUTING.md`) documentation to reflect current testing best practices.

The guidelines now recommend using Robolectric for JVM-based Compose UI tests, which are faster than instrumented tests. A best practice note has been added to pass mocked ViewModels directly to Composables in these tests instead of using Hilt.

Additionally, a critical workaround for developers using Java 17 is included: annotate Robolectric tests with `@Config(sdk = [34])` to prevent compatibility issues with Android SDK 35. The documentation also clarifies the distinction between component-level JVM tests and full end-to-end instrumented tests.

Signed-off-by: James Rich <[email protected]>
@jamesarich jamesarich marked this pull request as ready for review February 20, 2026 12:10
@jamesarich jamesarich added this pull request to the merge queue Feb 20, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 20, 2026
@jamesarich jamesarich added this pull request to the merge queue Feb 20, 2026
Merged via the queue into main with commit 6bfa5b5 Feb 20, 2026
10 of 13 checks passed
@jamesarich jamesarich deleted the feat/nordic-common-libs branch February 20, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants