refactor(model): remove ConnectionState helper methods and fix updateStatusNotification return type (#5074)

This commit is contained in:
James Rich 2026-04-11 18:41:34 -05:00 committed by GitHub
parent 174315b21f
commit 62264b10c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 51 additions and 35 deletions

View file

@ -16,7 +16,6 @@
*/
package org.meshtastic.app.service
import android.app.Notification
import dev.mokkery.MockMode
import dev.mokkery.mock
import org.meshtastic.core.model.Node
@ -37,7 +36,7 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
override fun updateServiceStateNotification(
state: org.meshtastic.core.model.ConnectionState,
telemetry: Telemetry?,
): Notification = mock(MockMode.autofill)
) {}
override suspend fun updateMessageNotification(
contactKey: String,

View file

@ -347,11 +347,12 @@ class MeshConnectionManagerImpl(
updateStatusNotification(t)
}
override fun updateStatusNotification(telemetry: Telemetry?): Any =
override fun updateStatusNotification(telemetry: Telemetry?) {
serviceNotifications.updateServiceStateNotification(
serviceRepository.connectionState.value,
telemetry = telemetry,
)
}
companion object {
private const val DEVICE_SLEEP_TIMEOUT_SECONDS = 30

View file

@ -28,12 +28,4 @@ sealed class ConnectionState {
/** The device is in a light sleep state, and we are waiting for it to wake up and reconnect to us. */
data object DeviceSleep : ConnectionState()
fun isConnected() = this == Connected
fun isConnecting() = this == Connecting
fun isDisconnected() = this == Disconnected
fun isDeviceSleep() = this == DeviceSleep
}

View file

@ -35,6 +35,6 @@ interface MeshConnectionManager {
/** Updates the telemetry information for the local node. */
fun updateTelemetry(t: Telemetry)
/** Updates and returns the current status notification. */
fun updateStatusNotification(telemetry: Telemetry? = null): Any
/** Updates the current status notification. */
fun updateStatusNotification(telemetry: Telemetry? = null)
}

View file

@ -28,7 +28,7 @@ interface MeshServiceNotifications {
fun initChannels()
fun updateServiceStateNotification(state: org.meshtastic.core.model.ConnectionState, telemetry: Telemetry?): Any
fun updateServiceStateNotification(state: org.meshtastic.core.model.ConnectionState, telemetry: Telemetry?)
suspend fun updateMessageNotification(
contactKey: String,

View file

@ -42,6 +42,7 @@ import org.meshtastic.core.repository.CommandSender
import org.meshtastic.core.repository.MeshConnectionManager
import org.meshtastic.core.repository.MeshLocationManager
import org.meshtastic.core.repository.MeshRouter
import org.meshtastic.core.repository.MeshServiceNotifications
import org.meshtastic.core.repository.NodeManager
import org.meshtastic.core.repository.RadioInterfaceService
import org.meshtastic.core.repository.SERVICE_NOTIFY_ID
@ -67,6 +68,12 @@ class MeshService : Service() {
private val connectionManager: MeshConnectionManager by inject()
private val notifications: MeshServiceNotifications by inject()
/** Android-typed accessor for the foreground service notification. */
private val androidNotifications: MeshServiceNotificationsImpl
get() = notifications as MeshServiceNotificationsImpl
private val orchestrator: MeshServiceOrchestrator by inject()
private val router: MeshRouter by inject()
@ -130,7 +137,8 @@ class MeshService : Service() {
val a = radioInterfaceService.getDeviceAddress()
val wantForeground = a != null && a != "n"
val notification = connectionManager.updateStatusNotification() as android.app.Notification
connectionManager.updateStatusNotification()
val notification = androidNotifications.getServiceNotification()
val foregroundServiceType =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

View file

@ -288,13 +288,25 @@ class MeshServiceNotificationsImpl(
private var cachedLocalStats: LocalStats? = null
private var nextStatsUpdateMillis: Long = 0
private var cachedMessage: String? = null
private var cachedServiceNotification: Notification? = null
/**
* Returns the last-built service state notification, or builds a default one if none exists. This is used by
* [MeshService] for [android.app.Service.startForeground].
*/
fun getServiceNotification(): Notification = cachedServiceNotification
?: createServiceStateNotification(
name = getString(Res.string.meshtastic_app_name),
message = null,
nextUpdateAt = 0,
)
// region Public Notification Methods
@Suppress("CyclomaticComplexMethod", "NestedBlockDepth")
override fun updateServiceStateNotification(
state: org.meshtastic.core.model.ConnectionState,
telemetry: Telemetry?,
): Notification {
) {
val summaryString =
when (state) {
is org.meshtastic.core.model.ConnectionState.Connected ->
@ -357,8 +369,8 @@ class MeshServiceNotificationsImpl(
message = cachedMessage,
nextUpdateAt = nextStatsUpdateMillis,
)
cachedServiceNotification = notification
notificationManager.notify(SERVICE_NOTIFY_ID, notification)
return notification
}
override suspend fun updateMessageNotification(

View file

@ -31,7 +31,7 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
override fun updateServiceStateNotification(
state: org.meshtastic.core.model.ConnectionState,
telemetry: Telemetry?,
): Any = Any()
) {}
override suspend fun updateMessageNotification(
contactKey: String,

View file

@ -45,9 +45,8 @@ class DesktopMeshServiceNotifications(private val notificationManager: Notificat
override fun updateServiceStateNotification(
state: org.meshtastic.core.model.ConnectionState,
telemetry: Telemetry?,
): Any {
) {
// We don't have a foreground service on desktop
return Unit
}
override suspend fun updateMessageNotification(

View file

@ -114,7 +114,7 @@ class NoopMeshServiceNotifications : MeshServiceNotifications {
override fun updateServiceStateNotification(
state: org.meshtastic.core.model.ConnectionState,
telemetry: Telemetry?,
): Any = Unit
) {}
override suspend fun updateMessageNotification(
contactKey: String,

View file

@ -151,7 +151,7 @@ fun ConnectionsScreen(
MainAppBar(
title = stringResource(Res.string.connections),
ourNode = ourNode,
showNodeChip = ourNode != null && connectionState.isConnected(),
showNodeChip = ourNode != null && connectionState is ConnectionState.Connected,
canNavigateUp = false,
onNavigateUp = {},
actions = {},
@ -167,8 +167,8 @@ fun ConnectionsScreen(
Spacer(modifier = Modifier.height(4.dp))
val uiState =
when {
connectionState.isConnected() && ourNode != null -> 2
connectionState.isConnected() ||
connectionState is ConnectionState.Connected && ourNode != null -> 2
connectionState is ConnectionState.Connected ||
connectionState == ConnectionState.Connecting ||
selectedDevice != NO_DEVICE_SELECTED -> 1

View file

@ -51,7 +51,7 @@ fun ConnectingDeviceInfo(
modifier: Modifier = Modifier,
) {
val statusText =
if (connectionState.isConnected()) {
if (connectionState is ConnectionState.Connected) {
stringResource(Res.string.connected)
} else {
stringResource(Res.string.connecting)

View file

@ -90,9 +90,9 @@ fun DeviceListItem(
val icon =
when (device) {
is DeviceListEntry.Ble ->
if (connectionState.isConnected()) {
if (connectionState is ConnectionState.Connected) {
MeshtasticIcons.BluetoothConnected
} else if (connectionState.isConnecting()) {
} else if (connectionState is ConnectionState.Connecting) {
MeshtasticIcons.BluetoothSearching
} else {
MeshtasticIcons.Bluetooth
@ -132,7 +132,7 @@ fun DeviceListItem(
contentDescription = contentDescription,
modifier = Modifier.size(32.dp),
tint =
if (connectionState.isConnected()) {
if (connectionState is ConnectionState.Connected) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
@ -146,10 +146,10 @@ fun DeviceListItem(
Rssi(rssi = displayedRssi)
}
if (connectionState.isConnecting()) {
if (connectionState is ConnectionState.Connecting) {
CircularProgressIndicator(modifier = Modifier.size(32.dp))
} else {
RadioButton(selected = connectionState.isConnected(), onClick = null)
RadioButton(selected = connectionState is ConnectionState.Connected, onClick = null)
}
}
},

View file

@ -65,6 +65,7 @@ import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.common.util.HomoglyphCharacterStringTransformer
import org.meshtastic.core.database.entity.QuickChatAction
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.model.Node
import org.meshtastic.core.model.util.getChannel
@ -327,7 +328,7 @@ fun MessageScreen(
Column {
AnimatedVisibility(visible = showQuickChat) {
QuickChatRow(
enabled = connectionState.isConnected(),
enabled = connectionState is ConnectionState.Connected,
actions = quickChatActions,
onClick = { action ->
handleQuickChatAction(
@ -344,7 +345,7 @@ fun MessageScreen(
ourNode = ourNode,
)
MessageInput(
isEnabled = connectionState.isConnected(),
isEnabled = connectionState is ConnectionState.Connected,
isHomoglyphEncodingEnabled = homoglyphEncodingEnabled,
textFieldState = messageInputState,
onSendMessage = {

View file

@ -64,6 +64,7 @@ import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.common.util.MeshtasticUri
import org.meshtastic.core.common.util.NumberFormatter
import org.meshtastic.core.common.util.nowMillis
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.model.Contact
import org.meshtastic.core.model.ContactSettings
import org.meshtastic.core.model.util.TimeConstants
@ -232,7 +233,7 @@ fun ContactsScreen(
MainAppBar(
title = stringResource(Res.string.conversations),
ourNode = ourNode,
showNodeChip = ourNode != null && connectionState.isConnected(),
showNodeChip = ourNode != null && connectionState is ConnectionState.Connected,
canNavigateUp = false,
onNavigateUp = {},
actions = {
@ -250,7 +251,7 @@ fun ContactsScreen(
)
},
floatingActionButton = {
if (connectionState.isConnected()) {
if (connectionState is ConnectionState.Connected) {
MeshtasticImportFAB(
sharedContact = sharedContactRequested,
onImport = { uriString ->

View file

@ -41,6 +41,7 @@ import org.meshtastic.core.domain.usecase.settings.SetMeshLogSettingsUseCase
import org.meshtastic.core.domain.usecase.settings.SetNotificationSettingsUseCase
import org.meshtastic.core.domain.usecase.settings.SetProvideLocationUseCase
import org.meshtastic.core.domain.usecase.settings.SetThemeUseCase
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.model.MyNodeInfo
import org.meshtastic.core.model.Node
import org.meshtastic.core.model.RadioController
@ -84,7 +85,9 @@ class SettingsViewModel(
val ourNodeInfo: StateFlow<Node?> = nodeRepository.ourNodeInfo
val isConnected =
radioController.connectionState.map { it.isConnected() }.stateInWhileSubscribed(initialValue = false)
radioController.connectionState
.map { it is ConnectionState.Connected }
.stateInWhileSubscribed(initialValue = false)
val localConfig: StateFlow<LocalConfig> =
radioConfigRepository.localConfigFlow.stateInWhileSubscribed(initialValue = LocalConfig())