From cfdb245da26c601e8dc37291857f1ab1990e696e Mon Sep 17 00:00:00 2001 From: andrekir Date: Tue, 12 Nov 2024 09:08:08 -0300 Subject: [PATCH] refactor: convert `connectionState` to StateFlow --- .../java/com/geeksville/mesh/MainActivity.kt | 5 ++- .../java/com/geeksville/mesh/model/UIState.kt | 2 +- .../geeksville/mesh/service/MeshService.kt | 1 + .../com/geeksville/mesh/ui/ChannelFragment.kt | 11 +++--- .../geeksville/mesh/ui/MessagesFragment.kt | 2 +- .../java/com/geeksville/mesh/ui/NodeItem.kt | 39 ++++++------------- .../geeksville/mesh/ui/SettingsFragment.kt | 2 +- .../com/geeksville/mesh/ui/UsersFragment.kt | 5 +-- 8 files changed, 25 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index dcdea088b..9294629b2 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -27,6 +27,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction +import androidx.lifecycle.asLiveData import androidx.viewpager2.adapter.FragmentStateAdapter import com.geeksville.mesh.android.* import com.geeksville.mesh.concurrent.handledLaunch @@ -541,7 +542,7 @@ class MainActivity : AppCompatActivity(), Logging { override fun onStart() { super.onStart() - model.connectionState.observe(this) { state -> + model.connectionState.asLiveData().observe(this) { state -> onMeshConnectionChanged(state) updateConnectionStatusImage(state) } @@ -619,7 +620,7 @@ class MainActivity : AppCompatActivity(), Logging { menuInflater.inflate(R.menu.menu_main, menu) model.actionBarMenu = menu - updateConnectionStatusImage(model.connectionState.value!!) + updateConnectionStatusImage(model.connectionState.value) return true } diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index ddb861c4c..4341c5bd1 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -465,7 +465,7 @@ class UIViewModel @Inject constructor( } // Connection state to our radio device - val connectionState get() = radioConfigRepository.connectionState.asLiveData() + val connectionState get() = radioConfigRepository.connectionState fun isConnected() = connectionState.value != MeshService.ConnectionState.DISCONNECTED private val _requestChannelUrl = MutableLiveData(null) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 898ef4bdb..d1bee8d84 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -140,6 +140,7 @@ class MeshService : Service(), Logging { DEVICE_SLEEP, // device is in LS sleep state, it will reconnected to us over bluetooth once it has data ; + fun isConnected() = this == CONNECTED fun isDisconnected() = this == DISCONNECTED } diff --git a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt index d8acd863d..6ff8ac25d 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.ButtonDefaults import androidx.compose.material.ContentAlpha import androidx.compose.material.Icon import androidx.compose.material.IconButton @@ -29,11 +30,9 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.twotone.Check import androidx.compose.material.icons.twotone.Close -import androidx.compose.material.ButtonDefaults import androidx.compose.material.icons.twotone.ContentCopy import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -63,14 +62,14 @@ import androidx.fragment.app.activityViewModels import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.geeksville.mesh.AppOnlyProtos.ChannelSet -import com.geeksville.mesh.analytics.DataPair -import com.geeksville.mesh.android.GeeksvilleApplication -import com.geeksville.mesh.android.Logging import com.geeksville.mesh.ChannelProtos import com.geeksville.mesh.ConfigProtos import com.geeksville.mesh.R +import com.geeksville.mesh.analytics.DataPair import com.geeksville.mesh.android.BuildUtils.debug import com.geeksville.mesh.android.BuildUtils.errormsg +import com.geeksville.mesh.android.GeeksvilleApplication +import com.geeksville.mesh.android.Logging import com.geeksville.mesh.android.getCameraPermissions import com.geeksville.mesh.android.hasCameraPermission import com.geeksville.mesh.channelSet @@ -129,7 +128,7 @@ fun ChannelScreen( val focusManager = LocalFocusManager.current val clipboardManager = LocalClipboardManager.current - val connectionState by viewModel.connectionState.observeAsState() + val connectionState by viewModel.connectionState.collectAsStateWithLifecycle() val enabled = connectionState == MeshService.ConnectionState.CONNECTED && !viewModel.isManaged val channels by viewModel.channels.collectAsStateWithLifecycle() diff --git a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt index 8e4843a06..704966499 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -161,7 +161,7 @@ class MessagesFragment : Fragment(), Logging { } // If connection state _OR_ myID changes we have to fix our ability to edit outgoing messages - model.connectionState.observe(viewLifecycleOwner) { + model.connectionState.asLiveData().observe(viewLifecycleOwner) { // If we don't know our node ID and we are offline don't let user try to send val isConnected = model.isConnected() binding.textInputLayout.isEnabled = isConnected diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt index 4ad140c47..402b9d6a7 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt @@ -1,9 +1,3 @@ -@file:Suppress( - "LongMethod", - "MagicNumber", - "CyclomaticComplexMethod", -) - package com.geeksville.mesh.ui import androidx.compose.animation.animateColorAsState @@ -58,8 +52,6 @@ import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig import com.geeksville.mesh.MeshProtos import com.geeksville.mesh.R import com.geeksville.mesh.database.entity.NodeEntity -import com.geeksville.mesh.service.MeshService -import com.geeksville.mesh.service.MeshService.ConnectionState import com.geeksville.mesh.ui.components.MenuItemAction import com.geeksville.mesh.ui.components.NodeKeyStatusIcon import com.geeksville.mesh.ui.components.NodeMenu @@ -71,6 +63,7 @@ import com.geeksville.mesh.ui.theme.AppTheme import com.geeksville.mesh.util.metersIn import com.geeksville.mesh.util.toDistanceString +@Suppress("LongMethod", "CyclomaticComplexMethod") @OptIn(ExperimentalMaterialApi::class) @Composable fun NodeItem( @@ -84,10 +77,9 @@ fun NodeItem( blinking: Boolean = false, expanded: Boolean = false, currentTimeMillis: Long, - connectionState: MeshService.ConnectionState? = ConnectionState.DISCONNECTED, + isConnected: Boolean = false, ) { - val isUnknownUser = thatNode.isUnknownUser - val unknownShortName = stringResource(id = R.string.unknown_node_short_name) + val isIgnored = ignoreIncomingList.contains(thatNode.num) val longName = thatNode.user.longName.ifEmpty { stringResource(id = R.string.unknown_username) } val isThisNode = thisNode?.num == thatNode.num @@ -101,28 +93,23 @@ fun NodeItem( MeshProtos.HardwareModel.UNSET -> MeshProtos.HardwareModel.UNSET.name else -> hwModel.name.replace('_', '-').replace('p', '.').lowercase() } - val roleName = if (isUnknownUser) { + val roleName = if (thatNode.isUnknownUser) { DeviceConfig.Role.UNRECOGNIZED.name } else { thatNode.user.role.name } - val nodeId = thatNode.user.id.ifEmpty { "???" } - val highlight = Color(0x33FFFFFF) val bgColor by animateColorAsState( - targetValue = if (blinking) highlight else Color.Transparent, + targetValue = if (blinking) Color(color = 0x33FFFFFF) else Color.Transparent, animationSpec = repeatable( iterations = 6, - animation = tween( - durationMillis = 250, - easing = FastOutSlowInEasing - ), + animation = tween(durationMillis = 250, easing = FastOutSlowInEasing), repeatMode = RepeatMode.Reverse ), label = "blinking node" ) - val style = if (isUnknownUser) { + val style = if (thatNode.isUnknownUser) { LocalTextStyle.current.copy(fontStyle = FontStyle.Italic) } else { LocalTextStyle.current @@ -179,7 +166,7 @@ fun NodeItem( content = { Text( modifier = Modifier.fillMaxWidth(), - text = thatNode.user.shortName.ifEmpty { unknownShortName }, + text = thatNode.user.shortName.ifEmpty { "???" }, fontWeight = FontWeight.Normal, fontSize = MaterialTheme.typography.button.fontSize, textDecoration = TextDecoration.LineThrough.takeIf { @@ -196,7 +183,7 @@ fun NodeItem( onMenuItemAction = menuItemActionClicked, expanded = menuExpanded, onDismissRequest = { menuExpanded = false }, - isConnected = connectionState == ConnectionState.CONNECTED, + isConnected = isConnected, ) } NodeKeyStatusIcon( @@ -208,11 +195,7 @@ fun NodeItem( modifier = Modifier.weight(1f), text = longName, style = style, - textDecoration = TextDecoration.LineThrough.takeIf { - ignoreIncomingList.contains( - thatNode.num - ) - }, + textDecoration = TextDecoration.LineThrough.takeIf { isIgnored }, softWrap = true, ) @@ -322,7 +305,7 @@ fun NodeItem( ) Text( modifier = Modifier.weight(1f), - text = nodeId, + text = thatNode.user.id.ifEmpty { "???" }, textAlign = TextAlign.End, fontSize = MaterialTheme.typography.button.fontSize, style = style, diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index 22a7186f7..eba5c78f7 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -183,7 +183,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { } // Only let user edit their name or set software update while connected to a radio - model.connectionState.observe(viewLifecycleOwner) { + model.connectionState.asLiveData().observe(viewLifecycleOwner) { updateNodeInfo() } diff --git a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt index efd1be58c..cc791980d 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy @@ -96,7 +95,7 @@ fun NodesScreen( } val currentTimeMillis = rememberTimeTickWithLifecycle() - val connectionState by model.connectionState.observeAsState() + val connectionState by model.connectionState.collectAsStateWithLifecycle() LazyColumn( state = listState, @@ -140,7 +139,7 @@ fun NodesScreen( blinking = node == focusedNode, expanded = state.showDetails, currentTimeMillis = currentTimeMillis, - connectionState = connectionState, + isConnected = connectionState.isConnected(), ) } }