mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor(colors): consolidate colors in UI components (#2520)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
c61d31c3b8
commit
6fd444c077
14 changed files with 1028 additions and 1386 deletions
|
|
@ -89,6 +89,7 @@ import androidx.compose.material3.HorizontalDivider
|
|||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||
import androidx.compose.material3.Switch
|
||||
|
|
@ -142,9 +143,10 @@ import com.geeksville.mesh.service.ServiceAction
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.preview.NodePreviewParameterProvider
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.common.theme.Green
|
||||
import com.geeksville.mesh.ui.common.theme.Orange
|
||||
import com.geeksville.mesh.ui.common.theme.Yellow
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusOrange
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import com.geeksville.mesh.ui.node.components.NodeActionDialogs
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
import com.geeksville.mesh.ui.radioconfig.NavCard
|
||||
|
|
@ -188,23 +190,24 @@ fun NodeDetailScreen(
|
|||
val lastTracerouteTime by uiViewModel.lastTraceRouteTime.collectAsStateWithLifecycle()
|
||||
val ourNode by uiViewModel.ourNodeInfo.collectAsStateWithLifecycle()
|
||||
|
||||
val availableLogs by remember(state, environmentState) {
|
||||
derivedStateOf {
|
||||
buildSet {
|
||||
if (state.hasDeviceMetrics()) add(LogsType.DEVICE)
|
||||
if (state.hasPositionLogs()) {
|
||||
add(LogsType.NODE_MAP)
|
||||
add(LogsType.POSITIONS)
|
||||
val availableLogs by
|
||||
remember(state, environmentState) {
|
||||
derivedStateOf {
|
||||
buildSet {
|
||||
if (state.hasDeviceMetrics()) add(LogsType.DEVICE)
|
||||
if (state.hasPositionLogs()) {
|
||||
add(LogsType.NODE_MAP)
|
||||
add(LogsType.POSITIONS)
|
||||
}
|
||||
if (environmentState.hasEnvironmentMetrics()) add(LogsType.ENVIRONMENT)
|
||||
if (state.hasSignalMetrics()) add(LogsType.SIGNAL)
|
||||
if (state.hasPowerMetrics()) add(LogsType.POWER)
|
||||
if (state.hasTracerouteLogs()) add(LogsType.TRACEROUTE)
|
||||
if (state.hasHostMetrics()) add(LogsType.HOST)
|
||||
if (state.hasPaxMetrics()) add(LogsType.PAX)
|
||||
}
|
||||
if (environmentState.hasEnvironmentMetrics()) add(LogsType.ENVIRONMENT)
|
||||
if (state.hasSignalMetrics()) add(LogsType.SIGNAL)
|
||||
if (state.hasPowerMetrics()) add(LogsType.POWER)
|
||||
if (state.hasTracerouteLogs()) add(LogsType.TRACEROUTE)
|
||||
if (state.hasHostMetrics()) add(LogsType.HOST)
|
||||
if (state.hasPaxMetrics()) add(LogsType.PAX)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val node = state.node
|
||||
if (node != null) {
|
||||
|
|
@ -229,9 +232,7 @@ fun NodeDetailScreen(
|
|||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -269,17 +270,21 @@ private fun handleNodeAction(
|
|||
|
||||
sealed interface NodeDetailAction {
|
||||
data class Navigate(val route: Route) : NodeDetailAction
|
||||
|
||||
data class TriggerServiceAction(val action: ServiceAction) : NodeDetailAction
|
||||
|
||||
data class HandleNodeMenuAction(val action: NodeMenuAction) : NodeDetailAction
|
||||
|
||||
data object ShareContact : NodeDetailAction
|
||||
}
|
||||
|
||||
val Node.isEffectivelyUnmessageable: Boolean
|
||||
get() = if (user.hasIsUnmessagable()) {
|
||||
user.isUnmessagable
|
||||
} else {
|
||||
user.role?.isUnmessageableRole() == true
|
||||
}
|
||||
get() =
|
||||
if (user.hasIsUnmessagable()) {
|
||||
user.isUnmessagable
|
||||
} else {
|
||||
user.role?.isUnmessageableRole() == true
|
||||
}
|
||||
|
||||
private enum class LogsType(@StringRes val titleRes: Int, val icon: ImageVector, val route: Route) {
|
||||
DEVICE(R.string.device_metrics_log, Icons.Default.ChargingStation, NodeDetailRoutes.DeviceMetrics),
|
||||
|
|
@ -343,19 +348,14 @@ private fun NodeDetailList(
|
|||
|
||||
if (showFirmwareSheet) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false)
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { showFirmwareSheet = false },
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
ModalBottomSheet(onDismissRequest = { showFirmwareSheet = false }, sheetState = sheetState) {
|
||||
selectedFirmware?.let { FirmwareReleaseSheetContent(firmwareRelease = it) }
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = modifier.fillMaxSize().padding(16.dp).verticalScroll(rememberScrollState())) {
|
||||
if (metricsState.deviceHardware != null) {
|
||||
PreferenceCategory(stringResource(R.string.device)) {
|
||||
DeviceDetailsContent(metricsState)
|
||||
}
|
||||
PreferenceCategory(stringResource(R.string.device)) { DeviceDetailsContent(metricsState) }
|
||||
}
|
||||
PreferenceCategory(stringResource(R.string.details)) {
|
||||
NodeDetailsContent(node, ourNode, metricsState.displayUnits)
|
||||
|
|
@ -406,11 +406,7 @@ private fun MetricsSection(
|
|||
PreferenceCategory(stringResource(id = R.string.logs)) {
|
||||
LogsType.entries.forEach { type ->
|
||||
if (availableLogs.contains(type)) {
|
||||
NavCard(
|
||||
title = stringResource(type.titleRes),
|
||||
icon = type.icon,
|
||||
enabled = true,
|
||||
) {
|
||||
NavCard(title = stringResource(type.titleRes), icon = type.icon, enabled = true) {
|
||||
onAction(NodeDetailAction.Navigate(type.route))
|
||||
}
|
||||
}
|
||||
|
|
@ -461,14 +457,14 @@ private fun AdministrationSection(
|
|||
label = stringResource(R.string.latest_stable_firmware),
|
||||
icon = Icons.Default.Memory,
|
||||
value = latestStable.id.substringBeforeLast(".").replace("v", ""),
|
||||
iconTint = Green,
|
||||
iconTint = colorScheme.StatusGreen,
|
||||
onClick = { onFirmwareSelected(latestStable) },
|
||||
)
|
||||
NodeDetailRow(
|
||||
label = stringResource(R.string.latest_alpha_firmware),
|
||||
icon = Icons.Default.Memory,
|
||||
value = latestAlpha.id.substringBeforeLast(".").replace("v", ""),
|
||||
iconTint = Yellow,
|
||||
iconTint = colorScheme.StatusYellow,
|
||||
onClick = { onFirmwareSelected(latestAlpha) },
|
||||
)
|
||||
}
|
||||
|
|
@ -483,11 +479,11 @@ private fun DeviceVersion.determineFirmwareStatusColor(
|
|||
val stableVersion = latestStable.asDeviceVersion()
|
||||
val alphaVersion = latestAlpha.asDeviceVersion()
|
||||
return when {
|
||||
this < stableVersion -> MaterialTheme.colorScheme.error
|
||||
this == stableVersion -> Green
|
||||
this in stableVersion..alphaVersion -> Yellow
|
||||
this > alphaVersion -> Orange
|
||||
else -> MaterialTheme.colorScheme.onSurface
|
||||
this < stableVersion -> colorScheme.StatusRed
|
||||
this == stableVersion -> colorScheme.StatusGreen
|
||||
this in stableVersion..alphaVersion -> colorScheme.StatusYellow
|
||||
this > alphaVersion -> colorScheme.StatusOrange
|
||||
else -> colorScheme.onSurface
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -495,19 +491,13 @@ private fun DeviceVersion.determineFirmwareStatusColor(
|
|||
private fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease) {
|
||||
val context = LocalContext.current
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()).padding(16.dp).fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(text = firmwareRelease.title, style = MaterialTheme.typography.titleLarge)
|
||||
Text(text = "Version: ${firmwareRelease.id}", style = MaterialTheme.typography.bodyMedium)
|
||||
Markdown(modifier = Modifier.padding(8.dp), content = firmwareRelease.releaseNotes)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_VIEW, firmwareRelease.pageUrl.toUri())
|
||||
|
|
@ -515,10 +505,7 @@ private fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease) {
|
|||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Link,
|
||||
contentDescription = stringResource(id = R.string.view_release),
|
||||
)
|
||||
Icon(imageVector = Icons.Default.Link, contentDescription = stringResource(id = R.string.view_release))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(id = R.string.view_release))
|
||||
}
|
||||
|
|
@ -529,10 +516,7 @@ private fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease) {
|
|||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
contentDescription = stringResource(id = R.string.download),
|
||||
)
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = stringResource(id = R.string.download))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(id = R.string.download))
|
||||
}
|
||||
|
|
@ -550,12 +534,8 @@ private fun NodeDetailRow(
|
|||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.thenIf(onClick != null) {
|
||||
clickable(onClick = onClick!!)
|
||||
}
|
||||
.padding(vertical = 8.dp),
|
||||
modifier =
|
||||
modifier.fillMaxWidth().thenIf(onClick != null) { clickable(onClick = onClick!!) }.padding(vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
|
|
@ -624,11 +604,7 @@ private fun DeviceActions(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun RemoteDeviceActions(
|
||||
node: Node,
|
||||
lastTracerouteTime: Long?,
|
||||
onAction: (NodeDetailAction) -> Unit,
|
||||
) {
|
||||
private fun RemoteDeviceActions(node: Node, lastTracerouteTime: Long?, onAction: (NodeDetailAction) -> Unit) {
|
||||
if (!node.isEffectivelyUnmessageable) {
|
||||
NodeActionButton(
|
||||
title = stringResource(id = R.string.direct_message),
|
||||
|
|
@ -663,8 +639,8 @@ private fun DeviceDetailsContent(state: MetricsState) {
|
|||
val hwModelName = deviceHardware.displayName
|
||||
val isSupported = deviceHardware.activelySupported
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(100.dp)
|
||||
modifier =
|
||||
Modifier.size(100.dp)
|
||||
.padding(4.dp)
|
||||
.clip(CircleShape)
|
||||
.background(color = Color(node.colors.second).copy(alpha = .5f), shape = CircleShape),
|
||||
|
|
@ -674,14 +650,15 @@ private fun DeviceDetailsContent(state: MetricsState) {
|
|||
}
|
||||
NodeDetailRow(label = stringResource(R.string.hardware), icon = Icons.Default.Router, value = hwModelName)
|
||||
NodeDetailRow(
|
||||
label = if (isSupported) {
|
||||
label =
|
||||
if (isSupported) {
|
||||
stringResource(R.string.supported)
|
||||
} else {
|
||||
stringResource(R.string.supported_by_community)
|
||||
},
|
||||
icon = if (isSupported) Icons.TwoTone.Verified else ImageVector.vectorResource(R.drawable.unverified),
|
||||
value = "",
|
||||
iconTint = if (isSupported) Color.Green else Color.Red,
|
||||
iconTint = if (isSupported) colorScheme.StatusGreen else colorScheme.StatusRed,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -737,11 +714,7 @@ private fun EncryptionErrorContent() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun MainNodeDetails(
|
||||
node: Node,
|
||||
ourNode: Node?,
|
||||
displayUnits: ConfigProtos.Config.DisplayConfig.DisplayUnits,
|
||||
) {
|
||||
private fun MainNodeDetails(node: Node, ourNode: Node?, displayUnits: ConfigProtos.Config.DisplayConfig.DisplayUnits) {
|
||||
NodeDetailRow(
|
||||
label = stringResource(R.string.long_name),
|
||||
icon = Icons.TwoTone.Person,
|
||||
|
|
@ -757,16 +730,8 @@ private fun MainNodeDetails(
|
|||
icon = Icons.Default.Numbers,
|
||||
value = node.num.toUInt().toString(),
|
||||
)
|
||||
NodeDetailRow(
|
||||
label = stringResource(R.string.user_id),
|
||||
icon = Icons.Default.Person,
|
||||
value = node.user.id,
|
||||
)
|
||||
NodeDetailRow(
|
||||
label = stringResource(R.string.role),
|
||||
icon = Icons.Default.Work,
|
||||
value = node.user.role.name,
|
||||
)
|
||||
NodeDetailRow(label = stringResource(R.string.user_id), icon = Icons.Default.Person, value = node.user.id)
|
||||
NodeDetailRow(label = stringResource(R.string.role), icon = Icons.Default.Work, value = node.user.role.name)
|
||||
if (node.isEffectivelyUnmessageable) {
|
||||
NodeDetailRow(
|
||||
label = stringResource(R.string.unmonitored_or_infrastructure),
|
||||
|
|
@ -804,25 +769,12 @@ private fun MainNodeDetails(
|
|||
@Composable
|
||||
private fun InfoCard(icon: ImageVector, text: String, value: String, rotateIcon: Float = 0f) {
|
||||
Card(modifier = Modifier.padding(4.dp).width(100.dp).height(100.dp)) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.width(100.dp)
|
||||
.height(100.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(modifier = Modifier.padding(4.dp).width(100.dp).height(100.dp), contentAlignment = Alignment.Center) {
|
||||
Column(verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = text,
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.thenIf(rotateIcon != 0f) {
|
||||
rotate(rotateIcon)
|
||||
},
|
||||
modifier = Modifier.size(24.dp).thenIf(rotateIcon != 0f) { rotate(rotateIcon) },
|
||||
)
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
|
|
@ -843,32 +795,14 @@ private fun InfoCard(icon: ImageVector, text: String, value: String, rotateIcon:
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun DrawableInfoCard(
|
||||
@DrawableRes iconRes: Int,
|
||||
text: String,
|
||||
value: String,
|
||||
rotateIcon: Float = 0f,
|
||||
) {
|
||||
private fun DrawableInfoCard(@DrawableRes iconRes: Int, text: String, value: String, rotateIcon: Float = 0f) {
|
||||
Card(modifier = Modifier.padding(4.dp).width(100.dp).height(100.dp)) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.width(100.dp)
|
||||
.height(100.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(modifier = Modifier.padding(4.dp).width(100.dp).height(100.dp), contentAlignment = Alignment.Center) {
|
||||
Column(verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(
|
||||
painter = painterResource(id = iconRes),
|
||||
contentDescription = text,
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.thenIf(rotateIcon != 0f) {
|
||||
rotate(rotateIcon)
|
||||
},
|
||||
modifier = Modifier.size(24.dp).thenIf(rotateIcon != 0f) { rotate(rotateIcon) },
|
||||
)
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
|
|
@ -895,118 +829,128 @@ private fun EnvironmentMetrics(
|
|||
isFahrenheit: Boolean = false,
|
||||
displayUnits: ConfigProtos.Config.DisplayConfig.DisplayUnits,
|
||||
) {
|
||||
val vectorMetrics = remember(node.environmentMetrics, isFahrenheit, displayUnits) {
|
||||
buildList {
|
||||
with(node.environmentMetrics) {
|
||||
if (hasTemperature()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.temperature,
|
||||
temperature.toTempString(isFahrenheit),
|
||||
Icons.Default.Thermostat,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasRelativeHumidity()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.humidity,
|
||||
"%.0f%%".format(relativeHumidity),
|
||||
Icons.Default.WaterDrop,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasBarometricPressure()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.pressure,
|
||||
"%.0f hPa".format(barometricPressure),
|
||||
Icons.Default.Speed,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasGasResistance()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.gas_resistance,
|
||||
"%.0f MΩ".format(gasResistance),
|
||||
Icons.Default.BlurOn,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasVoltage()) add(VectorMetricInfo(R.string.voltage, "%.2fV".format(voltage), Icons.Default.Bolt))
|
||||
if (hasCurrent()) add(VectorMetricInfo(R.string.current, "%.1fmA".format(current), Icons.Default.Power))
|
||||
if (hasIaq()) add(VectorMetricInfo(R.string.iaq, iaq.toString(), Icons.Default.Air))
|
||||
if (hasDistance()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.distance,
|
||||
distance.toSmallDistanceString(displayUnits),
|
||||
Icons.Default.Height,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasLux()) add(VectorMetricInfo(R.string.lux, "%.0f lx".format(lux), Icons.Default.LightMode))
|
||||
if (hasUvLux()) add(VectorMetricInfo(R.string.uv_lux, "%.0f lx".format(uvLux), Icons.Default.LightMode))
|
||||
if (hasWindSpeed()) {
|
||||
@Suppress("MagicNumber")
|
||||
val normalizedBearing = (windDirection + 180) % 360
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.wind,
|
||||
windSpeed.toSpeedString(displayUnits),
|
||||
Icons.Outlined.Navigation,
|
||||
normalizedBearing.toFloat(),
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasWeight()) add(VectorMetricInfo(R.string.weight, "%.2f kg".format(weight), Icons.Default.Scale))
|
||||
}
|
||||
}
|
||||
}
|
||||
val drawableMetrics = remember(node.environmentMetrics, isFahrenheit) {
|
||||
buildList {
|
||||
with(node.environmentMetrics) {
|
||||
if (hasTemperature() && hasRelativeHumidity()) {
|
||||
val dewPoint = UnitConversions.calculateDewPoint(temperature, relativeHumidity)
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.dew_point,
|
||||
dewPoint.toTempString(isFahrenheit),
|
||||
R.drawable.ic_outlined_dew_point_24,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasSoilTemperature()) {
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.soil_temperature,
|
||||
soilTemperature.toTempString(isFahrenheit),
|
||||
R.drawable.soil_temperature,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasSoilMoisture()) {
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.soil_moisture,
|
||||
"%d%%".format(soilMoisture),
|
||||
R.drawable.soil_moisture,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasRadiation()) {
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.radiation,
|
||||
"%.1f µR/h".format(radiation),
|
||||
R.drawable.ic_filled_radioactive_24,
|
||||
),
|
||||
)
|
||||
val vectorMetrics =
|
||||
remember(node.environmentMetrics, isFahrenheit, displayUnits) {
|
||||
buildList {
|
||||
with(node.environmentMetrics) {
|
||||
if (hasTemperature()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.temperature,
|
||||
temperature.toTempString(isFahrenheit),
|
||||
Icons.Default.Thermostat,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasRelativeHumidity()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.humidity,
|
||||
"%.0f%%".format(relativeHumidity),
|
||||
Icons.Default.WaterDrop,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasBarometricPressure()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.pressure,
|
||||
"%.0f hPa".format(barometricPressure),
|
||||
Icons.Default.Speed,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasGasResistance()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.gas_resistance,
|
||||
"%.0f MΩ".format(gasResistance),
|
||||
Icons.Default.BlurOn,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasVoltage()) {
|
||||
add(VectorMetricInfo(R.string.voltage, "%.2fV".format(voltage), Icons.Default.Bolt))
|
||||
}
|
||||
if (hasCurrent()) {
|
||||
add(VectorMetricInfo(R.string.current, "%.1fmA".format(current), Icons.Default.Power))
|
||||
}
|
||||
if (hasIaq()) add(VectorMetricInfo(R.string.iaq, iaq.toString(), Icons.Default.Air))
|
||||
if (hasDistance()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.distance,
|
||||
distance.toSmallDistanceString(displayUnits),
|
||||
Icons.Default.Height,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasLux()) add(VectorMetricInfo(R.string.lux, "%.0f lx".format(lux), Icons.Default.LightMode))
|
||||
if (hasUvLux()) {
|
||||
add(VectorMetricInfo(R.string.uv_lux, "%.0f lx".format(uvLux), Icons.Default.LightMode))
|
||||
}
|
||||
if (hasWindSpeed()) {
|
||||
@Suppress("MagicNumber")
|
||||
val normalizedBearing = (windDirection + 180) % 360
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
R.string.wind,
|
||||
windSpeed.toSpeedString(displayUnits),
|
||||
Icons.Outlined.Navigation,
|
||||
normalizedBearing.toFloat(),
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasWeight()) {
|
||||
add(VectorMetricInfo(R.string.weight, "%.2f kg".format(weight), Icons.Default.Scale))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val drawableMetrics =
|
||||
remember(node.environmentMetrics, isFahrenheit) {
|
||||
buildList {
|
||||
with(node.environmentMetrics) {
|
||||
if (hasTemperature() && hasRelativeHumidity()) {
|
||||
val dewPoint = UnitConversions.calculateDewPoint(temperature, relativeHumidity)
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.dew_point,
|
||||
dewPoint.toTempString(isFahrenheit),
|
||||
R.drawable.ic_outlined_dew_point_24,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasSoilTemperature()) {
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.soil_temperature,
|
||||
soilTemperature.toTempString(isFahrenheit),
|
||||
R.drawable.soil_temperature,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasSoilMoisture()) {
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.soil_moisture,
|
||||
"%d%%".format(soilMoisture),
|
||||
R.drawable.soil_moisture,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasRadiation()) {
|
||||
add(
|
||||
DrawableMetricInfo(
|
||||
R.string.radiation,
|
||||
"%.1f µR/h".format(radiation),
|
||||
R.drawable.ic_filled_radioactive_24,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
|
|
@ -1033,24 +977,25 @@ private fun EnvironmentMetrics(
|
|||
|
||||
@Composable
|
||||
private fun PowerMetrics(node: Node) {
|
||||
val metrics = remember(node.powerMetrics) {
|
||||
buildList {
|
||||
with(node.powerMetrics) {
|
||||
if (ch1Voltage != 0f) {
|
||||
add(VectorMetricInfo(R.string.channel_1, "%.2fV".format(ch1Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(R.string.channel_1, "%.1fmA".format(ch1Current), Icons.Default.Power))
|
||||
}
|
||||
if (ch2Voltage != 0f) {
|
||||
add(VectorMetricInfo(R.string.channel_2, "%.2fV".format(ch2Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(R.string.channel_2, "%.1fmA".format(ch2Current), Icons.Default.Power))
|
||||
}
|
||||
if (ch3Voltage != 0f) {
|
||||
add(VectorMetricInfo(R.string.channel_3, "%.2fV".format(ch3Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(R.string.channel_3, "%.1fmA".format(ch3Current), Icons.Default.Power))
|
||||
val metrics =
|
||||
remember(node.powerMetrics) {
|
||||
buildList {
|
||||
with(node.powerMetrics) {
|
||||
if (ch1Voltage != 0f) {
|
||||
add(VectorMetricInfo(R.string.channel_1, "%.2fV".format(ch1Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(R.string.channel_1, "%.1fmA".format(ch1Current), Icons.Default.Power))
|
||||
}
|
||||
if (ch2Voltage != 0f) {
|
||||
add(VectorMetricInfo(R.string.channel_2, "%.2fV".format(ch2Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(R.string.channel_2, "%.1fmA".format(ch2Current), Icons.Default.Power))
|
||||
}
|
||||
if (ch3Voltage != 0f) {
|
||||
add(VectorMetricInfo(R.string.channel_3, "%.2fV".format(ch3Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(R.string.channel_3, "%.1fmA".format(ch3Current), Icons.Default.Power))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
|
|
@ -1066,10 +1011,11 @@ private const val COOL_DOWN_TIME_MS = 30000L
|
|||
|
||||
@Composable
|
||||
fun TracerouteActionButton(title: String, lastTracerouteTime: Long?, onClick: () -> Unit) {
|
||||
var isCoolingDown by remember(lastTracerouteTime) {
|
||||
val timeSinceLast = System.currentTimeMillis() - (lastTracerouteTime ?: 0)
|
||||
mutableStateOf(timeSinceLast < COOL_DOWN_TIME_MS)
|
||||
}
|
||||
var isCoolingDown by
|
||||
remember(lastTracerouteTime) {
|
||||
val timeSinceLast = System.currentTimeMillis() - (lastTracerouteTime ?: 0)
|
||||
mutableStateOf(timeSinceLast < COOL_DOWN_TIME_MS)
|
||||
}
|
||||
|
||||
LaunchedEffect(lastTracerouteTime) {
|
||||
val timeSinceLast = System.currentTimeMillis() - (lastTracerouteTime ?: 0)
|
||||
|
|
@ -1079,10 +1025,7 @@ fun TracerouteActionButton(title: String, lastTracerouteTime: Long?, onClick: ()
|
|||
}
|
||||
}
|
||||
|
||||
val progress by animateFloatAsState(
|
||||
targetValue = if (isCoolingDown) 1f else 0f,
|
||||
label = "TracerouteCooldown",
|
||||
)
|
||||
val progress by animateFloatAsState(targetValue = if (isCoolingDown) 1f else 0f, label = "TracerouteCooldown")
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
|
|
@ -1090,10 +1033,7 @@ fun TracerouteActionButton(title: String, lastTracerouteTime: Long?, onClick: ()
|
|||
onClick()
|
||||
},
|
||||
enabled = !isCoolingDown,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.height(48.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).height(48.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (progress > 0f) {
|
||||
|
|
@ -1119,10 +1059,7 @@ fun TracerouteActionButton(title: String, lastTracerouteTime: Long?, onClick: ()
|
|||
|
||||
@Composable
|
||||
fun NodeActionButton(
|
||||
modifier: Modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.height(48.dp),
|
||||
modifier: Modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).height(48.dp),
|
||||
title: String,
|
||||
enabled: Boolean,
|
||||
icon: ImageVector? = null,
|
||||
|
|
@ -1156,8 +1093,8 @@ fun NodeActionSwitch(
|
|||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.height(48.dp)
|
||||
.toggleable(value = checked, enabled = enabled, role = Role.Switch, onValueChange = { onClick() }),
|
||||
|
|
@ -1167,9 +1104,7 @@ fun NodeActionSwitch(
|
|||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp, horizontal = 16.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp, horizontal = 16.dp),
|
||||
) {
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
|
|
@ -1268,10 +1203,5 @@ private fun PreviewWindDirectionN45() {
|
|||
@Composable
|
||||
private fun PreviewWindDirectionItem(windDirection: Float, windSpeed: String = "5 m/s") {
|
||||
val normalizedBearing = (windDirection + 180) % 360
|
||||
InfoCard(
|
||||
icon = Icons.Outlined.Navigation,
|
||||
text = "Wind",
|
||||
value = windSpeed,
|
||||
rotateIcon = normalizedBearing,
|
||||
)
|
||||
InfoCard(icon = Icons.Outlined.Navigation, text = "Wind", value = windSpeed, rotateIcon = normalizedBearing)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import androidx.compose.material3.ButtonDefaults
|
|||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
|
|
@ -46,7 +47,6 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
|
|
@ -58,72 +58,54 @@ import com.geeksville.mesh.R
|
|||
import com.geeksville.mesh.model.Channel
|
||||
import com.geeksville.mesh.ui.common.components.CopyIconButton
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import com.google.protobuf.ByteString
|
||||
|
||||
@Composable
|
||||
private fun KeyStatusDialog(
|
||||
@StringRes title: Int,
|
||||
@StringRes text: Int,
|
||||
key: ByteString?,
|
||||
onDismiss: () -> Unit = {}
|
||||
) = Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
private fun KeyStatusDialog(@StringRes title: Int, @StringRes text: Int, key: ByteString?, onDismiss: () -> Unit = {}) =
|
||||
Dialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(id = text),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
if (key != null && title == R.string.encryption_pkc) {
|
||||
val keyString = Base64.encodeToString(key.toByteArray(), Base64.NO_WRAP)
|
||||
Text(
|
||||
text = stringResource(id = R.string.config_security_public_key) + ":",
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
SelectionContainer {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item {
|
||||
Text(text = stringResource(id = title), textAlign = TextAlign.Center)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text(text = stringResource(id = text), textAlign = TextAlign.Center)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
if (key != null && title == R.string.encryption_pkc) {
|
||||
val keyString = Base64.encodeToString(key.toByteArray(), Base64.NO_WRAP)
|
||||
Text(
|
||||
text = keyString,
|
||||
text = stringResource(id = R.string.config_security_public_key) + ":",
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
SelectionContainer { Text(text = keyString, textAlign = TextAlign.Center) }
|
||||
Spacer(Modifier.height(8.dp))
|
||||
CopyIconButton(valueToCopy = keyString, modifier = Modifier.padding(start = 8.dp))
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.close))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
CopyIconButton(
|
||||
valueToCopy = keyString,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
) { Text(text = stringResource(id = R.string.close)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NodeKeyStatusIcon(
|
||||
|
|
@ -134,32 +116,33 @@ fun NodeKeyStatusIcon(
|
|||
) {
|
||||
var showEncryptionDialog by remember { mutableStateOf(false) }
|
||||
if (showEncryptionDialog) {
|
||||
val (title, text) = when {
|
||||
mismatchKey -> R.string.encryption_error to R.string.encryption_error_text
|
||||
hasPKC -> R.string.encryption_pkc to R.string.encryption_pkc_text
|
||||
else -> R.string.encryption_psk to R.string.encryption_psk_text
|
||||
}
|
||||
val (title, text) =
|
||||
when {
|
||||
mismatchKey -> R.string.encryption_error to R.string.encryption_error_text
|
||||
hasPKC -> R.string.encryption_pkc to R.string.encryption_pkc_text
|
||||
else -> R.string.encryption_psk to R.string.encryption_psk_text
|
||||
}
|
||||
KeyStatusDialog(title, text, publicKey) { showEncryptionDialog = false }
|
||||
}
|
||||
|
||||
val (icon, tint) = when {
|
||||
mismatchKey -> Icons.Default.KeyOff to Color.Red
|
||||
hasPKC -> Icons.Default.Lock to Color(color = 0xFF30C047)
|
||||
else -> ImageVector.vectorResource(R.drawable.ic_lock_open_right_24) to Color(color = 0xFFFEC30A)
|
||||
}
|
||||
val (icon, tint) =
|
||||
when {
|
||||
mismatchKey -> Icons.Default.KeyOff to colorScheme.StatusRed
|
||||
hasPKC -> Icons.Default.Lock to colorScheme.StatusGreen
|
||||
else -> ImageVector.vectorResource(R.drawable.ic_lock_open_right_24) to colorScheme.StatusYellow
|
||||
}
|
||||
|
||||
IconButton(
|
||||
onClick = { showEncryptionDialog = true },
|
||||
modifier = modifier,
|
||||
) {
|
||||
IconButton(onClick = { showEncryptionDialog = true }, modifier = modifier) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = stringResource(
|
||||
id = when {
|
||||
contentDescription =
|
||||
stringResource(
|
||||
id =
|
||||
when {
|
||||
mismatchKey -> R.string.encryption_error
|
||||
hasPKC -> R.string.encryption_pkc
|
||||
else -> R.string.encryption_psk
|
||||
}
|
||||
},
|
||||
),
|
||||
tint = tint,
|
||||
)
|
||||
|
|
@ -169,13 +152,7 @@ fun NodeKeyStatusIcon(
|
|||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun KeyStatusDialogErrorPreview() {
|
||||
AppTheme {
|
||||
KeyStatusDialog(
|
||||
title = R.string.encryption_error,
|
||||
text = R.string.encryption_error_text,
|
||||
key = null,
|
||||
)
|
||||
}
|
||||
AppTheme { KeyStatusDialog(title = R.string.encryption_error, text = R.string.encryption_error_text, key = null) }
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
|
|
@ -193,11 +170,5 @@ private fun KeyStatusDialogPkcPreview() {
|
|||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun KeyStatusDialogPskPreview() {
|
||||
AppTheme {
|
||||
KeyStatusDialog(
|
||||
title = R.string.encryption_psk,
|
||||
text = R.string.encryption_psk_text,
|
||||
key = null,
|
||||
)
|
||||
}
|
||||
AppTheme { KeyStatusDialog(title = R.string.encryption_psk, text = R.string.encryption_psk_text, key = null) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,24 +36,19 @@ import androidx.compose.material3.TooltipDefaults
|
|||
import androidx.compose.material3.rememberTooltipState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NodeStatusIcons(
|
||||
isThisNode: Boolean,
|
||||
isUnmessageable: Boolean,
|
||||
isFavorite: Boolean,
|
||||
isConnected: Boolean
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(4.dp)
|
||||
) {
|
||||
fun NodeStatusIcons(isThisNode: Boolean, isUnmessageable: Boolean, isFavorite: Boolean, isConnected: Boolean) {
|
||||
Row(modifier = Modifier.padding(4.dp)) {
|
||||
if (isThisNode) {
|
||||
TooltipBox(
|
||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
||||
|
|
@ -65,12 +60,12 @@ fun NodeStatusIcons(
|
|||
R.string.connected
|
||||
} else {
|
||||
R.string.disconnected
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
state = rememberTooltipState()
|
||||
state = rememberTooltipState(),
|
||||
) {
|
||||
if (isConnected) {
|
||||
@Suppress("MagicNumber")
|
||||
|
|
@ -78,14 +73,14 @@ fun NodeStatusIcons(
|
|||
imageVector = Icons.TwoTone.CloudDone,
|
||||
contentDescription = stringResource(R.string.connected),
|
||||
modifier = Modifier.size(24.dp), // Smaller size for badge
|
||||
tint = Color(0xFF4CAF50)
|
||||
tint = MaterialTheme.colorScheme.StatusGreen,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.CloudOff,
|
||||
contentDescription = stringResource(R.string.not_connected),
|
||||
modifier = Modifier.size(24.dp), // Smaller size for badge
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
tint = MaterialTheme.colorScheme.StatusRed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -94,23 +89,14 @@ fun NodeStatusIcons(
|
|||
if (isUnmessageable) {
|
||||
TooltipBox(
|
||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
||||
tooltip = {
|
||||
PlainTooltip {
|
||||
Text(stringResource(R.string.unmonitored_or_infrastructure))
|
||||
}
|
||||
},
|
||||
state = rememberTooltipState()
|
||||
tooltip = { PlainTooltip { Text(stringResource(R.string.unmonitored_or_infrastructure)) } },
|
||||
state = rememberTooltipState(),
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {},
|
||||
modifier = Modifier
|
||||
.size(24.dp),
|
||||
) {
|
||||
IconButton(onClick = {}, modifier = Modifier.size(24.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.NoCell,
|
||||
contentDescription = stringResource(R.string.unmessageable),
|
||||
modifier = Modifier
|
||||
.size(24.dp), // Smaller size for badge
|
||||
modifier = Modifier.size(24.dp), // Smaller size for badge
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -118,24 +104,15 @@ fun NodeStatusIcons(
|
|||
if (isFavorite && !isThisNode) {
|
||||
TooltipBox(
|
||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
||||
tooltip = {
|
||||
PlainTooltip {
|
||||
Text(stringResource(R.string.favorite))
|
||||
}
|
||||
},
|
||||
state = rememberTooltipState()
|
||||
tooltip = { PlainTooltip { Text(stringResource(R.string.favorite)) } },
|
||||
state = rememberTooltipState(),
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {},
|
||||
modifier = Modifier
|
||||
.size(24.dp),
|
||||
) {
|
||||
IconButton(onClick = {}, modifier = Modifier.size(24.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Star,
|
||||
contentDescription = stringResource(R.string.favorite),
|
||||
modifier = Modifier
|
||||
.size(24.dp), // Smaller size for badge
|
||||
tint = Color(color = 0xFFFEC30A)
|
||||
modifier = Modifier.size(24.dp), // Smaller size for badge
|
||||
tint = MaterialTheme.colorScheme.StatusYellow,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -146,10 +123,5 @@ fun NodeStatusIcons(
|
|||
@Preview
|
||||
@Composable
|
||||
fun StatusIconsPreview() {
|
||||
NodeStatusIcons(
|
||||
isThisNode = true,
|
||||
isUnmessageable = true,
|
||||
isFavorite = true,
|
||||
isConnected = false,
|
||||
)
|
||||
NodeStatusIcons(isThisNode = true, isUnmessageable = true, isFavorite = true, isConnected = false)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue