refactor: narrow ViewModel injections, add ConnectionAware, delete dead code, integration tests

ViewModel Narrowing:
- V1: Created ConnectionAware interface; MessageSender, DeviceAdmin, DeviceControl extend it
- V2: Narrowed 6 ViewModels/actions to focused sub-interfaces (DeviceAdmin, MessageSender, DataRequester, DeviceControl)

Cleanup:
- C1: Deleted dead MeshDataMapper and its DI registration

Integration Tests:
- T2: SdkStateBridgeTest verifying WentOffline/CameOnline presence handling
- Verified Koin resolution, full test suite passes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
James Rich
2026-05-05 15:15:45 -05:00
parent e9cb439849
commit 27b2c19e69
18 changed files with 252 additions and 104 deletions

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.model
import kotlinx.coroutines.flow.StateFlow
/** Provides read-only access to the app's connection state. */
interface ConnectionAware {
val connectionState: StateFlow<ConnectionState>
}

View File

@@ -20,7 +20,7 @@ import org.meshtastic.proto.Channel
import org.meshtastic.proto.Config
/** Focused interface for local device configuration and edit sessions. */
interface DeviceAdmin {
interface DeviceAdmin : ConnectionAware {
suspend fun setLocalConfig(config: Config)
suspend fun setLocalChannel(channel: Channel)
suspend fun beginEditSettings(destNum: Int)

View File

@@ -17,7 +17,7 @@
package org.meshtastic.core.model
/** Focused interface for device lifecycle control. */
interface DeviceControl {
interface DeviceControl : ConnectionAware {
suspend fun reboot(destNum: Int, packetId: Int)
suspend fun rebootToDfu(nodeNum: Int)
suspend fun requestRebootOta(requestId: Int, destNum: Int, mode: Int, hash: ByteArray?)

View File

@@ -17,7 +17,7 @@
package org.meshtastic.core.model
/** Focused interface for sending messages over the mesh. */
interface MessageSender {
interface MessageSender : ConnectionAware {
suspend fun sendMessage(packet: DataPacket)
fun getPacketId(): Int
}

View File

@@ -26,18 +26,6 @@ import org.meshtastic.proto.ClientNotification
* This super-interface remains for backward compatibility with existing injections.
*/
interface RadioController : MessageSender, DeviceAdmin, RemoteAdmin, DeviceControl, DataRequester {
/**
* Canonical app-level connection state, delegated from [ServiceRepository][connectionState].
*
* This exposes the same single source of truth as `ServiceRepository.connectionState`, surfaced through the
* controller interface for convenience in feature modules and ViewModels that depend on [RadioController] rather
* than [ServiceRepository] directly.
*
* This is **not** the transport-level state — it reflects the fully reconciled app-level state including handshake
* progress and device sleep policy.
*/
val connectionState: StateFlow<ConnectionState>
/**
* Flow of notifications from the radio client.
*

View File

@@ -1,55 +0,0 @@
/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("MagicNumber")
package org.meshtastic.core.model.util
import okio.ByteString.Companion.toByteString
import org.meshtastic.core.model.DataPacket
import org.meshtastic.proto.MeshPacket
/**
* Utility class to map [MeshPacket] protobufs to [DataPacket] domain models.
*
* This class is platform-agnostic and can be used in shared logic.
*/
open class MeshDataMapper(private val nodeIdLookup: NodeIdLookup) {
/** Maps a [MeshPacket] to a [DataPacket], or returns null if the packet has no decoded data. */
open fun toDataPacket(packet: MeshPacket): DataPacket? {
val decoded = packet.decoded ?: return null
return DataPacket(
from = packet.from,
to = packet.to,
time = packet.rx_time * 1000L,
id = packet.id,
dataType = decoded.portnum.value,
bytes = decoded.payload.toByteArray().toByteString(),
hopLimit = packet.hop_limit,
channel = if (packet.pki_encrypted == true) DataPacket.PKC_CHANNEL_INDEX else packet.channel,
wantAck = packet.want_ack == true,
hopStart = packet.hop_start,
snr = packet.rx_snr,
rssi = packet.rx_rssi,
replyId = decoded.reply_id,
relayNode = packet.relay_node,
viaMqtt = packet.via_mqtt == true,
emoji = decoded.emoji,
transportMechanism = packet.transport_mechanism.value,
)
}
}