SdkStateBridge:
- Wrap handleServiceAction in try/catch to prevent bridge death
- Favorite/Ignore/Mute: only apply local state update on admin
success (eliminates optimistic state inconsistency)
- ImportContact: guard with runCatching, log failures
- Extract dispatchAction for clean separation of concerns
SdkRadioController:
- Wrap sendMessage with try/catch + logging before re-throw
- Wrap sendRemoteAdmin with try/catch + logging before re-throw
- Ensures BLE disconnect errors are visible in logs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eliminate the vestigial NodeManager/NodeRepository interface split.
All runtime node state management methods (handleReceivedUser,
handleReceivedPosition, handleReceivedTelemetry, updateNode, etc.)
now live directly on NodeRepository alongside the query surface.
- Delete NodeManager.kt (82 LOC)
- Extend NodeRepository with NodeIdLookup and add all manager methods
- Update 8 consumers to inject NodeRepository instead of NodeManager
- Remove dead nodeManager param from MeshServiceOrchestrator
- Add NodeManager methods to FakeNodeRepository test double
- Update all tests (mocks, constructor params, verifications)
- SdkNodeRepositoryImpl now binds [NodeRepository, NodeIdLookup]
Build: assembleDebug ✅, desktop:compileKotlin ✅, all jvmTests ✅
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Delete dead methods from NodeManager/NodeRepository interfaces:
- allowNodeDbWrites, setAllowNodeDbWrites (never read)
- loadCachedNodeDB (no-op, zero-value call in orchestrator)
- getNodes(): List<NodeInfo> (deprecated, unused)
- handleReceivedPaxcounter (zero callers)
- handleReceivedNodeStatus, updateNodeStatus (zero callers)
- insertMetadata (zero production callers)
Delete NodeInfo data class (85 LOC):
- All consumers now use Node domain model directly
- Retained MeshUser, Position, DeviceMetrics, EnvironmentMetrics
which have active consumers across feature modules
Remove corresponding implementations from SdkNodeRepositoryImpl,
FakeNodeRepository, and test verifications.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
NodeManager merge and MeshActivity restoration are done.
Only optional VM param slimming and test coverage remain.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Merge NodeManagerImpl logic into SdkNodeRepositoryImpl (single source
of truth for all node state — eliminates duplicate in-memory maps)
- SdkNodeRepositoryImpl now binds NodeRepository, NodeManager, NodeIdLookup
- Delete NodeManagerImpl.kt (377 LOC)
- Add meshActivityFlow to ServiceRepository for nav-bar icon animation
- Emit MeshActivity.Send from SdkPacketHandler and SdkRadioController
- Emit MeshActivity.Receive from ServiceRepositoryImpl.emitMeshPacket()
- Wire UIViewModel.meshActivity to serviceRepository.meshActivityFlow
- Align insertMetadata signature (remove unnecessary suspend)
- Adapt NodeManagerImplTest to test SdkNodeRepositoryImpl directly
- Update FakeServiceRepository with meshActivityFlow stub
All targets compile clean (Android + Desktop), all tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove my_node, nodes, metadata tables via AutoMigration with @DeleteTable
- Parameterize all 34 PacketDao queries to accept myNodeNum directly
- Delete NodeInfoDao, NodeEntity, MyNodeEntity, MetadataEntity
- Delete CommonNodeInfoDaoTest (dead)
- Rewrite PacketRepositoryImpl to inject NodeRepository for myNodeNum
- Rewrite CommonPacketDaoTest and CommonPacketRepositoryTest
- Update MigrationTest to remove nodeInfoDao usage
- Update core/database README to reflect current schema
SDK is now sole source of truth for all node data.
Room only stores messages, logs, and user annotations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MeshLogRepositoryImpl was the sole external consumer of
NodeInfoReadDataSource — it only needed myNodeNum to identify the
local node in logs. Replace with NodeRepository.myNodeInfo (already
SDK-backed via SdkNodeRepositoryImpl).
PacketRepositoryImpl referenced NodeInfoDao.MAX_BIND_PARAMS — inlined
as a private constant since it's just the SQLite bind-param limit.
With zero external consumers remaining, delete:
- NodeInfoReadDataSource interface
- SwitchingNodeInfoReadDataSource implementation
The Room NodeInfoDao/NodeEntity/MyNodeEntity remain in the database
module for now (internal tests + migration history) but have no
external dependents — ready for a future Room migration 40 to DROP.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Constants.kt re-exported MeshtasticIntent constants for the deleted
ServiceBroadcasts system. No consumers remain after broadcast removal.
Also remove MeshService.actionReceived() companion method (no callers).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- RadioClientProvider: support BLE, TCP, and Serial transports via
InterfaceId routing; handle MOCK/NOP gracefully
- SdkNodeListViewModel: use SDK's MeshNode model for 20+ enriched
fields (isOnline, battery, position, signal quality, etc.)
- SdkConfigViewModel: remove Gap G workaround (_localConfigOverrides),
add reactive channels StateFlow from SDK
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the two-step buildPeripheralForSavedAddress() + BleTransport(peripheral, address)
with the new SDK factory BleTransport(address) { autoConnectIf { true } }.
The SDK's transport-ble androidMain now exposes BleTransport(address: String,
builderAction: PeripheralBuilder.() -> Unit) directly, removing the need for
the core:ble workaround. The autoConnectIf { true } flag preserves bonded-device
behavior (avoids GATT 133 on reconnect without a fresh advertisement).
Gap F note removed from RadioClientProvider KDoc — gap is resolved in SDK.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wires core flows to the meshtastic-sdk (0.1.0-SNAPSHOT) while keeping the
legacy path alive. Goal: prove the SDK works with a real Android app and
surface API deficiencies.
Build:
- settings.gradle.kts: composite build inclusion for meshtastic-sdk
(../meshtastic-sdk) with dependency substitution for all SDK artifacts
- libs.versions.toml: sdk = "0.1.0-SNAPSHOT", mavenCentral snapshots repo
- app/build.gradle.kts: sdk-core, sdk-proto, sdk-transport-ble,
sdk-storage-sqldelight dependencies
Bootstrap:
- MeshUtilApplication: AndroidContextHolder.context set in onCreate()
before startKoin so SqlDelightStorageProvider can locate app files
- RadioClientProvider (@Single, binds SdkClientLifecycle): mutex-serialized
rebuildAndConnect(), strips 'x' prefix from BLE devAddr, holds
RadioClient StateFlow
- RadioClientViewModel: exposes RadioClientProvider to UI layer
SDK ViewModels (POC quality, compile-verified):
- SdkNodeListViewModel: NodeChange.Snapshot/Added/Updated/Removed → UiNode
- SdkMessagingViewModel: sendText() via client.sendText(), incomingText
via client.textMessages (Gap B — now fixed in SDK)
- SdkConfigViewModel: configBundle reads, setConfig/setOwner writes,
loadChannels() via admin, Gap G workaround (local override map)
- SdkTelemetryViewModel: TelemetryApi.observe(NodeId), requestDeviceMetrics
Service lifecycle:
- SdkClientLifecycle interface in core:service (avoids reverse dep from
service → app); RadioClientProvider implements it
- MeshService.onDestroy: calls sdkClientLifecycle.disconnect() before
serviceJob.cancel()
- BlePeripheralFactory.kt in core:ble: public buildPeripheralForAddress()
wrapper (Gap F workaround; proper fix needed in SDK transport-ble)
SDK gaps discovered and logged:
Gap B - textMessages flow (FIXED in SDK feat/meshtastic-android-integration-gaps)
Gap C - channels StateFlow (no reactive cache, only admin.listChannels())
Gap F - BleTransport MAC string factory (requires live Peripheral today)
Gap G - configBundle not refreshed after editSettings writes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>