Meshtastic-Android/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt

366 lines
15 KiB
Kotlin
Raw Normal View History

2024-07-28 05:04:50 -05:00
@file:Suppress(
"LongMethod",
"MagicNumber",
"CyclomaticComplexMethod",
)
package com.geeksville.mesh.ui
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.repeatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
2024-07-28 05:04:50 -05:00
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
2024-07-28 05:04:50 -05:00
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
2024-07-28 05:04:50 -05:00
import androidx.compose.foundation.text.selection.DisableSelection
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.Chip
import androidx.compose.material.ChipDefaults
2024-07-28 05:04:50 -05:00
import androidx.compose.material.Divider
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
2024-07-28 05:04:50 -05:00
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
2024-07-28 05:04:50 -05:00
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
Add elevation and number of GPS satellites to node info (#895) * Move battery info to compose - always show voltage level and icons to match battery percentage Use tool text in preview, rather than actually set text value Simplify node info layout to avoid defining margins on everything * Move node position to Compose * Update hyperlink color to match previous value * Use compose preview in layout editor * Use compose preview in layout editor * Add simple preview for use in layout * Move last heard node info to Compose Clean up layout of node info * Move signal info to Compose and simplify bind * Prevent long coordinates from colliding with signal info * Move the rest of the node info card to compose Breaks the blinking feature when navigating from chat Wrap position to new line if overflow * Adjust layout and text sizing to closer match original * Use constraint layout for tighter display on busy nodes * Construct environment metrics so that there aren't trailing spaces if current is zero * Swap viewholder root for compose view rather than inflating layout Fix padding lost when changing out view holder root Intelligently update the list with only nodes that changed * Remove unused method, and adjust replacement method to match the same decimal precisions as before * Add elevation and number of GPS satellites to node info list Add some extension functions for easier conversion between units and systems * Dispose composition on recycle to avoid lingering spacing from previous layouts Remove comments explaning adapter functionality Remove unused methods * Use previous string for denoting unknown node names * Align properly if altitude but no signal info
2024-03-07 02:34:43 -07:00
import com.geeksville.mesh.ConfigProtos
2024-08-10 06:17:51 -05:00
import com.geeksville.mesh.ConfigProtos.Config.DeviceConfig
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig
2024-05-27 09:56:26 -03:00
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.R
import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.ui.components.NodeKeyStatusIcon
import com.geeksville.mesh.ui.components.SimpleAlertDialog
Add elevation and number of GPS satellites to node info (#895) * Move battery info to compose - always show voltage level and icons to match battery percentage Use tool text in preview, rather than actually set text value Simplify node info layout to avoid defining margins on everything * Move node position to Compose * Update hyperlink color to match previous value * Use compose preview in layout editor * Use compose preview in layout editor * Add simple preview for use in layout * Move last heard node info to Compose Clean up layout of node info * Move signal info to Compose and simplify bind * Prevent long coordinates from colliding with signal info * Move the rest of the node info card to compose Breaks the blinking feature when navigating from chat Wrap position to new line if overflow * Adjust layout and text sizing to closer match original * Use constraint layout for tighter display on busy nodes * Construct environment metrics so that there aren't trailing spaces if current is zero * Swap viewholder root for compose view rather than inflating layout Fix padding lost when changing out view holder root Intelligently update the list with only nodes that changed * Remove unused method, and adjust replacement method to match the same decimal precisions as before * Add elevation and number of GPS satellites to node info list Add some extension functions for easier conversion between units and systems * Dispose composition on recycle to avoid lingering spacing from previous layouts Remove comments explaning adapter functionality Remove unused methods * Use previous string for denoting unknown node names * Align properly if altitude but no signal info
2024-03-07 02:34:43 -07:00
import com.geeksville.mesh.ui.compose.ElevationInfo
import com.geeksville.mesh.ui.compose.SatelliteCountInfo
import com.geeksville.mesh.ui.preview.NodeEntityPreviewParameterProvider
import com.geeksville.mesh.ui.theme.AppTheme
Add elevation and number of GPS satellites to node info (#895) * Move battery info to compose - always show voltage level and icons to match battery percentage Use tool text in preview, rather than actually set text value Simplify node info layout to avoid defining margins on everything * Move node position to Compose * Update hyperlink color to match previous value * Use compose preview in layout editor * Use compose preview in layout editor * Add simple preview for use in layout * Move last heard node info to Compose Clean up layout of node info * Move signal info to Compose and simplify bind * Prevent long coordinates from colliding with signal info * Move the rest of the node info card to compose Breaks the blinking feature when navigating from chat Wrap position to new line if overflow * Adjust layout and text sizing to closer match original * Use constraint layout for tighter display on busy nodes * Construct environment metrics so that there aren't trailing spaces if current is zero * Swap viewholder root for compose view rather than inflating layout Fix padding lost when changing out view holder root Intelligently update the list with only nodes that changed * Remove unused method, and adjust replacement method to match the same decimal precisions as before * Add elevation and number of GPS satellites to node info list Add some extension functions for easier conversion between units and systems * Dispose composition on recycle to avoid lingering spacing from previous layouts Remove comments explaning adapter functionality Remove unused methods * Use previous string for denoting unknown node names * Align properly if altitude but no signal info
2024-03-07 02:34:43 -07:00
import com.geeksville.mesh.util.metersIn
import com.geeksville.mesh.util.toDistanceString
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun NodeItem(
thisNode: NodeEntity?,
thatNode: NodeEntity,
gpsFormat: Int,
distanceUnits: Int,
tempInFahrenheit: Boolean,
isIgnored: Boolean = false,
2024-07-28 05:04:50 -05:00
chipClicked: () -> Unit = {},
blinking: Boolean = false,
2024-07-28 05:04:50 -05:00
expanded: Boolean = false,
currentTimeMillis: Long,
) {
val isUnknownUser = thatNode.user.hwModel == MeshProtos.HardwareModel.UNSET
val unknownShortName = stringResource(id = R.string.unknown_node_short_name)
val longName = thatNode.user.longName.ifEmpty { stringResource(id = R.string.unknown_username) }
val isThisNode = thisNode?.num == thatNode.num
val distance = thisNode?.distance(thatNode)?.let {
val system = DisplayConfig.DisplayUnits.forNumber(distanceUnits)
if (it == 0) null else it.toDistanceString(system)
}
val (textColor, nodeColor) = thatNode.colors
val hwInfoString = thatNode.user.hwModel.let { hwModel ->
if (hwModel == MeshProtos.HardwareModel.UNSET) MeshProtos.HardwareModel.UNSET.name
else hwModel.name.replace('_', '-').replace('p', '.').lowercase()
}
val roleName = if (isUnknownUser) {
DeviceConfig.Role.UNRECOGNIZED.name
} else {
thatNode.user.role.name
}
val nodeId = thatNode.user.id.ifEmpty { "???" }
2024-07-28 05:04:50 -05:00
val highlight = Color(0x33FFFFFF)
val bgColor by animateColorAsState(
targetValue = if (blinking) highlight else Color.Transparent,
animationSpec = repeatable(
iterations = 6,
animation = tween(
durationMillis = 250,
easing = FastOutSlowInEasing
),
repeatMode = RepeatMode.Reverse
2024-07-28 05:04:50 -05:00
),
label = "blinking node"
)
val style = if (isUnknownUser) {
2024-07-28 05:04:50 -05:00
LocalTextStyle.current.copy(fontStyle = FontStyle.Italic)
} else {
LocalTextStyle.current
}
val (detailsShown, showDetails) = remember { mutableStateOf(expanded) }
var showEncryptionDialog by remember { mutableStateOf(false) }
if (showEncryptionDialog) {
val (title, text) = when {
thatNode.mismatchKey -> R.string.encryption_error to R.string.encryption_error_text
thatNode.hasPKC -> R.string.encryption_pkc to R.string.encryption_pkc_text
else -> R.string.encryption_psk to R.string.encryption_psk_text
}
SimpleAlertDialog(title, text) { showEncryptionDialog = false }
}
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 4.dp)
2024-07-28 05:04:50 -05:00
.defaultMinSize(minHeight = 80.dp),
onClick = { showDetails(!detailsShown) },
) {
Surface {
2024-07-28 05:04:50 -05:00
SelectionContainer {
Column(
modifier = Modifier
2024-07-28 05:04:50 -05:00
.fillMaxWidth()
.padding(8.dp)
.background(bgColor)
) {
2024-07-28 05:04:50 -05:00
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Chip(
modifier = Modifier
.width(IntrinsicSize.Min)
.defaultMinSize(minHeight = 32.dp, minWidth = 72.dp),
2024-07-28 05:04:50 -05:00
colors = ChipDefaults.chipColors(
backgroundColor = Color(nodeColor),
contentColor = Color(textColor)
),
onClick = { chipClicked() },
content = {
Text(
modifier = Modifier.fillMaxWidth(),
text = thatNode.user.shortName.ifEmpty { unknownShortName },
2024-07-28 05:04:50 -05:00
fontWeight = FontWeight.Normal,
fontSize = MaterialTheme.typography.button.fontSize,
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
textAlign = TextAlign.Center,
)
},
)
NodeKeyStatusIcon(
hasPKC = thatNode.hasPKC,
mismatchKey = thatNode.mismatchKey,
modifier = Modifier.size(32.dp)
) { showEncryptionDialog = true }
2024-07-28 05:04:50 -05:00
Text(
modifier = Modifier.weight(1f),
text = longName,
2024-07-28 05:04:50 -05:00
style = style,
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
softWrap = true,
)
LastHeardInfo(
lastHeard = thatNode.lastHeard,
currentTimeMillis = currentTimeMillis
2024-07-28 05:04:50 -05:00
)
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
if (distance != null) {
Text(
2024-07-28 05:04:50 -05:00
text = distance,
fontSize = MaterialTheme.typography.button.fontSize,
)
2024-07-28 05:04:50 -05:00
} else {
Spacer(modifier = Modifier.width(16.dp))
}
BatteryInfo(
batteryLevel = thatNode.batteryLevel,
voltage = thatNode.voltage
)
2024-07-28 05:04:50 -05:00
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
signalInfo(
node = thatNode,
2024-07-28 05:04:50 -05:00
isThisNode = isThisNode
)
thatNode.validPosition?.let { position ->
val satCount = position.satsInView
2024-07-28 05:04:50 -05:00
if (satCount > 0) {
SatelliteCountInfo(
satCount = satCount
)
Add elevation and number of GPS satellites to node info (#895) * Move battery info to compose - always show voltage level and icons to match battery percentage Use tool text in preview, rather than actually set text value Simplify node info layout to avoid defining margins on everything * Move node position to Compose * Update hyperlink color to match previous value * Use compose preview in layout editor * Use compose preview in layout editor * Add simple preview for use in layout * Move last heard node info to Compose Clean up layout of node info * Move signal info to Compose and simplify bind * Prevent long coordinates from colliding with signal info * Move the rest of the node info card to compose Breaks the blinking feature when navigating from chat Wrap position to new line if overflow * Adjust layout and text sizing to closer match original * Use constraint layout for tighter display on busy nodes * Construct environment metrics so that there aren't trailing spaces if current is zero * Swap viewholder root for compose view rather than inflating layout Fix padding lost when changing out view holder root Intelligently update the list with only nodes that changed * Remove unused method, and adjust replacement method to match the same decimal precisions as before * Add elevation and number of GPS satellites to node info list Add some extension functions for easier conversion between units and systems * Dispose composition on recycle to avoid lingering spacing from previous layouts Remove comments explaning adapter functionality Remove unused methods * Use previous string for denoting unknown node names * Align properly if altitude but no signal info
2024-03-07 02:34:43 -07:00
}
2024-07-28 05:04:50 -05:00
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
val telemetryString = thatNode.getTelemetryString(tempInFahrenheit)
if (telemetryString.isNotEmpty()) {
2024-07-28 05:04:50 -05:00
Text(
text = telemetryString,
2024-07-28 05:04:50 -05:00
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
Add elevation and number of GPS satellites to node info (#895) * Move battery info to compose - always show voltage level and icons to match battery percentage Use tool text in preview, rather than actually set text value Simplify node info layout to avoid defining margins on everything * Move node position to Compose * Update hyperlink color to match previous value * Use compose preview in layout editor * Use compose preview in layout editor * Add simple preview for use in layout * Move last heard node info to Compose Clean up layout of node info * Move signal info to Compose and simplify bind * Prevent long coordinates from colliding with signal info * Move the rest of the node info card to compose Breaks the blinking feature when navigating from chat Wrap position to new line if overflow * Adjust layout and text sizing to closer match original * Use constraint layout for tighter display on busy nodes * Construct environment metrics so that there aren't trailing spaces if current is zero * Swap viewholder root for compose view rather than inflating layout Fix padding lost when changing out view holder root Intelligently update the list with only nodes that changed * Remove unused method, and adjust replacement method to match the same decimal precisions as before * Add elevation and number of GPS satellites to node info list Add some extension functions for easier conversion between units and systems * Dispose composition on recycle to avoid lingering spacing from previous layouts Remove comments explaning adapter functionality Remove unused methods * Use previous string for denoting unknown node names * Align properly if altitude but no signal info
2024-03-07 02:34:43 -07:00
)
2024-07-28 05:04:50 -05:00
}
}
Add elevation and number of GPS satellites to node info (#895) * Move battery info to compose - always show voltage level and icons to match battery percentage Use tool text in preview, rather than actually set text value Simplify node info layout to avoid defining margins on everything * Move node position to Compose * Update hyperlink color to match previous value * Use compose preview in layout editor * Use compose preview in layout editor * Add simple preview for use in layout * Move last heard node info to Compose Clean up layout of node info * Move signal info to Compose and simplify bind * Prevent long coordinates from colliding with signal info * Move the rest of the node info card to compose Breaks the blinking feature when navigating from chat Wrap position to new line if overflow * Adjust layout and text sizing to closer match original * Use constraint layout for tighter display on busy nodes * Construct environment metrics so that there aren't trailing spaces if current is zero * Swap viewholder root for compose view rather than inflating layout Fix padding lost when changing out view holder root Intelligently update the list with only nodes that changed * Remove unused method, and adjust replacement method to match the same decimal precisions as before * Add elevation and number of GPS satellites to node info list Add some extension functions for easier conversion between units and systems * Dispose composition on recycle to avoid lingering spacing from previous layouts Remove comments explaning adapter functionality Remove unused methods * Use previous string for denoting unknown node names * Align properly if altitude but no signal info
2024-03-07 02:34:43 -07:00
2024-07-28 05:04:50 -05:00
if (detailsShown || expanded) {
Spacer(modifier = Modifier.height(8.dp))
Divider()
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
thatNode.validPosition?.let {
DisableSelection {
LinkedCoordinates(
latitude = thatNode.latitude,
longitude = thatNode.longitude,
format = gpsFormat,
nodeName = longName
)
}
Add elevation and number of GPS satellites to node info (#895) * Move battery info to compose - always show voltage level and icons to match battery percentage Use tool text in preview, rather than actually set text value Simplify node info layout to avoid defining margins on everything * Move node position to Compose * Update hyperlink color to match previous value * Use compose preview in layout editor * Use compose preview in layout editor * Add simple preview for use in layout * Move last heard node info to Compose Clean up layout of node info * Move signal info to Compose and simplify bind * Prevent long coordinates from colliding with signal info * Move the rest of the node info card to compose Breaks the blinking feature when navigating from chat Wrap position to new line if overflow * Adjust layout and text sizing to closer match original * Use constraint layout for tighter display on busy nodes * Construct environment metrics so that there aren't trailing spaces if current is zero * Swap viewholder root for compose view rather than inflating layout Fix padding lost when changing out view holder root Intelligently update the list with only nodes that changed * Remove unused method, and adjust replacement method to match the same decimal precisions as before * Add elevation and number of GPS satellites to node info list Add some extension functions for easier conversion between units and systems * Dispose composition on recycle to avoid lingering spacing from previous layouts Remove comments explaning adapter functionality Remove unused methods * Use previous string for denoting unknown node names * Align properly if altitude but no signal info
2024-03-07 02:34:43 -07:00
}
2024-07-28 05:04:50 -05:00
val system =
ConfigProtos.Config.DisplayConfig.DisplayUnits.forNumber(distanceUnits)
thatNode.validPosition?.let { position ->
2024-07-28 05:04:50 -05:00
val altitude = position.altitude.metersIn(system)
val elevationSuffix = stringResource(id = R.string.elevation_suffix)
ElevationInfo(
altitude = altitude,
system = system,
suffix = elevationSuffix
)
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
) {
Text(
modifier = Modifier.weight(1f),
text = hwInfoString,
fontSize = MaterialTheme.typography.button.fontSize,
style = style,
)
Text(
modifier = Modifier.weight(1f),
text = roleName,
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.button.fontSize,
style = style,
)
Text(
modifier = Modifier.weight(1f),
text = nodeId,
textAlign = TextAlign.End,
fontSize = MaterialTheme.typography.button.fontSize,
style = style,
)
2024-07-28 05:04:50 -05:00
}
}
}
}
}
}
}
@Composable
@Preview(showBackground = false)
fun NodeInfoSimplePreview() {
AppTheme {
val thisNode = NodeEntityPreviewParameterProvider().values.first()
val thatNode = NodeEntityPreviewParameterProvider().values.last()
NodeItem(
thisNode = thisNode,
thatNode = thatNode,
1,
0,
true,
currentTimeMillis = System.currentTimeMillis()
)
}
}
@Composable
@Preview(
showBackground = true,
uiMode = android.content.res.Configuration.UI_MODE_NIGHT_YES,
)
fun NodeInfoPreview(
@PreviewParameter(NodeEntityPreviewParameterProvider::class)
thatNode: NodeEntity
) {
AppTheme {
val thisNode = NodeEntityPreviewParameterProvider().values.first()
2024-07-28 05:04:50 -05:00
Column {
Text(
text = "Details Collapsed",
color = MaterialTheme.colors.onBackground
)
NodeItem(
thisNode = thisNode,
thatNode = thatNode,
2024-07-28 05:04:50 -05:00
gpsFormat = 0,
distanceUnits = 1,
tempInFahrenheit = true,
expanded = false,
currentTimeMillis = System.currentTimeMillis()
2024-07-28 05:04:50 -05:00
)
Text(
text = "Details Shown",
color = MaterialTheme.colors.onBackground
)
NodeItem(
thisNode = thisNode,
thatNode = thatNode,
2024-07-28 05:04:50 -05:00
gpsFormat = 0,
distanceUnits = 1,
tempInFahrenheit = true,
expanded = true,
currentTimeMillis = System.currentTimeMillis()
2024-07-28 05:04:50 -05:00
)
}
}
}