From d04378a0268a0f7af58c4892f43779867ebf566c Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Thu, 22 May 2025 21:39:46 -0500 Subject: [PATCH] feat: filter visibility, add unmessageable toggle (#1915) --- .../geeksville/mesh/service/MeshService.kt | 12 +++++++++-- .../java/com/geeksville/mesh/ui/NodeItem.kt | 1 - .../java/com/geeksville/mesh/ui/NodeScreen.kt | 10 ++++++++- .../geeksville/mesh/ui/components/NodeMenu.kt | 21 +++++++------------ .../mesh/ui/message/components/MessageList.kt | 5 ----- .../components/UserConfigItemList.kt | 10 +++++++++ 6 files changed, 37 insertions(+), 22 deletions(-) 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 1cb9e727b..d1253b52d 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -1828,10 +1828,18 @@ class MeshService : Service(), Logging { val dest = nodeDBbyID[id] ?: throw Exception("Can't set user without a NodeInfo") // this shouldn't happen val old = dest.user - if (longName == old.longName && shortName == old.shortName && isLicensed == old.isLicensed) { + + @Suppress("ComplexCondition") + if ( + user == old + ) { debug("Ignoring nop owner change") } else { - debug("setOwner Id: $id longName: ${longName.anonymize} shortName: $shortName isLicensed: $isLicensed") + debug( + "setOwner Id: $id longName: ${longName.anonymize}" + + " shortName: $shortName isLicensed: $isLicensed" + + " isUnmessagable: $isUnmessagable" + ) // Also update our own map for our nodeNum, by handling the packet just like packets from other users handleReceivedUser(dest.num, user) 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 b470ede91..f1f694bc3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt @@ -168,7 +168,6 @@ fun NodeItem( onAction = onAction, expanded = menuExpanded, onDismissRequest = { menuExpanded = false }, - firmwareVersion = thisNode?.metadata?.firmwareVersion ) } NodeKeyStatusIcon( diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt index 37a248cec..b06b9d331 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt @@ -20,6 +20,7 @@ package com.geeksville.mesh.ui import android.os.Build import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -36,6 +37,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -82,10 +84,15 @@ fun NodeScreen( modifier = Modifier.fillMaxSize(), ) { stickyHeader { + val animatedAlpha by animateFloatAsState( + targetValue = if (!listState.isScrollInProgress) 1.0f else 0f, + label = "alpha" + ) NodeFilterTextField( modifier = Modifier .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) + .background(MaterialTheme.colorScheme.surfaceDim.copy(alpha = animatedAlpha)) + .graphicsLayer(alpha = animatedAlpha) .padding(8.dp), filterText = state.filter, onTextChange = model::setNodeFilterText, @@ -139,6 +146,7 @@ fun NodeScreen( modifier = Modifier.align(androidx.compose.ui.Alignment.BottomEnd), visible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !listState.isScrollInProgress && + connectionState.isConnected() && shareCapable ) { @Suppress("NewApi") diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt index 76f4933e0..5006357b9 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt @@ -35,10 +35,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.geeksville.mesh.R -import com.geeksville.mesh.model.DeviceVersion import com.geeksville.mesh.model.Node import com.geeksville.mesh.model.isUnmessageableRole -import com.geeksville.mesh.ui.supportsQrCodeSharing @Suppress("LongMethod") @Composable @@ -48,7 +46,6 @@ fun NodeMenu( onDismissRequest: () -> Unit, expanded: Boolean = false, onAction: (NodeMenuAction) -> Unit, - firmwareVersion: String? = null, ) { val isUnmessageable = if (node.user.hasIsUnmessagable()) { node.user.isUnmessagable @@ -191,16 +188,14 @@ fun NodeMenu( ) HorizontalDivider(Modifier.padding(vertical = 8.dp)) } - val firmware = DeviceVersion(firmwareVersion ?: "0.0.0") - if (firmware.supportsQrCodeSharing()) { - DropdownMenuItem( - onClick = { - onDismissRequest() - onAction(NodeMenuAction.Share(node)) - }, - text = { Text(stringResource(R.string.share_contact)) } - ) - } + DropdownMenuItem( + onClick = { + onDismissRequest() + onAction(NodeMenuAction.Share(node)) + }, + text = { Text(stringResource(R.string.share_contact)) } + ) + DropdownMenuItem( onClick = { onDismissRequest() diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt index dbf736d56..4a94c9bb5 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt @@ -35,7 +35,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -156,9 +155,6 @@ internal fun MessageList( value += uuid } - val ourNode by viewModel.ourNodeInfo.collectAsState() - val firmwareVersion = ourNode?.metadata?.firmwareVersion - LazyColumn( modifier = modifier.fillMaxSize(), state = listState, @@ -196,7 +192,6 @@ internal fun MessageList( onDismissRequest = { expandedNodeMenu = false }, expanded = expandedNodeMenu, onAction = onNodeMenuAction, - firmwareVersion = firmwareVersion ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt index bccf0e2aa..bbd70fb2d 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt @@ -130,6 +130,16 @@ fun UserConfigItemList( } item { HorizontalDivider() } + item { + SwitchPreference( + title = stringResource(R.string.unmessageable), + summary = stringResource(R.string.unmonitored_or_infrastructure), + checked = userInput.isUnmessagable, + enabled = userInput.hasIsUnmessagable(), + onCheckedChange = { userInput = userInput.copy { isUnmessagable = it } } + ) + } + item { SwitchPreference( title = stringResource(R.string.licensed_amateur_radio),