mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor(ui): Icon audit and node list item refactor (#4313)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
5db2c9d69c
commit
a28aa4d52e
91 changed files with 2178 additions and 702 deletions
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,18 +14,17 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.navigation
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CellTower
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material.icons.filled.Memory
|
||||
import androidx.compose.material.icons.filled.People
|
||||
import androidx.compose.material.icons.filled.PermScanWifi
|
||||
import androidx.compose.material.icons.filled.Power
|
||||
import androidx.compose.material.icons.filled.Router
|
||||
import androidx.compose.material.icons.rounded.CellTower
|
||||
import androidx.compose.material.icons.rounded.LightMode
|
||||
import androidx.compose.material.icons.rounded.LocationOn
|
||||
import androidx.compose.material.icons.rounded.Memory
|
||||
import androidx.compose.material.icons.rounded.People
|
||||
import androidx.compose.material.icons.rounded.PermScanWifi
|
||||
import androidx.compose.material.icons.rounded.Power
|
||||
import androidx.compose.material.icons.rounded.Router
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
|
@ -286,49 +285,49 @@ enum class NodeDetailRoute(
|
|||
DEVICE(
|
||||
Res.string.device,
|
||||
NodeDetailRoutes.DeviceMetrics::class,
|
||||
Icons.Default.Router,
|
||||
Icons.Rounded.Router,
|
||||
{ metricsVM, onNavigateUp -> DeviceMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
POSITION_LOG(
|
||||
Res.string.position_log,
|
||||
NodeDetailRoutes.PositionLog::class,
|
||||
Icons.Default.LocationOn,
|
||||
Icons.Rounded.LocationOn,
|
||||
{ metricsVM, onNavigateUp -> PositionLogScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
ENVIRONMENT(
|
||||
Res.string.environment,
|
||||
NodeDetailRoutes.EnvironmentMetrics::class,
|
||||
Icons.Default.LightMode,
|
||||
Icons.Rounded.LightMode,
|
||||
{ metricsVM, onNavigateUp -> EnvironmentMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
SIGNAL(
|
||||
Res.string.signal,
|
||||
NodeDetailRoutes.SignalMetrics::class,
|
||||
Icons.Default.CellTower,
|
||||
Icons.Rounded.CellTower,
|
||||
{ metricsVM, onNavigateUp -> SignalMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
TRACEROUTE(
|
||||
Res.string.traceroute,
|
||||
NodeDetailRoutes.TracerouteLog::class,
|
||||
Icons.Default.PermScanWifi,
|
||||
Icons.Rounded.PermScanWifi,
|
||||
{ metricsVM, onNavigateUp -> TracerouteLogScreen(viewModel = metricsVM, onNavigateUp = onNavigateUp) },
|
||||
),
|
||||
POWER(
|
||||
Res.string.power,
|
||||
NodeDetailRoutes.PowerMetrics::class,
|
||||
Icons.Default.Power,
|
||||
Icons.Rounded.Power,
|
||||
{ metricsVM, onNavigateUp -> PowerMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
HOST(
|
||||
Res.string.host,
|
||||
NodeDetailRoutes.HostMetricsLog::class,
|
||||
Icons.Default.Memory,
|
||||
Icons.Rounded.Memory,
|
||||
{ metricsVM, onNavigateUp -> HostMetricsLogScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
PAX(
|
||||
Res.string.pax,
|
||||
NodeDetailRoutes.PaxMetrics::class,
|
||||
Icons.Default.People,
|
||||
Icons.Rounded.People,
|
||||
{ metricsVM, onNavigateUp -> PaxMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ import androidx.compose.foundation.layout.safeDrawingPadding
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Wifi
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
|
|
@ -150,6 +148,7 @@ import org.meshtastic.core.ui.icon.Map
|
|||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Nodes
|
||||
import org.meshtastic.core.ui.icon.Settings
|
||||
import org.meshtastic.core.ui.icon.Wifi
|
||||
import org.meshtastic.core.ui.qr.ScannedQrCodeDialog
|
||||
import org.meshtastic.core.ui.share.SharedContactDialog
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusBlue
|
||||
|
|
@ -162,7 +161,7 @@ enum class TopLevelDestination(val label: StringResource, val icon: ImageVector,
|
|||
Nodes(Res.string.nodes, MeshtasticIcons.Nodes, NodesRoutes.NodesGraph),
|
||||
Map(Res.string.map, MeshtasticIcons.Map, MapRoutes.Map()),
|
||||
Settings(Res.string.bottom_nav_settings, MeshtasticIcons.Settings, SettingsRoutes.SettingsGraph()),
|
||||
Connections(Res.string.connections, Icons.Rounded.Wifi, ConnectionsRoutes.ConnectionsGraph),
|
||||
Connections(Res.string.connections, MeshtasticIcons.Wifi, ConnectionsRoutes.ConnectionsGraph),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.contact
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -28,13 +27,6 @@ import androidx.compose.foundation.lazy.LazyListScope
|
|||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.twotone.VolumeMute
|
||||
import androidx.compose.material.icons.automirrored.twotone.VolumeUp
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.SelectAll
|
||||
import androidx.compose.material.icons.rounded.QrCode2
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
|
|
@ -100,6 +92,13 @@ import org.meshtastic.core.strings.unmute
|
|||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.core.ui.component.smartScrollToTop
|
||||
import org.meshtastic.core.ui.icon.Close
|
||||
import org.meshtastic.core.ui.icon.Delete
|
||||
import org.meshtastic.core.ui.icon.HardwareModel
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.SelectAll
|
||||
import org.meshtastic.core.ui.icon.VolumeMuteTwoTone
|
||||
import org.meshtastic.core.ui.icon.VolumeUpTwoTone
|
||||
import org.meshtastic.proto.AppOnlyProtos
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
|
@ -232,7 +231,7 @@ fun ContactsScreen(
|
|||
),
|
||||
onClick = onNavigateToShare,
|
||||
) {
|
||||
Icon(Icons.Rounded.QrCode2, contentDescription = stringResource(Res.string.share_contact))
|
||||
Icon(MeshtasticIcons.HardwareModel, contentDescription = stringResource(Res.string.share_contact))
|
||||
}
|
||||
},
|
||||
) { paddingValues ->
|
||||
|
|
@ -445,7 +444,7 @@ private fun SelectionToolbar(
|
|||
title = { Text(text = "$selectedCount") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onCloseSelection) {
|
||||
Icon(Icons.Default.Close, contentDescription = stringResource(Res.string.close_selection))
|
||||
Icon(MeshtasticIcons.Close, contentDescription = stringResource(Res.string.close_selection))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
|
|
@ -453,9 +452,9 @@ private fun SelectionToolbar(
|
|||
Icon(
|
||||
imageVector =
|
||||
if (isAllMuted) {
|
||||
Icons.AutoMirrored.TwoTone.VolumeUp
|
||||
MeshtasticIcons.VolumeUpTwoTone
|
||||
} else {
|
||||
Icons.AutoMirrored.TwoTone.VolumeMute
|
||||
MeshtasticIcons.VolumeMuteTwoTone
|
||||
},
|
||||
contentDescription =
|
||||
if (isAllMuted) {
|
||||
|
|
@ -466,10 +465,10 @@ private fun SelectionToolbar(
|
|||
)
|
||||
}
|
||||
IconButton(onClick = onDeleteSelected) {
|
||||
Icon(Icons.Default.Delete, contentDescription = stringResource(Res.string.delete_selection))
|
||||
Icon(MeshtasticIcons.Delete, contentDescription = stringResource(Res.string.delete_selection))
|
||||
}
|
||||
IconButton(onClick = onSelectAll) {
|
||||
Icon(Icons.Default.SelectAll, contentDescription = stringResource(Res.string.select_all))
|
||||
Icon(MeshtasticIcons.SelectAll, contentDescription = stringResource(Res.string.select_all))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.sharing
|
||||
|
||||
import android.Manifest
|
||||
|
|
@ -38,7 +37,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ChevronRight
|
||||
import androidx.compose.material.icons.rounded.ChevronRight
|
||||
import androidx.compose.material.icons.twotone.Check
|
||||
import androidx.compose.material.icons.twotone.Close
|
||||
import androidx.compose.material.icons.twotone.ContentCopy
|
||||
|
|
@ -566,7 +565,7 @@ private fun ModemPresetInfo(modemPresetName: String, onClick: () -> Unit) {
|
|||
}
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Icon(
|
||||
imageVector = Icons.Default.ChevronRight,
|
||||
imageVector = Icons.Rounded.ChevronRight,
|
||||
contentDescription = stringResource(Res.string.navigate_into_label),
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@
|
|||
<string name="elevation_suffix" translatable="false">MSL</string>
|
||||
<string name="channel_air_util" translatable="false">ChUtil %.1f%% AirUtilTX %.1f%%</string>
|
||||
|
||||
<string name="channel">Channel</string>
|
||||
<string name="channel_name">Channel Name</string>
|
||||
<string name="qr_code">QR code</string>
|
||||
<string name="unknown_username">Unknown Username</string>
|
||||
|
|
@ -410,7 +411,7 @@
|
|||
<string name="encryption_pkc">Public Key Encryption</string>
|
||||
<string name="encryption_pkc_text">Direct messages are using the new public key infrastructure for encryption.</string>
|
||||
<string name="encryption_error">Public key mismatch</string>
|
||||
<string name="encryption_error_text">The public key does not match the recorded key. You may remove the node and let it exchange keys again, but this may indicate a more serious security problem. Contact the user through another trusted channel, to determine if the key change was due to a factory reset or other intentional action.</string>
|
||||
<string name="encryption_error_text">The public key does not match the recorded key. You may remove the node and let it exchange keys again, but this may indicate a more security problem. Contact the user through another trusted channel, to determine if the key change was due to a factory reset or other intentional action.</string>
|
||||
<string name="userinfo">User Info</string>
|
||||
<string name="meshtastic_new_nodes_notifications">New node notifications</string>
|
||||
<string name="more_details">More details</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.channel
|
||||
import org.meshtastic.core.ui.icon.Channel
|
||||
import org.meshtastic.core.ui.icon.Counter0
|
||||
import org.meshtastic.core.ui.icon.Counter1
|
||||
import org.meshtastic.core.ui.icon.Counter2
|
||||
import org.meshtastic.core.ui.icon.Counter3
|
||||
import org.meshtastic.core.ui.icon.Counter4
|
||||
import org.meshtastic.core.ui.icon.Counter5
|
||||
import org.meshtastic.core.ui.icon.Counter6
|
||||
import org.meshtastic.core.ui.icon.Counter7
|
||||
import org.meshtastic.core.ui.icon.Counter8
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
@Suppress("MagicNumber")
|
||||
fun ChannelInfo(
|
||||
channel: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
val icon =
|
||||
when (channel) {
|
||||
0 -> MeshtasticIcons.Counter0
|
||||
1 -> MeshtasticIcons.Counter1
|
||||
2 -> MeshtasticIcons.Counter2
|
||||
3 -> MeshtasticIcons.Counter3
|
||||
4 -> MeshtasticIcons.Counter4
|
||||
5 -> MeshtasticIcons.Counter5
|
||||
6 -> MeshtasticIcons.Counter6
|
||||
7 -> MeshtasticIcons.Counter7
|
||||
8 -> MeshtasticIcons.Counter8
|
||||
else -> MeshtasticIcons.Channel
|
||||
}
|
||||
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = icon,
|
||||
contentDescription = stringResource(Res.string.channel),
|
||||
text = stringResource(Res.string.channel),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun ChannelInfoPreview() {
|
||||
AppTheme { ChannelInfo(channel = 2) }
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.distance
|
||||
import org.meshtastic.core.ui.icon.Distance
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun DistanceInfo(
|
||||
distance: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Distance,
|
||||
contentDescription = stringResource(Res.string.distance),
|
||||
text = distance,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun DistanceInfoPreview() {
|
||||
AppTheme { DistanceInfo(distance = "423 mi.") }
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.model.util.metersIn
|
||||
import org.meshtastic.core.model.util.toString
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.altitude
|
||||
import org.meshtastic.core.strings.elevation_suffix
|
||||
import org.meshtastic.core.ui.icon.Elevation
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig.DisplayUnits
|
||||
|
||||
@Composable
|
||||
fun ElevationInfo(
|
||||
modifier: Modifier = Modifier,
|
||||
altitude: Int,
|
||||
system: DisplayUnits,
|
||||
suffix: String = stringResource(Res.string.elevation_suffix),
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Elevation,
|
||||
contentDescription = stringResource(Res.string.altitude),
|
||||
text = altitude.metersIn(system).toString(system) + " " + suffix,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun ElevationInfoPreview() {
|
||||
MaterialTheme { ElevationInfo(altitude = 100, system = DisplayUnits.METRIC, suffix = "ASL") }
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.hops_away
|
||||
import org.meshtastic.core.ui.icon.Hops
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun HopsInfo(hops: Int, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Hops,
|
||||
contentDescription = stringResource(Res.string.hops_away),
|
||||
text = hops.toString(),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun HopsInfoPreview() {
|
||||
AppTheme { HopsInfo(hops = 3) }
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.meshtastic.core.ui.icon.Elevation
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
|
||||
private const val SIZE_ICON = 20
|
||||
|
||||
@Composable
|
||||
fun IconInfo(
|
||||
icon: ImageVector,
|
||||
contentDescription: String,
|
||||
modifier: Modifier = Modifier,
|
||||
text: String? = null,
|
||||
style: TextStyle = MaterialTheme.typography.labelMedium,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(SIZE_ICON.dp),
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = contentColor,
|
||||
)
|
||||
text?.let { Text(text = it, style = style, color = contentColor) }
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun IconInfoPreview() {
|
||||
MaterialTheme {
|
||||
IconInfo(icon = MeshtasticIcons.Elevation, contentDescription = "Elevation", content = { Text(text = "100") })
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -31,9 +30,6 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ThumbUp
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -61,6 +57,9 @@ import org.meshtastic.core.strings.Res
|
|||
import org.meshtastic.core.strings.air_quality_icon
|
||||
import org.meshtastic.core.strings.close
|
||||
import org.meshtastic.core.strings.indoor_air_quality_iaq
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.ThumbUp
|
||||
import org.meshtastic.core.ui.icon.Warning
|
||||
import org.meshtastic.core.ui.theme.IAQColors.IAQDangerouslyPolluted
|
||||
import org.meshtastic.core.ui.theme.IAQColors.IAQExcellent
|
||||
import org.meshtastic.core.ui.theme.IAQColors.IAQExtremelyPolluted
|
||||
|
|
@ -137,7 +136,7 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
|
|||
Text(text = "IAQ $iaq", color = Color.White, fontWeight = FontWeight.Bold)
|
||||
Icon(
|
||||
imageVector =
|
||||
if (iaqEnum.range.first < 100) Icons.Default.ThumbUp else Icons.Filled.Warning,
|
||||
if (iaqEnum.range.first < 100) MeshtasticIcons.ThumbUp else MeshtasticIcons.Warning,
|
||||
contentDescription = stringResource(Res.string.air_quality_icon),
|
||||
tint = Color.White,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.node_sort_last_heard
|
||||
import org.meshtastic.core.ui.R
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.util.formatAgo
|
||||
|
||||
@Composable
|
||||
fun LastHeardInfo(
|
||||
modifier: Modifier = Modifier,
|
||||
lastHeard: Int,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = ImageVector.vectorResource(id = R.drawable.ic_antenna_24),
|
||||
contentDescription = stringResource(Res.string.node_sort_last_heard),
|
||||
text = formatAgo(lastHeard),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun LastHeardInfoPreview() {
|
||||
AppTheme { LastHeardInfo(lastHeard = (System.currentTimeMillis() / 1000).toInt() - 8600) }
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@file:Suppress("MagicNumber")
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
|
@ -29,10 +28,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.SignalCellular4Bar
|
||||
import androidx.compose.material.icons.filled.SignalCellularAlt
|
||||
import androidx.compose.material.icons.filled.SignalCellularAlt1Bar
|
||||
import androidx.compose.material.icons.filled.SignalCellularAlt2Bar
|
||||
import androidx.compose.material.icons.rounded.SignalCellular4Bar
|
||||
import androidx.compose.material.icons.rounded.SignalCellularAlt
|
||||
import androidx.compose.material.icons.rounded.SignalCellularAlt1Bar
|
||||
import androidx.compose.material.icons.rounded.SignalCellularAlt2Bar
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
|
|
@ -72,10 +71,10 @@ enum class Quality(
|
|||
@Stable val imageVector: ImageVector,
|
||||
@Stable val color: @Composable () -> Color,
|
||||
) {
|
||||
NONE(Res.string.none_quality, Icons.Default.SignalCellularAlt1Bar, { colorScheme.StatusRed }),
|
||||
BAD(Res.string.bad, Icons.Default.SignalCellularAlt2Bar, { colorScheme.StatusOrange }),
|
||||
FAIR(Res.string.fair, Icons.Default.SignalCellularAlt, { colorScheme.StatusYellow }),
|
||||
GOOD(Res.string.good, Icons.Default.SignalCellular4Bar, { colorScheme.StatusGreen }),
|
||||
NONE(Res.string.none_quality, Icons.Rounded.SignalCellularAlt1Bar, { colorScheme.StatusRed }),
|
||||
BAD(Res.string.bad, Icons.Rounded.SignalCellularAlt2Bar, { colorScheme.StatusOrange }),
|
||||
FAIR(Res.string.fair, Icons.Rounded.SignalCellularAlt, { colorScheme.StatusYellow }),
|
||||
GOOD(Res.string.good, Icons.Rounded.SignalCellular4Bar, { colorScheme.StatusGreen }),
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import android.util.Base64
|
||||
|
|
@ -28,10 +27,6 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyOff
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material.icons.filled.LockOpen
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -52,7 +47,6 @@ 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.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
|
|
@ -61,7 +55,6 @@ import com.google.protobuf.ByteString
|
|||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.config_security_public_key
|
||||
import org.meshtastic.core.strings.encryption_error
|
||||
|
|
@ -74,6 +67,10 @@ import org.meshtastic.core.strings.security_icon_help_dismiss
|
|||
import org.meshtastic.core.strings.security_icon_help_show_all
|
||||
import org.meshtastic.core.strings.security_icon_help_show_less
|
||||
import org.meshtastic.core.strings.show_all_key_title
|
||||
import org.meshtastic.core.ui.icon.KeyOff
|
||||
import org.meshtastic.core.ui.icon.Lock
|
||||
import org.meshtastic.core.ui.icon.LockOpen
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
|
||||
|
|
@ -106,11 +103,9 @@ fun NodeKeyStatusIcon(
|
|||
|
||||
val (icon, tint) =
|
||||
when {
|
||||
mismatchKey -> Icons.Default.KeyOff to colorScheme.StatusRed
|
||||
hasPKC -> Icons.Default.Lock to colorScheme.StatusGreen
|
||||
else ->
|
||||
ImageVector.vectorResource(org.meshtastic.core.ui.R.drawable.ic_lock_open_right_24) to
|
||||
colorScheme.StatusYellow
|
||||
mismatchKey -> MeshtasticIcons.KeyOff to colorScheme.StatusRed
|
||||
hasPKC -> MeshtasticIcons.Lock to colorScheme.StatusGreen
|
||||
else -> MeshtasticIcons.LockOpen to colorScheme.StatusYellow
|
||||
}
|
||||
|
||||
IconButton(onClick = { showEncryptionDialog = true }, modifier = modifier) {
|
||||
|
|
@ -149,7 +144,7 @@ enum class NodeKeySecurityState(
|
|||
) {
|
||||
// State for public key mismatch
|
||||
PKM(
|
||||
icon = Icons.Default.KeyOff,
|
||||
icon = MeshtasticIcons.KeyOff,
|
||||
color = { colorScheme.StatusRed },
|
||||
descriptionResId = Res.string.encryption_error,
|
||||
helpTextResId = Res.string.encryption_error_text,
|
||||
|
|
@ -158,7 +153,7 @@ enum class NodeKeySecurityState(
|
|||
|
||||
// State for public key encryption
|
||||
PKC(
|
||||
icon = Icons.Default.Lock,
|
||||
icon = MeshtasticIcons.Lock,
|
||||
color = { colorScheme.StatusGreen },
|
||||
title = Res.string.encryption_pkc,
|
||||
helpTextResId = Res.string.encryption_pkc_text,
|
||||
|
|
@ -167,7 +162,7 @@ enum class NodeKeySecurityState(
|
|||
|
||||
// State for shared key encryption
|
||||
PSK(
|
||||
icon = Icons.Default.LockOpen,
|
||||
icon = MeshtasticIcons.LockOpen,
|
||||
color = { colorScheme.StatusYellow },
|
||||
title = Res.string.encryption_psk,
|
||||
helpTextResId = Res.string.encryption_psk_text,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.sats
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Satellites
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun SatelliteCountInfo(
|
||||
modifier: Modifier = Modifier,
|
||||
satCount: Int,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Satellites,
|
||||
contentDescription = stringResource(Res.string.sats),
|
||||
text = "$satCount",
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun SatelliteCountInfoPreview() {
|
||||
AppTheme { SatelliteCountInfo(satCount = 5) }
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
|
@ -30,10 +29,6 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material.icons.filled.LockOpen
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
|
|
@ -78,6 +73,10 @@ import org.meshtastic.core.strings.security_icon_insecure_no_precise
|
|||
import org.meshtastic.core.strings.security_icon_insecure_precise_only
|
||||
import org.meshtastic.core.strings.security_icon_secure
|
||||
import org.meshtastic.core.strings.security_icon_warning_precise_mqtt
|
||||
import org.meshtastic.core.ui.icon.Lock
|
||||
import org.meshtastic.core.ui.icon.LockOpen
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Warning
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
|
||||
|
|
@ -109,7 +108,7 @@ enum class SecurityState(
|
|||
) {
|
||||
/** State for a secure channel (green lock). */
|
||||
SECURE(
|
||||
icon = Icons.Filled.Lock,
|
||||
icon = MeshtasticIcons.Lock,
|
||||
color = { colorScheme.StatusGreen },
|
||||
descriptionResId = Res.string.security_icon_secure,
|
||||
helpTextResId = Res.string.security_icon_help_green_lock,
|
||||
|
|
@ -120,7 +119,7 @@ enum class SecurityState(
|
|||
* warning. (yellow open lock)
|
||||
*/
|
||||
INSECURE_NO_PRECISE(
|
||||
icon = Icons.Filled.LockOpen,
|
||||
icon = MeshtasticIcons.LockOpen,
|
||||
color = { colorScheme.StatusYellow },
|
||||
descriptionResId = Res.string.security_icon_insecure_no_precise,
|
||||
helpTextResId = Res.string.security_icon_help_yellow_open_lock,
|
||||
|
|
@ -131,7 +130,7 @@ enum class SecurityState(
|
|||
* lock)
|
||||
*/
|
||||
INSECURE_PRECISE_ONLY(
|
||||
icon = Icons.Filled.LockOpen,
|
||||
icon = MeshtasticIcons.LockOpen,
|
||||
color = { colorScheme.StatusRed },
|
||||
descriptionResId = Res.string.security_icon_insecure_precise_only,
|
||||
helpTextResId = Res.string.security_icon_help_red_open_lock,
|
||||
|
|
@ -142,11 +141,11 @@ enum class SecurityState(
|
|||
* badge).
|
||||
*/
|
||||
INSECURE_PRECISE_MQTT_WARNING(
|
||||
icon = Icons.Filled.LockOpen,
|
||||
icon = MeshtasticIcons.LockOpen,
|
||||
color = { colorScheme.StatusRed },
|
||||
descriptionResId = Res.string.security_icon_warning_precise_mqtt,
|
||||
helpTextResId = Res.string.security_icon_help_warning_precise_mqtt,
|
||||
badgeIcon = Icons.Filled.Warning,
|
||||
badgeIcon = MeshtasticIcons.Warning,
|
||||
badgeIconColor = { colorScheme.StatusYellow },
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,16 +14,12 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -34,12 +30,16 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.model.util.formatUptime
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.channel_air_util
|
||||
import org.meshtastic.core.strings.hops_away
|
||||
import org.meshtastic.core.strings.air_utilization
|
||||
import org.meshtastic.core.strings.channel_utilization
|
||||
import org.meshtastic.core.strings.signal
|
||||
import org.meshtastic.core.strings.signal_quality
|
||||
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
|
||||
import org.meshtastic.core.ui.icon.AirUtilization
|
||||
import org.meshtastic.core.ui.icon.ChannelUtilization
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
const val MAX_VALID_SNR = 100F
|
||||
|
|
@ -53,67 +53,53 @@ fun SignalInfo(
|
|||
isThisNode: Boolean,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
val text =
|
||||
if (isThisNode) {
|
||||
stringResource(Res.string.channel_air_util)
|
||||
.format(node.deviceMetrics.channelUtilization, node.deviceMetrics.airUtilTx)
|
||||
} else {
|
||||
buildList {
|
||||
val hopsString =
|
||||
"%s: %s"
|
||||
.format(
|
||||
stringResource(Res.string.hops_away),
|
||||
if (node.hopsAway == -1) {
|
||||
"?"
|
||||
} else {
|
||||
node.hopsAway.toString()
|
||||
},
|
||||
)
|
||||
if (node.channel > 0) {
|
||||
add("ch:${node.channel}")
|
||||
}
|
||||
if (node.hopsAway != 0) add(hopsString)
|
||||
}
|
||||
.joinToString(" ")
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (text.isNotEmpty()) {
|
||||
Text(text = text, color = contentColor, style = MaterialTheme.typography.labelSmall)
|
||||
}
|
||||
/* We only know the Signal Quality from direct nodes aka 0 hop. */
|
||||
if (node.hopsAway <= 0) {
|
||||
if (node.snr < MAX_VALID_SNR && node.rssi < MAX_VALID_RSSI) {
|
||||
val quality = determineSignalQuality(node.snr, node.rssi)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Snr(node.snr)
|
||||
Rssi(node.rssi)
|
||||
if (isThisNode) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
IconInfo(
|
||||
icon = MeshtasticIcons.ChannelUtilization,
|
||||
contentDescription = stringResource(Res.string.channel_utilization),
|
||||
text = "%.1f%%".format(node.deviceMetrics.channelUtilization),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
IconInfo(
|
||||
icon = MeshtasticIcons.AirUtilization,
|
||||
contentDescription = stringResource(Res.string.air_utilization),
|
||||
text = "%.1f%%".format(node.deviceMetrics.airUtilTx),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
if (node.channel > 0) {
|
||||
ChannelInfo(channel = node.channel, contentColor = contentColor)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = quality.imageVector,
|
||||
contentDescription = stringResource(Res.string.signal_quality),
|
||||
tint = quality.color.invoke(),
|
||||
)
|
||||
Text(
|
||||
text = "${stringResource(Res.string.signal)} ${stringResource(quality.nameRes)}",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = contentColor,
|
||||
maxLines = 1,
|
||||
)
|
||||
if (node.hopsAway > 0) {
|
||||
HopsInfo(hops = node.hopsAway, contentColor = contentColor)
|
||||
} else {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (node.snr < MAX_VALID_SNR && node.rssi < MAX_VALID_RSSI) {
|
||||
val quality = determineSignalQuality(node.snr, node.rssi)
|
||||
Snr(node.snr)
|
||||
Rssi(node.rssi)
|
||||
IconInfo(
|
||||
icon = quality.imageVector,
|
||||
contentDescription = stringResource(Res.string.signal_quality),
|
||||
contentColor = quality.color.invoke(),
|
||||
text = "${stringResource(Res.string.signal)} ${stringResource(quality.nameRes)}",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.deviceMetrics.uptimeSeconds > 0) {
|
||||
UptimeInfo(uptime = formatUptime(node.deviceMetrics.uptimeSeconds), contentColor = contentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.env_metrics_log
|
||||
import org.meshtastic.core.strings.node_id
|
||||
import org.meshtastic.core.strings.pax_metrics_log
|
||||
import org.meshtastic.core.strings.role
|
||||
import org.meshtastic.core.strings.uptime
|
||||
import org.meshtastic.core.ui.icon.AirQuality
|
||||
import org.meshtastic.core.ui.icon.ArrowCircleUp
|
||||
import org.meshtastic.core.ui.icon.HardwareModel
|
||||
import org.meshtastic.core.ui.icon.Humidity
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.NodeId
|
||||
import org.meshtastic.core.ui.icon.Paxcount
|
||||
import org.meshtastic.core.ui.icon.Power
|
||||
import org.meshtastic.core.ui.icon.Pressure
|
||||
import org.meshtastic.core.ui.icon.Role
|
||||
import org.meshtastic.core.ui.icon.Soil
|
||||
import org.meshtastic.core.ui.icon.Temperature
|
||||
|
||||
private const val SIZE_ICON = 20
|
||||
|
||||
@Composable
|
||||
fun TemperatureInfo(
|
||||
temp: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Temperature,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = temp,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HumidityInfo(
|
||||
humidity: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Humidity,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = humidity,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PressureInfo(
|
||||
pressure: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Pressure,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = pressure,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SoilTemperatureInfo(
|
||||
temp: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
OverlayIconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Soil,
|
||||
overlayIcon = MeshtasticIcons.Temperature,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = temp,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SoilMoistureInfo(
|
||||
moisture: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
OverlayIconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Soil,
|
||||
overlayIcon = MeshtasticIcons.Humidity,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = moisture,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PaxcountInfo(
|
||||
pax: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Paxcount,
|
||||
contentDescription = stringResource(Res.string.pax_metrics_log),
|
||||
text = pax,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AirQualityInfo(
|
||||
iaq: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.AirQuality,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = iaq,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PowerInfo(value: String, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Power,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = value,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UptimeInfo(
|
||||
uptime: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.ArrowCircleUp,
|
||||
contentDescription = stringResource(Res.string.uptime),
|
||||
text = uptime,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HardwareInfo(
|
||||
hwModel: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.HardwareModel,
|
||||
contentDescription = "Hardware Model",
|
||||
text = hwModel,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RoleInfo(role: String, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Role,
|
||||
contentDescription = stringResource(Res.string.role),
|
||||
text = role,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NodeIdInfo(id: String, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.NodeId,
|
||||
contentDescription = stringResource(Res.string.node_id),
|
||||
text = id,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("MagicNumber")
|
||||
fun OverlayIconInfo(
|
||||
icon: ImageVector,
|
||||
overlayIcon: ImageVector,
|
||||
contentDescription: String,
|
||||
modifier: Modifier = Modifier,
|
||||
text: String? = null,
|
||||
style: TextStyle = MaterialTheme.typography.labelMedium,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
val foregroundPainter = rememberVectorPainter(overlayIcon)
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = contentColor,
|
||||
modifier =
|
||||
Modifier.size(SIZE_ICON.dp).drawWithContent {
|
||||
drawContent()
|
||||
val badgeSize = size.width * .5f
|
||||
with(foregroundPainter) {
|
||||
draw(size = Size(badgeSize, badgeSize), colorFilter = ColorFilter.tint(contentColor))
|
||||
}
|
||||
},
|
||||
)
|
||||
text?.let { Text(text = it, style = style, color = contentColor) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.component.preview
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
|
@ -120,6 +119,9 @@ class NodePreviewParameterProvider : PreviewParameterProvider<Node> {
|
|||
voltage = 3.7F
|
||||
current = 0.0F
|
||||
iaq = 100
|
||||
barometricPressure = 1013.25F
|
||||
soilTemperature = 28.0F
|
||||
soilMoisture = 50
|
||||
},
|
||||
paxcounter =
|
||||
paxcount {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Reply
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material.icons.automirrored.filled.Sort
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.AddReaction
|
||||
import androidx.compose.material.icons.rounded.Clear
|
||||
import androidx.compose.material.icons.rounded.Close
|
||||
import androidx.compose.material.icons.rounded.CloudDownload
|
||||
import androidx.compose.material.icons.rounded.ContentCopy
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.Folder
|
||||
import androidx.compose.material.icons.rounded.MoreVert
|
||||
import androidx.compose.material.icons.rounded.Refresh
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material.icons.rounded.Search
|
||||
import androidx.compose.material.icons.rounded.SelectAll
|
||||
import androidx.compose.material.icons.rounded.Share
|
||||
import androidx.compose.material.icons.rounded.SystemUpdate
|
||||
import androidx.compose.material.icons.rounded.ThumbUp
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
val MeshtasticIcons.Add: ImageVector
|
||||
get() = Icons.Rounded.Add
|
||||
val MeshtasticIcons.AddReaction: ImageVector
|
||||
get() = Icons.Rounded.AddReaction
|
||||
val MeshtasticIcons.Clear: ImageVector
|
||||
get() = Icons.Rounded.Clear
|
||||
val MeshtasticIcons.Close: ImageVector
|
||||
get() = Icons.Rounded.Close
|
||||
val MeshtasticIcons.Copy: ImageVector
|
||||
get() = Icons.Rounded.ContentCopy
|
||||
val MeshtasticIcons.Delete: ImageVector
|
||||
get() = Icons.Rounded.Delete
|
||||
val MeshtasticIcons.Edit: ImageVector
|
||||
get() = Icons.Rounded.Edit
|
||||
val MeshtasticIcons.More: ImageVector
|
||||
get() = Icons.Rounded.MoreVert
|
||||
val MeshtasticIcons.Refresh: ImageVector
|
||||
get() = Icons.Rounded.Refresh
|
||||
val MeshtasticIcons.Reply: ImageVector
|
||||
get() = Icons.AutoMirrored.Filled.Reply
|
||||
val MeshtasticIcons.Save: ImageVector
|
||||
get() = Icons.Rounded.Save
|
||||
val MeshtasticIcons.Search: ImageVector
|
||||
get() = Icons.Rounded.Search
|
||||
val MeshtasticIcons.Send: ImageVector
|
||||
get() = Icons.AutoMirrored.Filled.Send
|
||||
val MeshtasticIcons.Share: ImageVector
|
||||
get() = Icons.Rounded.Share
|
||||
val MeshtasticIcons.Sort: ImageVector
|
||||
get() = Icons.AutoMirrored.Filled.Sort
|
||||
val MeshtasticIcons.CloudDownload: ImageVector
|
||||
get() = Icons.Rounded.CloudDownload
|
||||
val MeshtasticIcons.Folder: ImageVector
|
||||
get() = Icons.Rounded.Folder
|
||||
val MeshtasticIcons.SystemUpdate: ImageVector
|
||||
get() = Icons.Rounded.SystemUpdate
|
||||
val MeshtasticIcons.SelectAll: ImageVector
|
||||
get() = Icons.Rounded.SelectAll
|
||||
val MeshtasticIcons.ThumbUp: ImageVector
|
||||
get() = Icons.Rounded.ThumbUp
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import org.meshtastic.core.ui.R
|
||||
|
||||
/** These are from Material Symbols drawables. */
|
||||
val MeshtasticIcons.Counter0: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_0_24px)
|
||||
|
||||
val MeshtasticIcons.Counter1: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_1_24px)
|
||||
|
||||
val MeshtasticIcons.Counter2: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_2_24px)
|
||||
|
||||
val MeshtasticIcons.Counter3: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_3_24px)
|
||||
|
||||
val MeshtasticIcons.Counter4: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_4_24px)
|
||||
|
||||
val MeshtasticIcons.Counter5: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_5_24px)
|
||||
|
||||
val MeshtasticIcons.Counter6: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_6_24px)
|
||||
|
||||
val MeshtasticIcons.Counter7: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_7_24px)
|
||||
|
||||
val MeshtasticIcons.Counter8: ImageVector
|
||||
@Composable get() = ImageVector.vectorResource(R.drawable.counter_8_24px)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,15 +14,25 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Fingerprint
|
||||
import androidx.compose.material.icons.rounded.Router
|
||||
import androidx.compose.material.icons.rounded.Work
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val MeshtasticIcons.HardwareModel: ImageVector
|
||||
get() = Icons.Rounded.Router
|
||||
val MeshtasticIcons.Role: ImageVector
|
||||
get() = Icons.Rounded.Work
|
||||
val MeshtasticIcons.NodeId: ImageVector
|
||||
get() = Icons.Rounded.Fingerprint
|
||||
|
||||
/**
|
||||
* This is from Material Symbols.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Bluetooth
|
||||
import androidx.compose.material.icons.rounded.Usb
|
||||
import androidx.compose.material.icons.rounded.Wifi
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
val MeshtasticIcons.Bluetooth: ImageVector
|
||||
get() = Icons.Rounded.Bluetooth
|
||||
val MeshtasticIcons.Usb: ImageVector
|
||||
get() = Icons.Rounded.Usb
|
||||
val MeshtasticIcons.Wifi: ImageVector
|
||||
get() = Icons.Rounded.Wifi
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.AccountCircle
|
||||
import androidx.compose.material.icons.rounded.Group
|
||||
import androidx.compose.material.icons.rounded.Groups
|
||||
import androidx.compose.material.icons.rounded.Person
|
||||
import androidx.compose.material.icons.rounded.PersonOff
|
||||
import androidx.compose.material.icons.rounded.PersonSearch
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
val MeshtasticIcons.Person: ImageVector
|
||||
get() = Icons.Rounded.Person
|
||||
val MeshtasticIcons.PersonOff: ImageVector
|
||||
get() = Icons.Rounded.PersonOff
|
||||
val MeshtasticIcons.Groups: ImageVector
|
||||
get() = Icons.Rounded.Groups
|
||||
val MeshtasticIcons.Group: ImageVector
|
||||
get() = Icons.Rounded.Group
|
||||
val MeshtasticIcons.AccountCircle: ImageVector
|
||||
get() = Icons.Rounded.AccountCircle
|
||||
val MeshtasticIcons.PersonSearch: ImageVector
|
||||
get() = Icons.Rounded.PersonSearch
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Fingerprint
|
||||
import androidx.compose.material.icons.rounded.KeyOff
|
||||
import androidx.compose.material.icons.rounded.Lock
|
||||
import androidx.compose.material.icons.rounded.LockOpen
|
||||
import androidx.compose.material.icons.rounded.Verified
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
val MeshtasticIcons.Lock: ImageVector
|
||||
get() = Icons.Rounded.Lock
|
||||
val MeshtasticIcons.LockOpen: ImageVector
|
||||
get() = Icons.Rounded.LockOpen
|
||||
val MeshtasticIcons.Warning: ImageVector
|
||||
get() = Icons.Rounded.Warning
|
||||
val MeshtasticIcons.KeyOff: ImageVector
|
||||
get() = Icons.Rounded.KeyOff
|
||||
val MeshtasticIcons.Verified: ImageVector
|
||||
get() = Icons.Rounded.Verified
|
||||
val MeshtasticIcons.Fingerprint: ImageVector
|
||||
get() = Icons.Rounded.Fingerprint
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,15 +14,31 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.CrueltyFree
|
||||
import androidx.compose.material.icons.rounded.Route
|
||||
import androidx.compose.material.icons.rounded.SignalCellularAlt
|
||||
import androidx.compose.material.icons.rounded.SsidChart
|
||||
import androidx.compose.material.icons.rounded.WifiChannel
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val MeshtasticIcons.Hops: ImageVector
|
||||
get() = Icons.Rounded.CrueltyFree
|
||||
val MeshtasticIcons.Route: ImageVector
|
||||
get() = Icons.Rounded.Route
|
||||
val MeshtasticIcons.Channel: ImageVector
|
||||
get() = Icons.Rounded.WifiChannel
|
||||
val MeshtasticIcons.ChannelUtilization: ImageVector
|
||||
get() = Icons.Rounded.SignalCellularAlt
|
||||
val MeshtasticIcons.AirUtilization: ImageVector
|
||||
get() = Icons.Rounded.SsidChart
|
||||
|
||||
val MeshtasticIcons.SignalCellular0Bar: ImageVector
|
||||
get() {
|
||||
if (signalCellular0Bar != null) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.SpeakerNotes
|
||||
import androidx.compose.material.icons.automirrored.filled.VolumeOff
|
||||
import androidx.compose.material.icons.automirrored.filled.VolumeUp
|
||||
import androidx.compose.material.icons.automirrored.twotone.VolumeMute
|
||||
import androidx.compose.material.icons.automirrored.twotone.VolumeUp
|
||||
import androidx.compose.material.icons.rounded.ArrowCircleUp
|
||||
import androidx.compose.material.icons.rounded.CheckCircleOutline
|
||||
import androidx.compose.material.icons.rounded.Cloud
|
||||
import androidx.compose.material.icons.rounded.CloudOff
|
||||
import androidx.compose.material.icons.rounded.Dangerous
|
||||
import androidx.compose.material.icons.rounded.History
|
||||
import androidx.compose.material.icons.rounded.NoCell
|
||||
import androidx.compose.material.icons.rounded.SpeakerNotesOff
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.StarBorder
|
||||
import androidx.compose.material.icons.twotone.Cloud
|
||||
import androidx.compose.material.icons.twotone.CloudDone
|
||||
import androidx.compose.material.icons.twotone.CloudOff
|
||||
import androidx.compose.material.icons.twotone.CloudSync
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
val MeshtasticIcons.Favorite: ImageVector
|
||||
get() = Icons.Rounded.Star
|
||||
val MeshtasticIcons.NotFavorite: ImageVector
|
||||
get() = Icons.Rounded.StarBorder
|
||||
val MeshtasticIcons.Muted: ImageVector
|
||||
get() = Icons.Rounded.SpeakerNotesOff
|
||||
val MeshtasticIcons.Unmuted: ImageVector
|
||||
get() = Icons.AutoMirrored.Filled.SpeakerNotes
|
||||
val MeshtasticIcons.VolumeOff: ImageVector
|
||||
get() = Icons.AutoMirrored.Filled.VolumeOff
|
||||
val MeshtasticIcons.VolumeUp: ImageVector
|
||||
get() = Icons.AutoMirrored.Filled.VolumeUp
|
||||
val MeshtasticIcons.History: ImageVector
|
||||
get() = Icons.Rounded.History
|
||||
val MeshtasticIcons.Cloud: ImageVector
|
||||
get() = Icons.Rounded.Cloud
|
||||
val MeshtasticIcons.CloudOff: ImageVector
|
||||
get() = Icons.Rounded.CloudOff
|
||||
val MeshtasticIcons.Unmessageable: ImageVector
|
||||
get() = Icons.Rounded.NoCell
|
||||
|
||||
val MeshtasticIcons.CloudDone: ImageVector
|
||||
get() = Icons.TwoTone.CloudDone
|
||||
val MeshtasticIcons.CloudSync: ImageVector
|
||||
get() = Icons.TwoTone.CloudSync
|
||||
val MeshtasticIcons.CloudOffTwoTone: ImageVector
|
||||
get() = Icons.TwoTone.CloudOff
|
||||
val MeshtasticIcons.CloudTwoTone: ImageVector
|
||||
get() = Icons.TwoTone.Cloud
|
||||
|
||||
val MeshtasticIcons.ArrowCircleUp: ImageVector
|
||||
get() = Icons.Rounded.ArrowCircleUp
|
||||
val MeshtasticIcons.Dangerous: ImageVector
|
||||
get() = Icons.Rounded.Dangerous
|
||||
|
||||
val MeshtasticIcons.VolumeUpTwoTone: ImageVector
|
||||
get() = Icons.AutoMirrored.TwoTone.VolumeUp
|
||||
val MeshtasticIcons.VolumeMuteTwoTone: ImageVector
|
||||
get() = Icons.AutoMirrored.TwoTone.VolumeMute
|
||||
|
||||
val MeshtasticIcons.CheckCircle: ImageVector
|
||||
get() = Icons.Rounded.CheckCircleOutline
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.icon
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Air
|
||||
import androidx.compose.material.icons.rounded.DataArray
|
||||
import androidx.compose.material.icons.rounded.ElectricBolt
|
||||
import androidx.compose.material.icons.rounded.Grass
|
||||
import androidx.compose.material.icons.rounded.People
|
||||
import androidx.compose.material.icons.rounded.SocialDistance
|
||||
import androidx.compose.material.icons.rounded.Speed
|
||||
import androidx.compose.material.icons.rounded.StackedLineChart
|
||||
import androidx.compose.material.icons.rounded.Thermostat
|
||||
import androidx.compose.material.icons.rounded.WaterDrop
|
||||
import androidx.compose.material.icons.twotone.SatelliteAlt
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
val MeshtasticIcons.Temperature: ImageVector
|
||||
get() = Icons.Rounded.Thermostat
|
||||
val MeshtasticIcons.Humidity: ImageVector
|
||||
get() = Icons.Rounded.WaterDrop
|
||||
val MeshtasticIcons.Pressure: ImageVector
|
||||
get() = Icons.Rounded.Speed
|
||||
val MeshtasticIcons.Soil: ImageVector
|
||||
get() = Icons.Rounded.Grass
|
||||
val MeshtasticIcons.Paxcount: ImageVector
|
||||
get() = Icons.Rounded.People
|
||||
val MeshtasticIcons.AirQuality: ImageVector
|
||||
get() = Icons.Rounded.Air
|
||||
val MeshtasticIcons.Power: ImageVector
|
||||
get() = Icons.Rounded.ElectricBolt
|
||||
val MeshtasticIcons.Distance: ImageVector
|
||||
get() = Icons.Rounded.SocialDistance
|
||||
val MeshtasticIcons.Satellites: ImageVector
|
||||
get() = Icons.TwoTone.SatelliteAlt
|
||||
val MeshtasticIcons.DataArray: ImageVector
|
||||
get() = Icons.Rounded.DataArray
|
||||
val MeshtasticIcons.Speed: ImageVector
|
||||
get() = Icons.Rounded.Speed
|
||||
val MeshtasticIcons.Chart: ImageVector
|
||||
get() = Icons.Rounded.StackedLineChart
|
||||
5
core/ui/src/main/res/drawable/counter_0_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_0_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM440,680L520,680Q553,680 576.5,656.5Q600,633 600,600L600,360Q600,327 576.5,303.5Q553,280 520,280L440,280Q407,280 383.5,303.5Q360,327 360,360L360,600Q360,633 383.5,656.5Q407,680 440,680ZM440,360L520,360Q520,360 520,360Q520,360 520,360L520,600Q520,600 520,600Q520,600 520,600L440,600Q440,600 440,600Q440,600 440,600L440,360Q440,360 440,360Q440,360 440,360Z"/>
|
||||
|
||||
</vector>
|
||||
5
core/ui/src/main/res/drawable/counter_1_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_1_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM460,360L460,640Q460,657 471.5,668.5Q483,680 500,680Q517,680 528.5,668.5Q540,657 540,640L540,320Q540,303 528.5,291.5Q517,280 500,280L420,280Q403,280 391.5,291.5Q380,303 380,320Q380,337 391.5,348.5Q403,360 420,360L460,360Z"/>
|
||||
|
||||
</vector>
|
||||
5
core/ui/src/main/res/drawable/counter_2_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_2_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM560,680Q577,680 588.5,668.5Q600,657 600,640Q600,623 588.5,611.5Q577,600 560,600L440,600L440,520Q440,520 440,520Q440,520 440,520L520,520Q553,520 576.5,496.5Q600,473 600,440L600,360Q600,327 576.5,303.5Q553,280 520,280L400,280Q383,280 371.5,291.5Q360,303 360,320Q360,337 371.5,348.5Q383,360 400,360L520,360Q520,360 520,360Q520,360 520,360L520,440Q520,440 520,440Q520,440 520,440L440,440Q407,440 383.5,463.5Q360,487 360,520L360,640Q360,657 371.5,668.5Q383,680 400,680L560,680Z"/>
|
||||
|
||||
</vector>
|
||||
5
core/ui/src/main/res/drawable/counter_3_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_3_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM400,680L520,680Q553,680 576.5,656.5Q600,633 600,600L600,540Q600,514 583,497Q566,480 540,480Q566,480 583,463Q600,446 600,420L600,360Q600,327 576.5,303.5Q553,280 520,280L400,280Q383,280 371.5,291.5Q360,303 360,320Q360,337 371.5,348.5Q383,360 400,360L520,360Q520,360 520,360Q520,360 520,360L520,440Q520,440 520,440Q520,440 520,440L480,440Q463,440 451.5,451.5Q440,463 440,480Q440,497 451.5,508.5Q463,520 480,520L520,520Q520,520 520,520Q520,520 520,520L520,600Q520,600 520,600Q520,600 520,600L400,600Q383,600 371.5,611.5Q360,623 360,640Q360,657 371.5,668.5Q383,680 400,680Z"/>
|
||||
|
||||
</vector>
|
||||
22
core/ui/src/main/res/drawable/counter_4_24px.xml
Normal file
22
core/ui/src/main/res/drawable/counter_4_24px.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM520,680L600,680L600,280L520,280L520,440L440,440L440,280L360,280L360,520L520,520L520,680Z"/>
|
||||
|
||||
</vector>
|
||||
5
core/ui/src/main/res/drawable/counter_5_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_5_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM400,680L520,680Q553,680 576.5,656.5Q600,633 600,600L600,520Q600,487 576.5,463.5Q553,440 520,440L440,440L440,360L560,360Q577,360 588.5,348.5Q600,337 600,320Q600,303 588.5,291.5Q577,280 560,280L400,280Q383,280 371.5,291.5Q360,303 360,320L360,480Q360,497 371.5,508.5Q383,520 400,520L520,520Q520,520 520,520Q520,520 520,520L520,600Q520,600 520,600Q520,600 520,600L400,600Q383,600 371.5,611.5Q360,623 360,640Q360,657 371.5,668.5Q383,680 400,680Z"/>
|
||||
|
||||
</vector>
|
||||
5
core/ui/src/main/res/drawable/counter_6_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_6_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM440,680L520,680Q553,680 576.5,656.5Q600,633 600,600L600,520Q600,487 576.5,463.5Q553,440 520,440L440,440L440,360Q440,360 440,360Q440,360 440,360L520,360Q537,360 548.5,348.5Q560,337 560,320Q560,303 548.5,291.5Q537,280 520,280L440,280Q407,280 383.5,303.5Q360,327 360,360L360,600Q360,633 383.5,656.5Q407,680 440,680ZM440,520L520,520Q520,520 520,520Q520,520 520,520L520,600Q520,600 520,600Q520,600 520,600L440,600Q440,600 440,600Q440,600 440,600L440,520Z"/>
|
||||
|
||||
</vector>
|
||||
5
core/ui/src/main/res/drawable/counter_7_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_7_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M520,360L452,632Q447,650 459,665Q471,680 490,680Q504,680 514,672Q524,664 527,651L598,370Q600,365 600,361Q600,357 600,352Q600,323 579.5,301.5Q559,280 530,280L400,280Q383,280 371.5,291.5Q360,303 360,320Q360,337 371.5,348.5Q383,360 400,360L520,360ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
|
||||
|
||||
</vector>
|
||||
5
core/ui/src/main/res/drawable/counter_8_24px.xml
Normal file
5
core/ui/src/main/res/drawable/counter_8_24px.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM440,680L520,680Q553,680 576.5,656.5Q600,633 600,600L600,540Q600,515 582.5,497.5Q565,480 540,480Q565,480 582.5,462.5Q600,445 600,420L600,360Q600,327 576.5,303.5Q553,280 520,280L440,280Q407,280 383.5,303.5Q360,327 360,360L360,420Q360,445 377.5,462.5Q395,480 420,480Q395,480 377.5,497.5Q360,515 360,540L360,600Q360,633 383.5,656.5Q407,680 440,680ZM440,360L520,360Q520,360 520,360Q520,360 520,360L520,440Q520,440 520,440Q520,440 520,440L440,440Q440,440 440,440Q440,440 440,440L440,360Q440,360 440,360Q440,360 440,360ZM440,600Q440,600 440,600Q440,600 440,600L440,520Q440,520 440,520Q440,520 440,520L520,520Q520,520 520,520Q520,520 520,520L520,600Q520,600 520,600Q520,600 520,600L440,600Z"/>
|
||||
|
||||
</vector>
|
||||
|
|
@ -42,16 +42,6 @@ import androidx.compose.foundation.text.TextAutoSize
|
|||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.CloudDownload
|
||||
import androidx.compose.material.icons.filled.Dangerous
|
||||
import androidx.compose.material.icons.filled.Folder
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.SystemUpdate
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material.icons.rounded.Bluetooth
|
||||
import androidx.compose.material.icons.rounded.Usb
|
||||
import androidx.compose.material.icons.rounded.Wifi
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
|
|
@ -146,6 +136,17 @@ import org.meshtastic.core.strings.i_know_what_i_m_doing
|
|||
import org.meshtastic.core.strings.learn_more
|
||||
import org.meshtastic.core.strings.okay
|
||||
import org.meshtastic.core.strings.save
|
||||
import org.meshtastic.core.ui.icon.Bluetooth
|
||||
import org.meshtastic.core.ui.icon.CheckCircle
|
||||
import org.meshtastic.core.ui.icon.CloudDownload
|
||||
import org.meshtastic.core.ui.icon.Dangerous
|
||||
import org.meshtastic.core.ui.icon.Folder
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.core.ui.icon.SystemUpdate
|
||||
import org.meshtastic.core.ui.icon.Usb
|
||||
import org.meshtastic.core.ui.icon.Warning
|
||||
import org.meshtastic.core.ui.icon.Wifi
|
||||
|
||||
private const val CYCLE_DELAY_MS = 4500L
|
||||
|
||||
|
|
@ -413,7 +414,7 @@ private fun ReadyState(
|
|||
},
|
||||
modifier = Modifier.fillMaxWidth().height(56.dp),
|
||||
) {
|
||||
Icon(Icons.Default.Folder, contentDescription = null)
|
||||
Icon(MeshtasticIcons.Folder, contentDescription = null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(stringResource(Res.string.firmware_update_select_file))
|
||||
}
|
||||
|
|
@ -428,10 +429,10 @@ private fun ReadyState(
|
|||
Icon(
|
||||
imageVector =
|
||||
when (state.updateMethod) {
|
||||
FirmwareUpdateMethod.Ble -> Icons.Rounded.Bluetooth
|
||||
FirmwareUpdateMethod.Usb -> Icons.Rounded.Usb
|
||||
FirmwareUpdateMethod.Wifi -> Icons.Rounded.Wifi
|
||||
else -> Icons.Default.SystemUpdate
|
||||
FirmwareUpdateMethod.Ble -> MeshtasticIcons.Bluetooth
|
||||
FirmwareUpdateMethod.Usb -> MeshtasticIcons.Usb
|
||||
FirmwareUpdateMethod.Wifi -> MeshtasticIcons.Wifi
|
||||
else -> MeshtasticIcons.SystemUpdate
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
|
|
@ -459,7 +460,7 @@ private fun DisclaimerDialog(updateMethod: FirmwareUpdateMethod, onDismissReques
|
|||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
Icons.Default.Warning,
|
||||
MeshtasticIcons.Warning,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.size(16.dp),
|
||||
|
|
@ -616,7 +617,7 @@ private fun BootloaderWarningCard(deviceHardware: DeviceHardware, onDismissForDe
|
|||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Warning,
|
||||
imageVector = MeshtasticIcons.Warning,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onErrorContainer,
|
||||
)
|
||||
|
|
@ -708,7 +709,7 @@ private fun ProgressContent(
|
|||
) {
|
||||
if (isDownloading) {
|
||||
Icon(
|
||||
Icons.Default.CloudDownload,
|
||||
MeshtasticIcons.CloudDownload,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
|
|
@ -818,7 +819,7 @@ private fun CyclingMessages() {
|
|||
@Composable
|
||||
private fun VerificationFailedState(onRetry: () -> Unit, onIgnore: () -> Unit) {
|
||||
Icon(
|
||||
Icons.Default.Warning,
|
||||
MeshtasticIcons.Warning,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(64.dp),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
|
|
@ -833,7 +834,7 @@ private fun VerificationFailedState(onRetry: () -> Unit, onIgnore: () -> Unit) {
|
|||
Spacer(Modifier.height(32.dp))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
OutlinedButton(onClick = onRetry) {
|
||||
Icon(Icons.Default.Refresh, contentDescription = null)
|
||||
Icon(MeshtasticIcons.Refresh, contentDescription = null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(stringResource(Res.string.firmware_update_retry))
|
||||
}
|
||||
|
|
@ -844,7 +845,7 @@ private fun VerificationFailedState(onRetry: () -> Unit, onIgnore: () -> Unit) {
|
|||
@Composable
|
||||
private fun ErrorState(error: String, onRetry: () -> Unit) {
|
||||
Icon(
|
||||
Icons.Default.Dangerous,
|
||||
MeshtasticIcons.Dangerous,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(64.dp),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
|
|
@ -858,7 +859,7 @@ private fun ErrorState(error: String, onRetry: () -> Unit) {
|
|||
)
|
||||
Spacer(Modifier.height(32.dp))
|
||||
OutlinedButton(onClick = onRetry) {
|
||||
Icon(Icons.Default.Refresh, contentDescription = null)
|
||||
Icon(MeshtasticIcons.Refresh, contentDescription = null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(stringResource(Res.string.firmware_update_retry))
|
||||
}
|
||||
|
|
@ -871,7 +872,7 @@ private fun SuccessState(onDone: () -> Unit) {
|
|||
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(
|
||||
Icons.Default.CheckCircle,
|
||||
MeshtasticIcons.CheckCircle,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(100.dp),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
package org.meshtastic.feature.map
|
||||
|
||||
import android.Manifest // Added for Accompanist
|
||||
import android.Manifest
|
||||
import android.graphics.Paint
|
||||
import android.text.format.DateUtils
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
|
|
@ -34,14 +34,14 @@ import androidx.compose.foundation.layout.wrapContentWidth
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Lens
|
||||
import androidx.compose.material.icons.filled.LocationDisabled
|
||||
import androidx.compose.material.icons.filled.PinDrop
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material.icons.outlined.Layers
|
||||
import androidx.compose.material.icons.outlined.MyLocation
|
||||
import androidx.compose.material.icons.outlined.Tune
|
||||
import androidx.compose.material.icons.rounded.Check
|
||||
import androidx.compose.material.icons.rounded.Lens
|
||||
import androidx.compose.material.icons.rounded.LocationDisabled
|
||||
import androidx.compose.material.icons.rounded.PinDrop
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.Checkbox
|
||||
|
|
@ -77,8 +77,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi // Added for Accompanist
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState // Added for Accompanist
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
|
|
@ -758,7 +758,7 @@ fun MapView(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Star,
|
||||
imageVector = Icons.Rounded.Star,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
|
|
@ -783,7 +783,7 @@ fun MapView(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PinDrop,
|
||||
imageVector = Icons.Rounded.PinDrop,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
|
|
@ -808,7 +808,7 @@ fun MapView(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Lens,
|
||||
imageVector = Icons.Rounded.Lens,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
|
|
@ -834,7 +834,7 @@ fun MapView(
|
|||
if (myLocationOverlay == null) {
|
||||
Icons.Outlined.MyLocation
|
||||
} else {
|
||||
Icons.Default.LocationDisabled
|
||||
Icons.Rounded.LocationDisabled
|
||||
},
|
||||
contentDescription = stringResource(Res.string.toggle_my_position),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.map.component
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
|
|
@ -23,7 +22,7 @@ import androidx.compose.animation.core.tween
|
|||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -51,7 +50,7 @@ fun DownloadButton(enabled: Boolean, onClick: () -> Unit) {
|
|||
) {
|
||||
FloatingActionButton(onClick = onClick, contentColor = MaterialTheme.colorScheme.primary) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
imageVector = Icons.Rounded.Download,
|
||||
contentDescription = stringResource(Res.string.map_download_region),
|
||||
modifier = Modifier.scale(1.25f),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
package org.meshtastic.feature.map.component
|
||||
|
||||
import android.app.DatePickerDialog
|
||||
import android.app.TimePickerDialog
|
||||
import android.text.format.DateFormat
|
||||
import android.widget.DatePicker
|
||||
import android.widget.TimePicker
|
||||
import androidx.compose.foundation.Image
|
||||
|
|
@ -37,8 +35,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CalendarMonth
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material.icons.rounded.CalendarMonth
|
||||
import androidx.compose.material.icons.rounded.Lock
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -179,7 +177,7 @@ fun EditWaypointDialog(
|
|||
modifier = Modifier.fillMaxWidth().size(48.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(imageVector = Icons.Default.Lock, contentDescription = stringResource(Res.string.locked))
|
||||
Image(imageVector = Icons.Rounded.Lock, contentDescription = stringResource(Res.string.locked))
|
||||
Text(stringResource(Res.string.locked))
|
||||
Switch(
|
||||
modifier = Modifier.fillMaxWidth().wrapContentWidth(Alignment.End),
|
||||
|
|
@ -221,7 +219,7 @@ fun EditWaypointDialog(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
imageVector = Icons.Default.CalendarMonth,
|
||||
imageVector = Icons.Rounded.CalendarMonth,
|
||||
contentDescription = stringResource(Res.string.expires),
|
||||
)
|
||||
Text(stringResource(Res.string.expires))
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.filled.TripOrigin
|
||||
import androidx.compose.material.icons.rounded.TripOrigin
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
|
|
@ -501,7 +501,7 @@ fun MapView(
|
|||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = androidx.compose.material.icons.Icons.Default.TripOrigin,
|
||||
imageVector = androidx.compose.material.icons.Icons.Rounded.TripOrigin,
|
||||
contentDescription = stringResource(Res.string.track_point),
|
||||
tint = color,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ import androidx.compose.foundation.shape.CircleShape
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CalendarMonth
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material.icons.rounded.CalendarMonth
|
||||
import androidx.compose.material.icons.rounded.Lock
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
|
|
@ -180,7 +180,7 @@ fun EditWaypointDialog(
|
|||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Image(
|
||||
imageVector = Icons.Default.Lock,
|
||||
imageVector = Icons.Rounded.Lock,
|
||||
contentDescription = stringResource(Res.string.locked),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
|
@ -199,7 +199,7 @@ fun EditWaypointDialog(
|
|||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Image(
|
||||
imageVector = Icons.Default.CalendarMonth,
|
||||
imageVector = Icons.Rounded.CalendarMonth,
|
||||
contentDescription = stringResource(Res.string.expires),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,18 +14,17 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.map.component
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.LocationDisabled
|
||||
import androidx.compose.material.icons.filled.Navigation
|
||||
import androidx.compose.material.icons.outlined.Layers
|
||||
import androidx.compose.material.icons.outlined.Map
|
||||
import androidx.compose.material.icons.outlined.MyLocation
|
||||
import androidx.compose.material.icons.outlined.Navigation
|
||||
import androidx.compose.material.icons.outlined.Tune
|
||||
import androidx.compose.material.icons.rounded.LocationDisabled
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.HorizontalFloatingToolbar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -122,7 +121,7 @@ fun MapControlsOverlay(
|
|||
MapButton(
|
||||
icon =
|
||||
if (isLocationTrackingEnabled) {
|
||||
Icons.Default.LocationDisabled
|
||||
Icons.Rounded.LocationDisabled
|
||||
} else {
|
||||
Icons.Outlined.MyLocation
|
||||
},
|
||||
|
|
|
|||
|
|
@ -48,19 +48,19 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.Reply
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material.icons.filled.ArrowDownward
|
||||
import androidx.compose.material.icons.filled.ChatBubbleOutline
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.SelectAll
|
||||
import androidx.compose.material.icons.filled.SpeakerNotes
|
||||
import androidx.compose.material.icons.filled.SpeakerNotesOff
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material.icons.rounded.ArrowDownward
|
||||
import androidx.compose.material.icons.rounded.ChatBubbleOutline
|
||||
import androidx.compose.material.icons.rounded.ContentCopy
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.FilterList
|
||||
import androidx.compose.material.icons.rounded.FilterListOff
|
||||
import androidx.compose.material.icons.rounded.MoreVert
|
||||
import androidx.compose.material.icons.rounded.SelectAll
|
||||
import androidx.compose.material.icons.rounded.SpeakerNotes
|
||||
import androidx.compose.material.icons.rounded.SpeakerNotesOff
|
||||
import androidx.compose.material.icons.rounded.Visibility
|
||||
import androidx.compose.material.icons.rounded.VisibilityOff
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
|
|
@ -499,7 +499,7 @@ private fun BoxScope.ScrollToBottomFab(coroutineScope: CoroutineScope, listState
|
|||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowDownward,
|
||||
imageVector = Icons.Rounded.ArrowDownward,
|
||||
contentDescription = stringResource(Res.string.scroll_to_bottom),
|
||||
)
|
||||
}
|
||||
|
|
@ -683,13 +683,13 @@ private fun ActionModeTopBar(selectedCount: Int, onAction: (MessageMenuAction) -
|
|||
},
|
||||
actions = {
|
||||
IconButton(onClick = { onAction(MessageMenuAction.ClipboardCopy) }) {
|
||||
Icon(imageVector = Icons.Default.ContentCopy, contentDescription = stringResource(Res.string.copy))
|
||||
Icon(imageVector = Icons.Rounded.ContentCopy, contentDescription = stringResource(Res.string.copy))
|
||||
}
|
||||
IconButton(onClick = { onAction(MessageMenuAction.Delete) }) {
|
||||
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.delete))
|
||||
Icon(imageVector = Icons.Rounded.Delete, contentDescription = stringResource(Res.string.delete))
|
||||
}
|
||||
IconButton(onClick = { onAction(MessageMenuAction.SelectAll) }) {
|
||||
Icon(imageVector = Icons.Default.SelectAll, contentDescription = stringResource(Res.string.select_all))
|
||||
Icon(imageVector = Icons.Rounded.SelectAll, contentDescription = stringResource(Res.string.select_all))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -775,7 +775,7 @@ private fun MessageTopBarActions(
|
|||
var expanded by remember { mutableStateOf(false) }
|
||||
Box {
|
||||
IconButton(onClick = { expanded = true }, enabled = true) {
|
||||
Icon(imageVector = Icons.Default.MoreVert, contentDescription = stringResource(Res.string.overflow_menu))
|
||||
Icon(imageVector = Icons.Rounded.MoreVert, contentDescription = stringResource(Res.string.overflow_menu))
|
||||
}
|
||||
OverFlowMenu(
|
||||
expanded = expanded,
|
||||
|
|
@ -828,7 +828,7 @@ private fun QuickChatToggleMenuItem(showQuickChat: Boolean, onDismiss: () -> Uni
|
|||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = if (showQuickChat) Icons.Default.SpeakerNotesOff else Icons.Default.SpeakerNotes,
|
||||
imageVector = if (showQuickChat) Icons.Rounded.SpeakerNotesOff else Icons.Rounded.SpeakerNotes,
|
||||
contentDescription = title,
|
||||
)
|
||||
},
|
||||
|
|
@ -844,7 +844,7 @@ private fun QuickChatOptionsMenuItem(onDismiss: () -> Unit, onNavigate: () -> Un
|
|||
onDismiss()
|
||||
onNavigate()
|
||||
},
|
||||
leadingIcon = { Icon(imageVector = Icons.Default.ChatBubbleOutline, contentDescription = title) },
|
||||
leadingIcon = { Icon(imageVector = Icons.Rounded.ChatBubbleOutline, contentDescription = title) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -859,7 +859,7 @@ private fun FilteredMessagesMenuItem(showFiltered: Boolean, count: Int, onDismis
|
|||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = if (showFiltered) Icons.Default.VisibilityOff else Icons.Default.Visibility,
|
||||
imageVector = if (showFiltered) Icons.Rounded.VisibilityOff else Icons.Rounded.Visibility,
|
||||
contentDescription = title,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.messaging
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -35,10 +34,10 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.DragHandle
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.FastForward
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.DragHandle
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.FastForward
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
|
|
@ -148,7 +147,7 @@ fun QuickChatScreen(
|
|||
onClick = { showActionDialog = QuickChatAction(position = actions.size) },
|
||||
modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp),
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(Res.string.add))
|
||||
Icon(imageVector = Icons.Rounded.Add, contentDescription = stringResource(Res.string.add))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -231,9 +230,9 @@ private fun EditQuickChatDialog(
|
|||
|
||||
val (text, icon) =
|
||||
if (isInstant) {
|
||||
Res.string.quick_chat_instant to Icons.Default.FastForward
|
||||
Res.string.quick_chat_instant to Icons.Rounded.FastForward
|
||||
} else {
|
||||
Res.string.quick_chat_append to Icons.Default.Add
|
||||
Res.string.quick_chat_append to Icons.Rounded.Add
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
|
|
@ -338,7 +337,7 @@ private fun QuickChatItem(
|
|||
leadingContent = {
|
||||
if (action.mode == QuickChatAction.Mode.Instant) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.FastForward,
|
||||
imageVector = Icons.Rounded.FastForward,
|
||||
contentDescription = stringResource(Res.string.quick_chat_instant),
|
||||
)
|
||||
}
|
||||
|
|
@ -349,12 +348,12 @@ private fun QuickChatItem(
|
|||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
IconButton(onClick = { onEdit(action) }, modifier = Modifier.size(48.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
imageVector = Icons.Rounded.Edit,
|
||||
contentDescription = stringResource(Res.string.quick_chat_edit),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.DragHandle,
|
||||
imageVector = Icons.Rounded.DragHandle,
|
||||
contentDescription = stringResource(Res.string.quick_chat),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Reply
|
||||
import androidx.compose.material.icons.filled.AddReaction
|
||||
import androidx.compose.material.icons.rounded.AddReaction
|
||||
import androidx.compose.material.icons.twotone.AddLink
|
||||
import androidx.compose.material.icons.twotone.Cloud
|
||||
import androidx.compose.material.icons.twotone.CloudDone
|
||||
|
|
@ -61,7 +61,7 @@ internal fun ReactionButton(onSendReaction: (String) -> Unit = {}) {
|
|||
)
|
||||
}
|
||||
IconButton(onClick = { showEmojiPickerDialog = true }) {
|
||||
Icon(imageVector = Icons.Default.AddReaction, contentDescription = stringResource(Res.string.react))
|
||||
Icon(imageVector = Icons.Rounded.AddReaction, contentDescription = stringResource(Res.string.react))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AddReaction
|
||||
import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Reply
|
||||
import androidx.compose.material.icons.filled.SelectAll
|
||||
import androidx.compose.material.icons.rounded.AddReaction
|
||||
import androidx.compose.material.icons.rounded.ContentCopy
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Reply
|
||||
import androidx.compose.material.icons.rounded.SelectAll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -95,25 +95,25 @@ fun MessageActionsContent(
|
|||
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(Res.string.reply)) },
|
||||
leadingContent = { Icon(Icons.Default.Reply, contentDescription = stringResource(Res.string.reply)) },
|
||||
leadingContent = { Icon(Icons.Rounded.Reply, contentDescription = stringResource(Res.string.reply)) },
|
||||
modifier = Modifier.clickable(onClick = onReply),
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(Res.string.copy)) },
|
||||
leadingContent = { Icon(Icons.Default.ContentCopy, contentDescription = stringResource(Res.string.copy)) },
|
||||
leadingContent = { Icon(Icons.Rounded.ContentCopy, contentDescription = stringResource(Res.string.copy)) },
|
||||
modifier = Modifier.clickable(onClick = onCopy),
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(Res.string.select)) },
|
||||
leadingContent = { Icon(Icons.Default.SelectAll, contentDescription = stringResource(Res.string.select)) },
|
||||
leadingContent = { Icon(Icons.Rounded.SelectAll, contentDescription = stringResource(Res.string.select)) },
|
||||
modifier = Modifier.clickable(onClick = onSelect),
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(Res.string.delete)) },
|
||||
leadingContent = { Icon(Icons.Default.Delete, contentDescription = stringResource(Res.string.delete)) },
|
||||
leadingContent = { Icon(Icons.Rounded.Delete, contentDescription = stringResource(Res.string.delete)) },
|
||||
modifier = Modifier.clickable(onClick = onDelete),
|
||||
)
|
||||
}
|
||||
|
|
@ -146,7 +146,7 @@ private fun QuickEmojiRow(quickEmojis: List<String>, onReact: (String) -> Unit,
|
|||
modifier = Modifier.size(40.dp).background(MaterialTheme.colorScheme.surfaceVariant, CircleShape),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.AddReaction,
|
||||
Icons.Rounded.AddReaction,
|
||||
contentDescription = "More reactions",
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.meshtastic.feature.messaging.component
|
||||
|
||||
import android.content.ClipData
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
|
|
@ -29,16 +30,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Cloud
|
||||
import androidx.compose.material.icons.filled.FormatQuote
|
||||
import androidx.compose.material.icons.twotone.AddLink
|
||||
import androidx.compose.material.icons.twotone.Cloud
|
||||
import androidx.compose.material.icons.twotone.CloudDone
|
||||
import androidx.compose.material.icons.twotone.CloudOff
|
||||
import androidx.compose.material.icons.twotone.CloudUpload
|
||||
import androidx.compose.material.icons.twotone.HowToReg
|
||||
import androidx.compose.material.icons.twotone.Link
|
||||
import androidx.compose.material.icons.twotone.Warning
|
||||
import androidx.compose.material.icons.rounded.FormatQuote
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -52,18 +44,20 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.ClipEntry
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.database.entity.Reaction
|
||||
import org.meshtastic.core.database.model.Message
|
||||
|
|
@ -71,7 +65,6 @@ import org.meshtastic.core.database.model.Node
|
|||
import org.meshtastic.core.model.MessageStatus
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.filter_message_label
|
||||
import org.meshtastic.core.strings.hops_away_template
|
||||
import org.meshtastic.core.strings.message_delivery_status
|
||||
import org.meshtastic.core.strings.reply
|
||||
import org.meshtastic.core.strings.sample_message
|
||||
|
|
@ -82,6 +75,14 @@ import org.meshtastic.core.ui.component.Rssi
|
|||
import org.meshtastic.core.ui.component.Snr
|
||||
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
|
||||
import org.meshtastic.core.ui.emoji.EmojiPicker
|
||||
import org.meshtastic.core.ui.icon.Cloud
|
||||
import org.meshtastic.core.ui.icon.CloudDone
|
||||
import org.meshtastic.core.ui.icon.CloudOffTwoTone
|
||||
import org.meshtastic.core.ui.icon.CloudSync
|
||||
import org.meshtastic.core.ui.icon.CloudTwoTone
|
||||
import org.meshtastic.core.ui.icon.Hops
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Warning
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.theme.MessageItemColors
|
||||
|
||||
|
|
@ -123,7 +124,8 @@ internal fun MessageItem(
|
|||
),
|
||||
) {
|
||||
var activeSheet by remember { mutableStateOf<ActiveSheet?>(null) }
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val clipboardManager = LocalClipboard.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
|
||||
if (activeSheet != null) {
|
||||
|
|
@ -143,7 +145,11 @@ internal fun MessageItem(
|
|||
onMoreReactions = { activeSheet = ActiveSheet.Emoji },
|
||||
onCopy = {
|
||||
activeSheet = null
|
||||
clipboardManager.setText(AnnotatedString(message.text))
|
||||
coroutineScope.launch {
|
||||
clipboardManager.setClipEntry(
|
||||
ClipEntry(ClipData.newPlainText("message", message.text)),
|
||||
)
|
||||
}
|
||||
},
|
||||
onSelect = {
|
||||
activeSheet = null
|
||||
|
|
@ -222,7 +228,7 @@ internal fun MessageItem(
|
|||
)
|
||||
if (message.viaMqtt) {
|
||||
Icon(
|
||||
Icons.Default.Cloud,
|
||||
MeshtasticIcons.Cloud,
|
||||
contentDescription = stringResource(Res.string.via_mqtt),
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
|
|
@ -278,10 +284,21 @@ internal fun MessageItem(
|
|||
Rssi(message.rssi)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
text = stringResource(Res.string.hops_away_template, message.hopsAway),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = MeshtasticIcons.Hops,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(14.dp),
|
||||
tint = cardColors.contentColor.copy(alpha = 0.7f),
|
||||
)
|
||||
Text(
|
||||
text = message.hopsAway.toString(),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (containsBel) {
|
||||
|
|
@ -341,14 +358,14 @@ private enum class ActiveSheet {
|
|||
private fun MessageStatusIcon(status: MessageStatus, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val icon =
|
||||
when (status) {
|
||||
MessageStatus.RECEIVED -> Icons.TwoTone.HowToReg
|
||||
MessageStatus.QUEUED -> Icons.TwoTone.CloudUpload
|
||||
MessageStatus.DELIVERED -> Icons.TwoTone.CloudDone
|
||||
MessageStatus.SFPP_ROUTING -> Icons.TwoTone.AddLink
|
||||
MessageStatus.SFPP_CONFIRMED -> Icons.TwoTone.Link
|
||||
MessageStatus.ENROUTE -> Icons.TwoTone.Cloud
|
||||
MessageStatus.ERROR -> Icons.TwoTone.CloudOff
|
||||
else -> Icons.TwoTone.Warning
|
||||
MessageStatus.RECEIVED -> MeshtasticIcons.CloudDone
|
||||
MessageStatus.QUEUED -> MeshtasticIcons.CloudSync
|
||||
MessageStatus.DELIVERED -> MeshtasticIcons.CloudDone
|
||||
MessageStatus.SFPP_ROUTING -> MeshtasticIcons.CloudSync
|
||||
MessageStatus.SFPP_CONFIRMED -> MeshtasticIcons.CloudDone
|
||||
MessageStatus.ENROUTE -> MeshtasticIcons.CloudTwoTone
|
||||
MessageStatus.ERROR -> MeshtasticIcons.CloudOffTwoTone
|
||||
else -> MeshtasticIcons.Warning
|
||||
}
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
|
|
@ -392,7 +409,7 @@ private fun OriginalMessageSnippet(
|
|||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.FormatQuote,
|
||||
Icons.Rounded.FormatQuote,
|
||||
contentDescription = stringResource(Res.string.reply),
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import androidx.compose.foundation.lazy.LazyRow
|
|||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AddReaction
|
||||
import androidx.compose.material.icons.rounded.AddReaction
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -67,7 +67,6 @@ import org.meshtastic.core.model.util.getShortDateTime
|
|||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.delivery_confirmed
|
||||
import org.meshtastic.core.strings.error
|
||||
import org.meshtastic.core.strings.hops_away_template
|
||||
import org.meshtastic.core.strings.message_delivery_status
|
||||
import org.meshtastic.core.strings.message_status_enroute
|
||||
import org.meshtastic.core.strings.message_status_queued
|
||||
|
|
@ -77,6 +76,8 @@ import org.meshtastic.core.ui.component.BottomSheetDialog
|
|||
import org.meshtastic.core.ui.component.Rssi
|
||||
import org.meshtastic.core.ui.component.Snr
|
||||
import org.meshtastic.core.ui.emoji.EmojiPickerDialog
|
||||
import org.meshtastic.core.ui.icon.Hops
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.feature.messaging.DeliveryInfo
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
|
|
@ -186,7 +187,7 @@ private fun AddReactionButton(modifier: Modifier = Modifier, onSendReaction: (St
|
|||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.2f)),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.AddReaction,
|
||||
imageVector = Icons.Rounded.AddReaction,
|
||||
contentDescription = stringResource(Res.string.react),
|
||||
modifier = Modifier.padding(6.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
|
|
@ -300,10 +301,21 @@ internal fun ReactionDialog(
|
|||
Rssi(reaction.rssi)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
text = stringResource(Res.string.hops_away_template, reaction.hopsAway),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = MeshtasticIcons.Hops,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(14.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
|
||||
)
|
||||
Text(
|
||||
text = reaction.hopsAway.toString(),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ private fun PrimaryActionsRow(
|
|||
|
||||
IconToggleButton(checked = node.isFavorite, onCheckedChange = { onFavoriteClick() }) {
|
||||
Icon(
|
||||
imageVector = if (node.isFavorite) Icons.Default.Star else Icons.Default.StarBorder,
|
||||
imageVector = if (node.isFavorite) Icons.Rounded.Star else Icons.Rounded.StarBorder,
|
||||
contentDescription = stringResource(Res.string.favorite),
|
||||
tint = if (node.isFavorite) Color.Yellow else LocalContentColor.current,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ package org.meshtastic.feature.node.component
|
|||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ForkLeft
|
||||
import androidx.compose.material.icons.filled.Icecream
|
||||
import androidx.compose.material.icons.filled.Memory
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.rounded.ForkLeft
|
||||
import androidx.compose.material.icons.rounded.Icecream
|
||||
import androidx.compose.material.icons.rounded.Memory
|
||||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -63,7 +63,7 @@ fun AdministrationSection(
|
|||
Column {
|
||||
ListItem(
|
||||
text = stringResource(Res.string.request_metadata),
|
||||
leadingIcon = Icons.Default.Memory,
|
||||
leadingIcon = Icons.Rounded.Memory,
|
||||
trailingIcon = null,
|
||||
onClick = {
|
||||
onAction(NodeDetailAction.TriggerServiceAction(ServiceAction.GetDeviceMetadata(node.num)))
|
||||
|
|
@ -74,7 +74,7 @@ fun AdministrationSection(
|
|||
|
||||
ListItem(
|
||||
text = stringResource(Res.string.remote_admin),
|
||||
leadingIcon = Icons.Default.Settings,
|
||||
leadingIcon = Icons.Rounded.Settings,
|
||||
enabled = metricsState.isLocal || node.metadata != null,
|
||||
) {
|
||||
onAction(NodeDetailAction.Navigate(SettingsRoutes.Settings(node.num)))
|
||||
|
|
@ -101,8 +101,8 @@ private fun FirmwareSection(
|
|||
firmwareEdition?.let { edition ->
|
||||
val icon =
|
||||
when (edition) {
|
||||
MeshProtos.FirmwareEdition.VANILLA -> Icons.Default.Icecream
|
||||
else -> Icons.Default.ForkLeft
|
||||
MeshProtos.FirmwareEdition.VANILLA -> Icons.Rounded.Icecream
|
||||
else -> Icons.Rounded.ForkLeft
|
||||
}
|
||||
|
||||
ListItem(
|
||||
|
|
@ -138,7 +138,7 @@ private fun FirmwareVersionItems(
|
|||
|
||||
ListItem(
|
||||
text = stringResource(Res.string.installed_firmware_version),
|
||||
leadingIcon = Icons.Default.Memory,
|
||||
leadingIcon = Icons.Rounded.Memory,
|
||||
supportingText = version.substringBeforeLast("."),
|
||||
copyable = true,
|
||||
leadingIconTint = statusColor,
|
||||
|
|
@ -149,7 +149,7 @@ private fun FirmwareVersionItems(
|
|||
|
||||
ListItem(
|
||||
text = stringResource(Res.string.latest_stable_firmware),
|
||||
leadingIcon = Icons.Default.Memory,
|
||||
leadingIcon = Icons.Rounded.Memory,
|
||||
supportingText = latestStable.id.substringBeforeLast(".").replace("v", ""),
|
||||
copyable = true,
|
||||
leadingIconTint = MaterialTheme.colorScheme.StatusGreen,
|
||||
|
|
@ -161,7 +161,7 @@ private fun FirmwareVersionItems(
|
|||
|
||||
ListItem(
|
||||
text = stringResource(Res.string.latest_alpha_firmware),
|
||||
leadingIcon = Icons.Default.Memory,
|
||||
leadingIcon = Icons.Rounded.Memory,
|
||||
supportingText = latestAlpha.id.substringBeforeLast(".").replace("v", ""),
|
||||
copyable = true,
|
||||
leadingIconTint = MaterialTheme.colorScheme.StatusYellow,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Tsunami
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun ChannelInfo(
|
||||
channel: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Tsunami,
|
||||
contentDescription = "Channel",
|
||||
text = channel.toString(),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun ChannelInfoPreview() {
|
||||
AppTheme { ChannelInfo(channel = 2) }
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import androidx.compose.foundation.Canvas
|
||||
|
|
@ -27,8 +26,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ErrorOutline
|
||||
import androidx.compose.material.icons.filled.GpsFixed
|
||||
import androidx.compose.material.icons.rounded.ErrorOutline
|
||||
import androidx.compose.material.icons.rounded.GpsFixed
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -153,7 +152,7 @@ fun CompassSheetContent(
|
|||
)
|
||||
// Quick way to re-request a fresh fix without leaving the compass sheet
|
||||
Button(onClick = onRequestPosition, modifier = Modifier.fillMaxWidth()) {
|
||||
Icon(imageVector = Icons.Default.GpsFixed, contentDescription = null)
|
||||
Icon(imageVector = Icons.Rounded.GpsFixed, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(Res.string.exchange_position))
|
||||
}
|
||||
|
|
@ -190,7 +189,7 @@ private fun WarningList(
|
|||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ErrorOutline,
|
||||
imageVector = Icons.Rounded.ErrorOutline,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onErrorContainer,
|
||||
)
|
||||
|
|
@ -205,13 +204,13 @@ private fun WarningList(
|
|||
|
||||
if (warnings.contains(CompassWarning.NO_LOCATION_PERMISSION)) {
|
||||
Button(onClick = onRequestPermission, modifier = Modifier.fillMaxWidth()) {
|
||||
Icon(imageVector = Icons.Default.GpsFixed, contentDescription = null)
|
||||
Icon(imageVector = Icons.Rounded.GpsFixed, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(Res.string.compass_no_location_permission))
|
||||
}
|
||||
} else if (warnings.contains(CompassWarning.LOCATION_DISABLED)) {
|
||||
Button(onClick = onOpenLocationSettings, modifier = Modifier.fillMaxWidth()) {
|
||||
Icon(imageVector = Icons.Default.GpsFixed, contentDescription = null)
|
||||
Icon(imageVector = Icons.Rounded.GpsFixed, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(Res.string.compass_location_disabled))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ package org.meshtastic.feature.node.component
|
|||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.CircularWavyProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -36,6 +34,8 @@ import androidx.compose.ui.graphics.drawscope.Stroke
|
|||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
internal const val COOL_DOWN_TIME_MS = 30000L
|
||||
|
|
@ -147,7 +147,7 @@ fun CooldownOutlinedIconButton(
|
|||
private fun CooldownOutlinedIconButtonPreview() {
|
||||
AppTheme {
|
||||
CooldownOutlinedIconButton(onClick = {}, cooldownTimestamp = System.currentTimeMillis() - 15000L) {
|
||||
Icon(imageVector = Icons.Default.Refresh, contentDescription = null)
|
||||
Icon(imageVector = MeshtasticIcons.Refresh, contentDescription = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ import androidx.compose.material.icons.automirrored.filled.Message
|
|||
import androidx.compose.material.icons.automirrored.filled.VolumeOff
|
||||
import androidx.compose.material.icons.automirrored.filled.VolumeUp
|
||||
import androidx.compose.material.icons.automirrored.outlined.VolumeMute
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material.icons.filled.StarBorder
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.QrCode2
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.StarBorder
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -170,7 +170,7 @@ private fun PrimaryActionsRow(
|
|||
|
||||
IconToggleButton(checked = node.isFavorite, onCheckedChange = { onFavoriteClick() }) {
|
||||
Icon(
|
||||
imageVector = if (node.isFavorite) Icons.Default.Star else Icons.Default.StarBorder,
|
||||
imageVector = if (node.isFavorite) Icons.Rounded.Star else Icons.Rounded.StarBorder,
|
||||
contentDescription = stringResource(Res.string.favorite),
|
||||
tint = if (node.isFavorite) Color.Yellow else LocalContentColor.current,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Router
|
||||
import androidx.compose.material.icons.rounded.Router
|
||||
import androidx.compose.material.icons.twotone.Verified
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -78,7 +78,7 @@ fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
|
|||
?: deviceHardware.displayName
|
||||
ListItem(
|
||||
text = stringResource(Res.string.hardware),
|
||||
leadingIcon = Icons.Default.Router,
|
||||
leadingIcon = Icons.Rounded.Router,
|
||||
supportingText = deviceText,
|
||||
copyable = true,
|
||||
trailingIcon = null,
|
||||
|
|
|
|||
|
|
@ -20,17 +20,17 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Air
|
||||
import androidx.compose.material.icons.filled.BlurOn
|
||||
import androidx.compose.material.icons.filled.Bolt
|
||||
import androidx.compose.material.icons.filled.Height
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.filled.Power
|
||||
import androidx.compose.material.icons.filled.Scale
|
||||
import androidx.compose.material.icons.filled.Speed
|
||||
import androidx.compose.material.icons.filled.Thermostat
|
||||
import androidx.compose.material.icons.filled.WaterDrop
|
||||
import androidx.compose.material.icons.outlined.Navigation
|
||||
import androidx.compose.material.icons.rounded.Air
|
||||
import androidx.compose.material.icons.rounded.BlurOn
|
||||
import androidx.compose.material.icons.rounded.Bolt
|
||||
import androidx.compose.material.icons.rounded.Height
|
||||
import androidx.compose.material.icons.rounded.LightMode
|
||||
import androidx.compose.material.icons.rounded.Power
|
||||
import androidx.compose.material.icons.rounded.Scale
|
||||
import androidx.compose.material.icons.rounded.Speed
|
||||
import androidx.compose.material.icons.rounded.Thermostat
|
||||
import androidx.compose.material.icons.rounded.WaterDrop
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -77,7 +77,7 @@ internal fun EnvironmentMetrics(
|
|||
VectorMetricInfo(
|
||||
Res.string.temperature,
|
||||
temperature.toTempString(isFahrenheit),
|
||||
Icons.Default.Thermostat,
|
||||
Icons.Rounded.Thermostat,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ internal fun EnvironmentMetrics(
|
|||
VectorMetricInfo(
|
||||
Res.string.humidity,
|
||||
"%.0f%%".format(relativeHumidity),
|
||||
Icons.Default.WaterDrop,
|
||||
Icons.Rounded.WaterDrop,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ internal fun EnvironmentMetrics(
|
|||
VectorMetricInfo(
|
||||
Res.string.pressure,
|
||||
"%.0f hPa".format(barometricPressure),
|
||||
Icons.Default.Speed,
|
||||
Icons.Rounded.Speed,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -104,29 +104,29 @@ internal fun EnvironmentMetrics(
|
|||
VectorMetricInfo(
|
||||
Res.string.gas_resistance,
|
||||
"%.0f MΩ".format(gasResistance),
|
||||
Icons.Default.BlurOn,
|
||||
Icons.Rounded.BlurOn,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasVoltage()) {
|
||||
add(VectorMetricInfo(Res.string.voltage, "%.2fV".format(voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(Res.string.voltage, "%.2fV".format(voltage), Icons.Rounded.Bolt))
|
||||
}
|
||||
if (hasCurrent()) {
|
||||
add(VectorMetricInfo(Res.string.current, "%.1fmA".format(current), Icons.Default.Power))
|
||||
add(VectorMetricInfo(Res.string.current, "%.1fmA".format(current), Icons.Rounded.Power))
|
||||
}
|
||||
if (hasIaq()) add(VectorMetricInfo(Res.string.iaq, iaq.toString(), Icons.Default.Air))
|
||||
if (hasIaq()) add(VectorMetricInfo(Res.string.iaq, iaq.toString(), Icons.Rounded.Air))
|
||||
if (hasDistance()) {
|
||||
add(
|
||||
VectorMetricInfo(
|
||||
Res.string.distance,
|
||||
distance.toSmallDistanceString(displayUnits),
|
||||
Icons.Default.Height,
|
||||
Icons.Rounded.Height,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (hasLux()) add(VectorMetricInfo(Res.string.lux, "%.0f lx".format(lux), Icons.Default.LightMode))
|
||||
if (hasLux()) add(VectorMetricInfo(Res.string.lux, "%.0f lx".format(lux), Icons.Rounded.LightMode))
|
||||
if (hasUvLux()) {
|
||||
add(VectorMetricInfo(Res.string.uv_lux, "%.0f lx".format(uvLux), Icons.Default.LightMode))
|
||||
add(VectorMetricInfo(Res.string.uv_lux, "%.0f lx".format(uvLux), Icons.Rounded.LightMode))
|
||||
}
|
||||
if (hasWindSpeed()) {
|
||||
@Suppress("MagicNumber")
|
||||
|
|
@ -141,7 +141,7 @@ internal fun EnvironmentMetrics(
|
|||
)
|
||||
}
|
||||
if (hasWeight()) {
|
||||
add(VectorMetricInfo(Res.string.weight, "%.2f kg".format(weight), Icons.Default.Scale))
|
||||
add(VectorMetricInfo(Res.string.weight, "%.2f kg".format(weight), Icons.Rounded.Scale))
|
||||
}
|
||||
if (hasTemperature() && hasRelativeHumidity()) {
|
||||
val dewPoint = UnitConversions.calculateDewPoint(temperature, relativeHumidity)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
|
|
@ -29,8 +28,8 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.Link
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -77,7 +76,7 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi
|
|||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Link, contentDescription = stringResource(Res.string.view_release))
|
||||
Icon(imageVector = Icons.Rounded.Link, contentDescription = stringResource(Res.string.view_release))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(Res.string.view_release))
|
||||
}
|
||||
|
|
@ -93,7 +92,7 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi
|
|||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = stringResource(Res.string.download))
|
||||
Icon(imageVector = Icons.Rounded.Download, contentDescription = stringResource(Res.string.download))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(Res.string.download))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.CrueltyFree
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.hops_away
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun HopsInfo(hops: Int, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.CrueltyFree,
|
||||
contentDescription = stringResource(Res.string.hops_away),
|
||||
text = hops.toString(),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun HopsInfoPreview() {
|
||||
AppTheme { HopsInfo(hops = 3) }
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -28,6 +27,7 @@ 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.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.meshtastic.core.ui.icon.Elevation
|
||||
|
|
@ -41,6 +41,7 @@ fun IconInfo(
|
|||
contentDescription: String,
|
||||
modifier: Modifier = Modifier,
|
||||
text: String? = null,
|
||||
style: TextStyle = MaterialTheme.typography.labelMedium,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
|
|
@ -55,7 +56,7 @@ fun IconInfo(
|
|||
contentDescription = contentDescription,
|
||||
tint = contentColor,
|
||||
)
|
||||
text?.let { Text(text = it, style = MaterialTheme.typography.labelMedium, color = contentColor) }
|
||||
text?.let { Text(text = it, style = style, color = contentColor) }
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import android.content.Intent
|
|||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material.icons.rounded.LocationOn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -87,7 +87,7 @@ fun LinkedCoordinatesItem(node: Node, displayUnits: DisplayUnits = DisplayUnits.
|
|||
)
|
||||
},
|
||||
text = stringResource(Res.string.last_position_update),
|
||||
leadingIcon = Icons.Default.LocationOn,
|
||||
leadingIcon = Icons.Rounded.LocationOn,
|
||||
supportingText = "$ago • $coordinates$elevationText",
|
||||
trailingContent = Icons.AutoMirrored.Rounded.KeyboardArrowRight.icon(),
|
||||
onClick = {
|
||||
|
|
|
|||
|
|
@ -30,17 +30,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.Cloud
|
||||
import androidx.compose.material.icons.filled.History
|
||||
import androidx.compose.material.icons.filled.KeyOff
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material.icons.filled.Numbers
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.SignalCellularAlt
|
||||
import androidx.compose.material.icons.filled.Verified
|
||||
import androidx.compose.material.icons.filled.Work
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material.icons.rounded.Numbers
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -82,6 +72,17 @@ import org.meshtastic.core.strings.supported
|
|||
import org.meshtastic.core.strings.uptime
|
||||
import org.meshtastic.core.strings.user_id
|
||||
import org.meshtastic.core.strings.via_mqtt
|
||||
import org.meshtastic.core.ui.icon.ArrowCircleUp
|
||||
import org.meshtastic.core.ui.icon.ChannelUtilization
|
||||
import org.meshtastic.core.ui.icon.Cloud
|
||||
import org.meshtastic.core.ui.icon.History
|
||||
import org.meshtastic.core.ui.icon.Hops
|
||||
import org.meshtastic.core.ui.icon.KeyOff
|
||||
import org.meshtastic.core.ui.icon.Lock
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Person
|
||||
import org.meshtastic.core.ui.icon.Role
|
||||
import org.meshtastic.core.ui.icon.Verified
|
||||
import org.meshtastic.core.ui.util.formatAgo
|
||||
|
||||
@Composable
|
||||
|
|
@ -107,7 +108,7 @@ private fun MismatchKeyWarning(modifier: Modifier = Modifier) {
|
|||
Column(modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyOff,
|
||||
imageVector = MeshtasticIcons.KeyOff,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onErrorContainer,
|
||||
)
|
||||
|
|
@ -129,7 +130,6 @@ private fun MismatchKeyWarning(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun MainNodeDetails(node: Node) {
|
||||
Column {
|
||||
|
|
@ -151,19 +151,6 @@ private fun MainNodeDetails(node: Node) {
|
|||
SectionDivider()
|
||||
PublicKeyItem(publicKey.toByteArray())
|
||||
}
|
||||
|
||||
if (!node.nodeStatus.isNullOrEmpty()) {
|
||||
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f))
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
InfoItem(
|
||||
label = "Status",
|
||||
value = node.nodeStatus!!,
|
||||
icon = Icons.Default.CheckCircle,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,13 +160,13 @@ private fun NameAndRoleRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.short_name),
|
||||
value = node.user.shortName.ifEmpty { "???" },
|
||||
icon = Icons.Default.Person,
|
||||
icon = MeshtasticIcons.Person,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
InfoItem(
|
||||
label = stringResource(Res.string.role),
|
||||
value = node.user.role.name,
|
||||
icon = Icons.Default.Work,
|
||||
icon = MeshtasticIcons.Role,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
|
@ -191,13 +178,13 @@ private fun NodeIdentificationRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.node_id),
|
||||
value = DataPacket.nodeNumToDefaultId(node.num),
|
||||
icon = Icons.Default.Numbers,
|
||||
icon = Icons.Rounded.Numbers,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
InfoItem(
|
||||
label = stringResource(Res.string.node_number),
|
||||
value = node.num.toUInt().toString(),
|
||||
icon = Icons.Default.Numbers,
|
||||
icon = Icons.Rounded.Numbers,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
|
@ -209,14 +196,14 @@ private fun HearsAndHopsRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.node_sort_last_heard),
|
||||
value = formatAgo(node.lastHeard),
|
||||
icon = Icons.Default.History,
|
||||
icon = MeshtasticIcons.History,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (node.hopsAway >= 0) {
|
||||
InfoItem(
|
||||
label = stringResource(Res.string.hops_away),
|
||||
value = node.hopsAway.toString(),
|
||||
icon = Icons.Default.SignalCellularAlt,
|
||||
icon = MeshtasticIcons.Hops,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -231,14 +218,14 @@ private fun UserAndUptimeRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.user_id),
|
||||
value = node.user.id,
|
||||
icon = Icons.Default.Person,
|
||||
icon = MeshtasticIcons.Person,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (node.deviceMetrics.uptimeSeconds > 0) {
|
||||
InfoItem(
|
||||
label = stringResource(Res.string.uptime),
|
||||
value = formatUptime(node.deviceMetrics.uptimeSeconds),
|
||||
icon = Icons.Default.CheckCircle,
|
||||
icon = MeshtasticIcons.ArrowCircleUp,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -254,7 +241,7 @@ private fun SignalRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.snr),
|
||||
value = "%.1f dB".format(node.snr),
|
||||
icon = Icons.Default.SignalCellularAlt,
|
||||
icon = MeshtasticIcons.ChannelUtilization,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -264,7 +251,7 @@ private fun SignalRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.rssi),
|
||||
value = "%d dBm".format(node.rssi),
|
||||
icon = Icons.Default.SignalCellularAlt,
|
||||
icon = MeshtasticIcons.ChannelUtilization,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -280,7 +267,7 @@ private fun MqttAndVerificationRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.via_mqtt),
|
||||
value = "Yes",
|
||||
icon = Icons.Default.Cloud,
|
||||
icon = MeshtasticIcons.Cloud,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -290,7 +277,7 @@ private fun MqttAndVerificationRow(node: Node) {
|
|||
InfoItem(
|
||||
label = stringResource(Res.string.supported),
|
||||
value = "Verified",
|
||||
icon = Icons.Default.Verified,
|
||||
icon = MeshtasticIcons.Verified,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -327,7 +314,7 @@ private fun PublicKeyItem(publicKeyBytes: ByteArray) {
|
|||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Lock,
|
||||
imageVector = MeshtasticIcons.Lock,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(14.dp),
|
||||
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.8f),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -32,8 +31,8 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Sort
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.rounded.Clear
|
||||
import androidx.compose.material.icons.rounded.Search
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
|
|
@ -156,13 +155,13 @@ private fun NodeFilterTextField(filterText: String, onTextChange: (String) -> Un
|
|||
)
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Search, contentDescription = stringResource(Res.string.node_filter_placeholder))
|
||||
Icon(Icons.Rounded.Search, contentDescription = stringResource(Res.string.node_filter_placeholder))
|
||||
},
|
||||
onValueChange = onTextChange,
|
||||
trailingIcon = {
|
||||
if (filterText.isNotEmpty() || isFocused) {
|
||||
Icon(
|
||||
Icons.Default.Clear,
|
||||
Icons.Rounded.Clear,
|
||||
contentDescription = stringResource(Res.string.desc_node_filter_clear),
|
||||
modifier =
|
||||
Modifier.clickable {
|
||||
|
|
|
|||
|
|
@ -20,12 +20,11 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
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.material3.Card
|
||||
|
|
@ -47,15 +46,31 @@ import androidx.compose.ui.unit.dp
|
|||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.database.model.isUnmessageableRole
|
||||
import org.meshtastic.core.model.util.UnitConversions.celsiusToFahrenheit
|
||||
import org.meshtastic.core.model.util.toDistanceString
|
||||
import org.meshtastic.core.service.ConnectionState
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.elevation_suffix
|
||||
import org.meshtastic.core.strings.unknown_username
|
||||
import org.meshtastic.core.ui.component.AirQualityInfo
|
||||
import org.meshtastic.core.ui.component.DistanceInfo
|
||||
import org.meshtastic.core.ui.component.ElevationInfo
|
||||
import org.meshtastic.core.ui.component.HardwareInfo
|
||||
import org.meshtastic.core.ui.component.HumidityInfo
|
||||
import org.meshtastic.core.ui.component.LastHeardInfo
|
||||
import org.meshtastic.core.ui.component.MaterialBatteryInfo
|
||||
import org.meshtastic.core.ui.component.NodeChip
|
||||
import org.meshtastic.core.ui.component.NodeIdInfo
|
||||
import org.meshtastic.core.ui.component.NodeKeyStatusIcon
|
||||
import org.meshtastic.core.ui.component.PaxcountInfo
|
||||
import org.meshtastic.core.ui.component.PowerInfo
|
||||
import org.meshtastic.core.ui.component.PressureInfo
|
||||
import org.meshtastic.core.ui.component.RoleInfo
|
||||
import org.meshtastic.core.ui.component.SatelliteCountInfo
|
||||
import org.meshtastic.core.ui.component.SignalInfo
|
||||
import org.meshtastic.core.ui.component.SoilMoistureInfo
|
||||
import org.meshtastic.core.ui.component.SoilTemperatureInfo
|
||||
import org.meshtastic.core.ui.component.TemperatureInfo
|
||||
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig
|
||||
|
|
@ -80,7 +95,17 @@ fun NodeItem(
|
|||
val isFavorite = remember(thatNode) { thatNode.isFavorite }
|
||||
val isMuted = remember(thatNode) { thatNode.isMuted }
|
||||
val isIgnored = thatNode.isIgnored
|
||||
val longName = thatNode.user.longName.ifEmpty { stringResource(Res.string.unknown_username) }
|
||||
val originalLongName = thatNode.user.longName.ifEmpty { stringResource(Res.string.unknown_username) }
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
val longName =
|
||||
remember(originalLongName) {
|
||||
if (originalLongName.length > 20) {
|
||||
"${originalLongName.take(20)}…"
|
||||
} else {
|
||||
originalLongName
|
||||
}
|
||||
}
|
||||
val isThisNode = remember(thatNode) { thisNode?.num == thatNode.num }
|
||||
val system = remember(distanceUnits) { DisplayConfig.DisplayUnits.forNumber(distanceUnits) }
|
||||
val distance =
|
||||
|
|
@ -115,121 +140,183 @@ fun NodeItem(
|
|||
}
|
||||
}
|
||||
|
||||
Card(modifier = modifier.fillMaxWidth().defaultMinSize(minHeight = 80.dp), colors = cardColors) {
|
||||
Card(modifier = modifier.fillMaxWidth(), colors = cardColors) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.combinedClickable(onClick = onClick, onLongClick = onLongClick).fillMaxWidth().padding(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
NodeChip(node = thatNode)
|
||||
NodeItemHeader(
|
||||
thatNode = thatNode,
|
||||
isThisNode = isThisNode,
|
||||
longName = longName,
|
||||
style = style,
|
||||
isIgnored = isIgnored,
|
||||
isFavorite = isFavorite,
|
||||
isMuted = isMuted,
|
||||
isUnmessageable = unmessageable,
|
||||
connectionState = connectionState,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
|
||||
NodeKeyStatusIcon(
|
||||
hasPKC = thatNode.hasPKC,
|
||||
mismatchKey = thatNode.mismatchKey,
|
||||
publicKey = thatNode.user.publicKey,
|
||||
modifier = Modifier.size(32.dp),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = longName,
|
||||
style = MaterialTheme.typography.titleMediumEmphasized.copy(fontStyle = style),
|
||||
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
|
||||
softWrap = true,
|
||||
)
|
||||
LastHeardInfo(lastHeard = thatNode.lastHeard, contentColor = contentColor)
|
||||
NodeStatusIcons(
|
||||
isThisNode = isThisNode,
|
||||
isFavorite = isFavorite,
|
||||
isMuted = isMuted,
|
||||
isUnmessageable = unmessageable,
|
||||
connectionState = connectionState,
|
||||
NodeItemMetrics(thatNode = thatNode, distance = distance, system = system, contentColor = contentColor)
|
||||
|
||||
SignalInfo(node = thatNode, isThisNode = isThisNode, contentColor = contentColor)
|
||||
|
||||
NodeItemEnvironment(thatNode = thatNode, tempInFahrenheit = tempInFahrenheit, contentColor = contentColor)
|
||||
|
||||
NodeItemFooter(thatNode = thatNode, contentColor = contentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
private fun NodeItemHeader(
|
||||
thatNode: Node,
|
||||
isThisNode: Boolean,
|
||||
longName: String,
|
||||
style: FontStyle,
|
||||
isIgnored: Boolean,
|
||||
isFavorite: Boolean,
|
||||
isMuted: Boolean,
|
||||
isUnmessageable: Boolean,
|
||||
connectionState: ConnectionState,
|
||||
contentColor: Color,
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
NodeChip(node = thatNode)
|
||||
|
||||
NodeKeyStatusIcon(
|
||||
hasPKC = thatNode.hasPKC,
|
||||
mismatchKey = thatNode.mismatchKey,
|
||||
publicKey = thatNode.user.publicKey,
|
||||
modifier = Modifier.size(32.dp),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = longName,
|
||||
style = MaterialTheme.typography.titleMediumEmphasized.copy(fontStyle = style),
|
||||
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
|
||||
softWrap = true,
|
||||
)
|
||||
LastHeardInfo(lastHeard = thatNode.lastHeard, contentColor = contentColor)
|
||||
NodeStatusIcons(
|
||||
isThisNode = isThisNode,
|
||||
isFavorite = isFavorite,
|
||||
isMuted = isMuted,
|
||||
isUnmessageable = isUnmessageable,
|
||||
connectionState = connectionState,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NodeItemMetrics(
|
||||
thatNode: Node,
|
||||
distance: String?,
|
||||
system: DisplayConfig.DisplayUnits,
|
||||
contentColor: Color,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
if (thatNode.batteryLevel > 0 || thatNode.voltage > 0f) {
|
||||
MaterialBatteryInfo(level = thatNode.batteryLevel, voltage = thatNode.voltage, contentColor = contentColor)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
if (distance != null) {
|
||||
DistanceInfo(distance = distance, contentColor = contentColor)
|
||||
}
|
||||
thatNode.validPosition?.let { position ->
|
||||
ElevationInfo(
|
||||
altitude = position.altitude,
|
||||
system = system,
|
||||
suffix = stringResource(Res.string.elevation_suffix),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (thatNode.batteryLevel > 0 || thatNode.voltage > 0f) {
|
||||
MaterialBatteryInfo(
|
||||
level = thatNode.batteryLevel,
|
||||
voltage = thatNode.voltage,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (distance != null) {
|
||||
DistanceInfo(distance = distance, contentColor = contentColor)
|
||||
}
|
||||
thatNode.validPosition?.let { position ->
|
||||
ElevationInfo(
|
||||
altitude = position.altitude,
|
||||
system = system,
|
||||
suffix = stringResource(Res.string.elevation_suffix),
|
||||
contentColor = contentColor,
|
||||
)
|
||||
val satCount = position.satsInView
|
||||
if (satCount > 0) {
|
||||
SatelliteCountInfo(satCount = satCount, contentColor = contentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
itemVerticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SignalInfo(node = thatNode, isThisNode = isThisNode, contentColor = contentColor)
|
||||
}
|
||||
val telemetryStrings = thatNode.getTelemetryStrings(tempInFahrenheit)
|
||||
|
||||
if (telemetryStrings.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
telemetryStrings.forEach { telemetryString ->
|
||||
Text(text = telemetryString, style = MaterialTheme.typography.bodySmall, color = contentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!thatNode.nodeStatus.isNullOrEmpty()) {
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = thatNode.nodeStatus!!,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = contentColor,
|
||||
maxLines = 2,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val labelStyle =
|
||||
if (thatNode.isUnknownUser) {
|
||||
MaterialTheme.typography.labelSmall.copy(fontStyle = FontStyle.Italic)
|
||||
} else {
|
||||
MaterialTheme.typography.labelSmall
|
||||
}
|
||||
Text(text = thatNode.user.hwModel.name, style = labelStyle)
|
||||
Text(text = thatNode.user.role.name, style = labelStyle)
|
||||
Text(text = thatNode.user.id.ifEmpty { "???" }, style = labelStyle)
|
||||
val satCount = thatNode.validPosition?.satsInView ?: 0
|
||||
if (satCount > 0) {
|
||||
SatelliteCountInfo(satCount = satCount, contentColor = contentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun NodeItemEnvironment(thatNode: Node, tempInFahrenheit: Boolean, contentColor: Color) {
|
||||
val env = thatNode.environmentMetrics
|
||||
val pax = thatNode.paxcounter
|
||||
if (thatNode.hasEnvironmentMetrics || pax.ble != 0 || pax.wifi != 0) {
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
if (pax.ble != 0 || pax.wifi != 0) {
|
||||
PaxcountInfo(pax = "B:${pax.ble} W:${pax.wifi}", contentColor = contentColor)
|
||||
}
|
||||
if (env.temperature != 0f) {
|
||||
val temp =
|
||||
if (tempInFahrenheit) {
|
||||
"%.1f°F".format(celsiusToFahrenheit(env.temperature))
|
||||
} else {
|
||||
"%.1f°C".format(env.temperature)
|
||||
}
|
||||
TemperatureInfo(temp = temp, contentColor = contentColor)
|
||||
}
|
||||
if (env.relativeHumidity != 0f) {
|
||||
HumidityInfo(humidity = "%.0f%%".format(env.relativeHumidity), contentColor = contentColor)
|
||||
}
|
||||
if (env.barometricPressure != 0f) {
|
||||
PressureInfo(pressure = "%.1fhPa".format(env.barometricPressure), contentColor = contentColor)
|
||||
}
|
||||
if (env.soilTemperature != 0f) {
|
||||
val temp =
|
||||
if (tempInFahrenheit) {
|
||||
"%.1f°F".format(celsiusToFahrenheit(env.soilTemperature))
|
||||
} else {
|
||||
"%.1f°C".format(env.soilTemperature)
|
||||
}
|
||||
SoilTemperatureInfo(temp = temp, contentColor = contentColor)
|
||||
}
|
||||
if (env.soilMoisture != 0 && env.soilTemperature != 0f) {
|
||||
SoilMoistureInfo(moisture = "${env.soilMoisture}%", contentColor = contentColor)
|
||||
}
|
||||
if (env.voltage != 0f) {
|
||||
PowerInfo(value = "%.2fV".format(env.voltage), contentColor = contentColor)
|
||||
}
|
||||
if (env.current != 0f) {
|
||||
PowerInfo(value = "%.1fmA".format(env.current), contentColor = contentColor)
|
||||
}
|
||||
if (env.iaq != 0) {
|
||||
AirQualityInfo(iaq = "${env.iaq}", contentColor = contentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NodeItemFooter(thatNode: Node, contentColor: Color) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
HardwareInfo(hwModel = thatNode.user.hwModel.name, contentColor = contentColor)
|
||||
RoleInfo(role = thatNode.user.role.name, contentColor = contentColor)
|
||||
NodeIdInfo(id = thatNode.user.id.ifEmpty { "???" }, contentColor = contentColor)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = false, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
fun NodeInfoSimplePreview() {
|
||||
|
|
|
|||
|
|
@ -19,17 +19,9 @@ package org.meshtastic.feature.node.component
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.VolumeOff
|
||||
import androidx.compose.material.icons.rounded.NoCell
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.twotone.Cloud
|
||||
import androidx.compose.material.icons.twotone.CloudDone
|
||||
import androidx.compose.material.icons.twotone.CloudOff
|
||||
import androidx.compose.material.icons.twotone.CloudSync
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.PlainTooltip
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -55,6 +47,14 @@ import org.meshtastic.core.strings.favorite
|
|||
import org.meshtastic.core.strings.mute_always
|
||||
import org.meshtastic.core.strings.unmessageable
|
||||
import org.meshtastic.core.strings.unmonitored_or_infrastructure
|
||||
import org.meshtastic.core.ui.icon.CloudDone
|
||||
import org.meshtastic.core.ui.icon.CloudOffTwoTone
|
||||
import org.meshtastic.core.ui.icon.CloudSync
|
||||
import org.meshtastic.core.ui.icon.CloudTwoTone
|
||||
import org.meshtastic.core.ui.icon.Favorite
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Unmessageable
|
||||
import org.meshtastic.core.ui.icon.VolumeOff
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusOrange
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
|
||||
|
|
@ -68,29 +68,33 @@ fun NodeStatusIcons(
|
|||
isFavorite: Boolean,
|
||||
isMuted: Boolean,
|
||||
connectionState: ConnectionState,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = LocalContentColor.current,
|
||||
) {
|
||||
Row(modifier = Modifier.padding(4.dp)) {
|
||||
Row(modifier = modifier.padding(4.dp)) {
|
||||
if (isThisNode) {
|
||||
ThisNodeStatusBadge(connectionState)
|
||||
}
|
||||
|
||||
if (isUnmessageable) {
|
||||
StatusBadge(
|
||||
imageVector = Icons.Rounded.NoCell,
|
||||
imageVector = MeshtasticIcons.Unmessageable,
|
||||
contentDescription = Res.string.unmessageable,
|
||||
tooltipText = Res.string.unmonitored_or_infrastructure,
|
||||
tint = contentColor,
|
||||
)
|
||||
}
|
||||
if (isMuted && !isThisNode) {
|
||||
StatusBadge(
|
||||
imageVector = Icons.AutoMirrored.Filled.VolumeOff,
|
||||
imageVector = MeshtasticIcons.VolumeOff,
|
||||
contentDescription = Res.string.mute_always,
|
||||
tooltipText = Res.string.mute_always,
|
||||
tint = contentColor,
|
||||
)
|
||||
}
|
||||
if (isFavorite && !isThisNode) {
|
||||
StatusBadge(
|
||||
imageVector = Icons.Rounded.Star,
|
||||
imageVector = MeshtasticIcons.Favorite,
|
||||
contentDescription = Res.string.favorite,
|
||||
tooltipText = Res.string.favorite,
|
||||
tint = MaterialTheme.colorScheme.StatusYellow,
|
||||
|
|
@ -132,7 +136,7 @@ private fun ThisNodeStatusBadge(connectionState: ConnectionState) {
|
|||
@Composable
|
||||
private fun ConnectedStatusIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.CloudDone,
|
||||
imageVector = MeshtasticIcons.CloudDone,
|
||||
contentDescription = stringResource(Res.string.connected),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.StatusGreen,
|
||||
|
|
@ -142,7 +146,7 @@ private fun ConnectedStatusIcon() {
|
|||
@Composable
|
||||
private fun ConnectingStatusIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.CloudSync,
|
||||
imageVector = MeshtasticIcons.CloudSync,
|
||||
contentDescription = stringResource(Res.string.connecting),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.StatusOrange,
|
||||
|
|
@ -152,7 +156,7 @@ private fun ConnectingStatusIcon() {
|
|||
@Composable
|
||||
private fun DisconnectedStatusIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.CloudOff,
|
||||
imageVector = MeshtasticIcons.CloudOffTwoTone,
|
||||
contentDescription = stringResource(Res.string.disconnected),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.StatusRed,
|
||||
|
|
@ -162,7 +166,7 @@ private fun DisconnectedStatusIcon() {
|
|||
@Composable
|
||||
private fun DeviceSleepStatusIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Cloud,
|
||||
imageVector = MeshtasticIcons.CloudTwoTone,
|
||||
contentDescription = stringResource(Res.string.device_sleeping),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.StatusYellow,
|
||||
|
|
@ -175,21 +179,19 @@ private fun StatusBadge(
|
|||
imageVector: ImageVector,
|
||||
contentDescription: StringResource,
|
||||
tooltipText: StringResource,
|
||||
tint: Color = Color.Unspecified,
|
||||
tint: Color = LocalContentColor.current,
|
||||
) {
|
||||
TooltipBox(
|
||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
||||
tooltip = { PlainTooltip { Text(stringResource(tooltipText)) } },
|
||||
state = rememberTooltipState(),
|
||||
) {
|
||||
IconButton(onClick = {}, modifier = Modifier.size(24.dp)) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = stringResource(contentDescription),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = stringResource(contentDescription),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -25,7 +24,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -87,7 +86,7 @@ fun NotesSection(node: Node, onSaveNotes: (Int, String) -> Unit, modifier: Modif
|
|||
},
|
||||
enabled = edited,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Save, contentDescription = stringResource(Res.string.save))
|
||||
Icon(imageVector = Icons.Rounded.Save, contentDescription = stringResource(Res.string.save))
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Explore
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material.icons.filled.SocialDistance
|
||||
import androidx.compose.material.icons.rounded.Explore
|
||||
import androidx.compose.material.icons.rounded.LocationOn
|
||||
import androidx.compose.material.icons.rounded.SocialDistance
|
||||
import androidx.compose.material3.AssistChip
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
|
|
@ -132,7 +132,7 @@ private fun PositionMap(node: Node, distance: String?) {
|
|||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(Icons.Default.SocialDistance, null, Modifier.size(16.dp))
|
||||
Icon(Icons.Rounded.SocialDistance, null, Modifier.size(16.dp))
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(distance, style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ private fun PositionActionButtons(
|
|||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
) {
|
||||
Icon(Icons.Default.LocationOn, null, Modifier.size(18.dp))
|
||||
Icon(Icons.Rounded.LocationOn, null, Modifier.size(18.dp))
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(
|
||||
text = stringResource(Res.string.exchange_position),
|
||||
|
|
@ -179,7 +179,7 @@ private fun PositionActionButtons(
|
|||
modifier = Modifier.weight(COMPASS_BUTTON_WEIGHT),
|
||||
shape = MaterialTheme.shapes.large,
|
||||
) {
|
||||
Icon(Icons.Default.Explore, null, Modifier.size(18.dp))
|
||||
Icon(Icons.Rounded.Explore, null, Modifier.size(18.dp))
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(
|
||||
text = stringResource(Res.string.open_compass),
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Bolt
|
||||
import androidx.compose.material.icons.filled.Power
|
||||
import androidx.compose.material.icons.rounded.Bolt
|
||||
import androidx.compose.material.icons.rounded.Power
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -47,16 +47,16 @@ internal fun PowerMetrics(node: Node) {
|
|||
buildList {
|
||||
with(node.powerMetrics) {
|
||||
if (ch1Voltage != 0f) {
|
||||
add(VectorMetricInfo(Res.string.channel_1, "%.2fV".format(ch1Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(Res.string.channel_1, "%.1fmA".format(ch1Current), Icons.Default.Power))
|
||||
add(VectorMetricInfo(Res.string.channel_1, "%.2fV".format(ch1Voltage), Icons.Rounded.Bolt))
|
||||
add(VectorMetricInfo(Res.string.channel_1, "%.1fmA".format(ch1Current), Icons.Rounded.Power))
|
||||
}
|
||||
if (ch2Voltage != 0f) {
|
||||
add(VectorMetricInfo(Res.string.channel_2, "%.2fV".format(ch2Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(Res.string.channel_2, "%.1fmA".format(ch2Current), Icons.Default.Power))
|
||||
add(VectorMetricInfo(Res.string.channel_2, "%.2fV".format(ch2Voltage), Icons.Rounded.Bolt))
|
||||
add(VectorMetricInfo(Res.string.channel_2, "%.1fmA".format(ch2Current), Icons.Rounded.Power))
|
||||
}
|
||||
if (ch3Voltage != 0f) {
|
||||
add(VectorMetricInfo(Res.string.channel_3, "%.2fV".format(ch3Voltage), Icons.Default.Bolt))
|
||||
add(VectorMetricInfo(Res.string.channel_3, "%.1fmA".format(ch3Current), Icons.Default.Power))
|
||||
add(VectorMetricInfo(Res.string.channel_3, "%.2fV".format(ch3Voltage), Icons.Rounded.Bolt))
|
||||
add(VectorMetricInfo(Res.string.channel_3, "%.1fmA".format(ch3Current), Icons.Rounded.Power))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,6 @@ import androidx.compose.foundation.layout.Spacer
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Air
|
||||
import androidx.compose.material.icons.filled.Groups
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Speed
|
||||
import androidx.compose.material.icons.filled.StackedLineChart
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
|
|
@ -64,6 +57,14 @@ import org.meshtastic.core.strings.request_local_stats
|
|||
import org.meshtastic.core.strings.request_telemetry
|
||||
import org.meshtastic.core.strings.telemetry
|
||||
import org.meshtastic.core.strings.userinfo
|
||||
import org.meshtastic.core.ui.icon.AirQuality
|
||||
import org.meshtastic.core.ui.icon.Chart
|
||||
import org.meshtastic.core.ui.icon.Groups
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Person
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.core.ui.icon.Speed
|
||||
import org.meshtastic.core.ui.icon.Temperature
|
||||
import org.meshtastic.feature.node.model.LogsType
|
||||
import org.meshtastic.feature.node.model.MetricsState
|
||||
import org.meshtastic.feature.node.model.NodeDetailAction
|
||||
|
|
@ -119,7 +120,7 @@ private fun rememberTelemetricFeatures(
|
|||
listOf(
|
||||
TelemetricFeature(
|
||||
titleRes = Res.string.userinfo,
|
||||
icon = Icons.Default.Person,
|
||||
icon = MeshtasticIcons.Person,
|
||||
requestAction = { NodeMenuAction.RequestUserInfo(it) },
|
||||
),
|
||||
TelemetricFeature(
|
||||
|
|
@ -131,7 +132,7 @@ private fun rememberTelemetricFeatures(
|
|||
),
|
||||
TelemetricFeature(
|
||||
titleRes = Res.string.neighbor_info,
|
||||
icon = Icons.Default.Groups,
|
||||
icon = MeshtasticIcons.Groups,
|
||||
requestAction = { NodeMenuAction.RequestNeighborInfo(it) },
|
||||
isVisible = { it.capabilities.canRequestNeighborInfo },
|
||||
cooldownTimestamp = lastRequestNeighborsTime,
|
||||
|
|
@ -145,7 +146,7 @@ private fun rememberTelemetricFeatures(
|
|||
),
|
||||
TelemetricFeature(
|
||||
titleRes = LogsType.ENVIRONMENT.titleRes,
|
||||
icon = Icons.Default.Air,
|
||||
icon = MeshtasticIcons.Temperature,
|
||||
requestAction = { NodeMenuAction.RequestTelemetry(it, TelemetryType.ENVIRONMENT) },
|
||||
logsType = LogsType.ENVIRONMENT,
|
||||
content = { EnvironmentMetrics(it, metricsState.displayUnits, metricsState.isFahrenheit) },
|
||||
|
|
@ -153,7 +154,7 @@ private fun rememberTelemetricFeatures(
|
|||
),
|
||||
TelemetricFeature(
|
||||
titleRes = Res.string.request_air_quality_metrics,
|
||||
icon = Icons.Default.Air,
|
||||
icon = MeshtasticIcons.AirQuality,
|
||||
requestAction = { NodeMenuAction.RequestTelemetry(it, TelemetryType.AIR_QUALITY) },
|
||||
),
|
||||
TelemetricFeature(
|
||||
|
|
@ -166,7 +167,7 @@ private fun rememberTelemetricFeatures(
|
|||
),
|
||||
TelemetricFeature(
|
||||
titleRes = Res.string.request_local_stats,
|
||||
icon = Icons.Default.Speed,
|
||||
icon = MeshtasticIcons.Speed,
|
||||
requestAction = { NodeMenuAction.RequestTelemetry(it, TelemetryType.LOCAL_STATS) },
|
||||
),
|
||||
TelemetricFeature(
|
||||
|
|
@ -226,7 +227,7 @@ private fun FeatureRow(node: Node, feature: TelemetricFeature, hasLogs: Boolean,
|
|||
},
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.StackedLineChart,
|
||||
MeshtasticIcons.Chart,
|
||||
contentDescription = logsDescription,
|
||||
modifier = Modifier.size(IconButtonDefaults.mediumIconSize),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
|
|
@ -252,7 +253,7 @@ private fun FeatureRow(node: Node, feature: TelemetricFeature, hasLogs: Boolean,
|
|||
cooldownDuration = feature.cooldownDuration,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
imageVector = MeshtasticIcons.Refresh,
|
||||
contentDescription = requestDescription,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.node.component
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Air
|
||||
import androidx.compose.material.icons.rounded.ElectricBolt
|
||||
import androidx.compose.material.icons.rounded.Fingerprint
|
||||
import androidx.compose.material.icons.rounded.Grass
|
||||
import androidx.compose.material.icons.rounded.People
|
||||
import androidx.compose.material.icons.rounded.Router
|
||||
import androidx.compose.material.icons.rounded.Thermostat
|
||||
import androidx.compose.material.icons.rounded.WaterDrop
|
||||
import androidx.compose.material.icons.rounded.Work
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.env_metrics_log
|
||||
import org.meshtastic.core.strings.node_id
|
||||
import org.meshtastic.core.strings.pax_metrics_log
|
||||
import org.meshtastic.core.strings.role
|
||||
|
||||
@Composable
|
||||
fun TemperatureInfo(
|
||||
temp: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Thermostat,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = temp,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HumidityInfo(
|
||||
humidity: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.WaterDrop,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = humidity,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SoilTemperatureInfo(
|
||||
temp: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Grass,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = temp,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SoilMoistureInfo(
|
||||
moisture: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Grass,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = moisture,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PaxcountInfo(
|
||||
pax: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.People,
|
||||
contentDescription = stringResource(Res.string.pax_metrics_log),
|
||||
text = pax,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AirQualityInfo(
|
||||
iaq: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Air,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = iaq,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PowerInfo(value: String, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.ElectricBolt,
|
||||
contentDescription = stringResource(Res.string.env_metrics_log),
|
||||
text = value,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HardwareInfo(
|
||||
hwModel: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Router,
|
||||
contentDescription = "Hardware Model",
|
||||
text = hwModel,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RoleInfo(role: String, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Work,
|
||||
contentDescription = stringResource(Res.string.role),
|
||||
text = role,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NodeIdInfo(id: String, modifier: Modifier = Modifier, contentColor: Color = MaterialTheme.colorScheme.onSurface) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = Icons.Rounded.Fingerprint,
|
||||
contentDescription = stringResource(Res.string.node_id),
|
||||
text = id,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.metrics
|
||||
|
||||
import android.graphics.Paint
|
||||
|
|
@ -32,7 +31,7 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.rounded.Info
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -258,7 +257,7 @@ fun Legend(legendData: List<LegendData>, displayInfoIcon: Boolean = true, prompt
|
|||
if (displayInfoIcon) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
imageVector = Icons.Rounded.Info,
|
||||
modifier = Modifier.clickable { promptInfoDialog() },
|
||||
contentDescription = stringResource(Res.string.info),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -33,9 +33,8 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
|
|
@ -78,6 +77,8 @@ import org.meshtastic.core.ui.component.MainAppBar
|
|||
import org.meshtastic.core.ui.component.MaterialBatteryInfo
|
||||
import org.meshtastic.core.ui.component.OptionLabel
|
||||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.theme.GraphColors.Cyan
|
||||
import org.meshtastic.core.ui.theme.GraphColors.Green
|
||||
|
|
@ -158,10 +159,7 @@ fun DeviceMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat
|
|||
actions = {
|
||||
if (!state.isLocal) {
|
||||
IconButton(onClick = { viewModel.requestTelemetry(TelemetryType.DEVICE) }) {
|
||||
androidx.compose.material3.Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
contentDescription = null,
|
||||
)
|
||||
Icon(imageVector = MeshtasticIcons.Refresh, contentDescription = null)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -75,6 +73,8 @@ import org.meshtastic.core.ui.component.IndoorAirQuality
|
|||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.OptionLabel
|
||||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.feature.node.detail.NodeRequestEffect
|
||||
import org.meshtastic.feature.node.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import org.meshtastic.feature.node.metrics.CommonCharts.MS_PER_SEC
|
||||
|
|
@ -134,7 +134,7 @@ fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNa
|
|||
if (!state.isLocal) {
|
||||
IconButton(onClick = { viewModel.requestTelemetry(TelemetryType.ENVIRONMENT) }) {
|
||||
androidx.compose.material3.Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
imageVector = MeshtasticIcons.Refresh,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -339,27 +339,6 @@ private fun GasCompositionDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics
|
|||
}
|
||||
}
|
||||
}
|
||||
// These are in a differnt proto ...
|
||||
// envMetrics.co2?.let { co2 ->
|
||||
// Spacer(modifier = Modifier.height(4.dp))
|
||||
// Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
// Text(
|
||||
// text = "%s %.0f ppm".format(stringResource(Res.string.co2), co2),
|
||||
// color = MaterialTheme.colorScheme.onSurface,
|
||||
// fontSize = MaterialTheme.typography.labelLarge.fontSize,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// envMetrics.tvoc?.let { tvoc ->
|
||||
// Spacer(modifier = Modifier.height(4.dp))
|
||||
// Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
// Text(
|
||||
// text = "%s %.0f ppb".format(stringResource(Res.string.tvoc), tvoc),
|
||||
// color = MaterialTheme.colorScheme.onSurface,
|
||||
// fontSize = MaterialTheme.typography.labelLarge.fontSize,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.DataArray
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -69,6 +66,9 @@ import org.meshtastic.core.strings.load_indexed
|
|||
import org.meshtastic.core.strings.uptime
|
||||
import org.meshtastic.core.strings.user_string
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.icon.DataArray
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.feature.node.detail.NodeRequestEffect
|
||||
import org.meshtastic.feature.node.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
|
|
@ -105,10 +105,7 @@ fun HostMetricsLogScreen(metricsViewModel: MetricsViewModel = hiltViewModel(), o
|
|||
actions = {
|
||||
if (!state.isLocal) {
|
||||
IconButton(onClick = { metricsViewModel.requestTelemetry(TelemetryType.HOST) }) {
|
||||
Icon(
|
||||
imageVector = androidx.compose.material.icons.Icons.Default.Refresh,
|
||||
contentDescription = null,
|
||||
)
|
||||
Icon(imageVector = MeshtasticIcons.Refresh, contentDescription = null)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -136,7 +133,7 @@ fun HostMetricsItem(modifier: Modifier = Modifier, telemetry: TelemetryProtos.Te
|
|||
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
|
||||
) {
|
||||
Row(modifier = Modifier.padding(16.dp)) {
|
||||
Icon(imageVector = Icons.Default.DataArray, contentDescription = null, modifier = Modifier.width(24.dp))
|
||||
Icon(imageVector = MeshtasticIcons.DataArray, contentDescription = null, modifier = Modifier.width(24.dp))
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
SelectionContainer {
|
||||
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
|
|
|
|||
|
|
@ -33,8 +33,6 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -69,11 +67,16 @@ import org.meshtastic.core.strings.Res
|
|||
import org.meshtastic.core.strings.ble_devices
|
||||
import org.meshtastic.core.strings.no_pax_metrics_logs
|
||||
import org.meshtastic.core.strings.pax
|
||||
import org.meshtastic.core.strings.pax_metrics_log
|
||||
import org.meshtastic.core.strings.uptime
|
||||
import org.meshtastic.core.strings.wifi_devices
|
||||
import org.meshtastic.core.ui.component.IconInfo
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.OptionLabel
|
||||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Paxcount
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.feature.node.detail.NodeRequestEffect
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
import org.meshtastic.proto.PaxcountProtos
|
||||
|
|
@ -229,7 +232,7 @@ fun PaxMetricsScreen(metricsViewModel: MetricsViewModel = hiltViewModel(), onNav
|
|||
actions = {
|
||||
if (!state.isLocal) {
|
||||
IconButton(onClick = { metricsViewModel.requestTelemetry(TelemetryType.PAX) }) {
|
||||
Icon(imageVector = Icons.Default.Refresh, contentDescription = null)
|
||||
Icon(imageVector = MeshtasticIcons.Refresh, contentDescription = null)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -331,6 +334,21 @@ fun unescapeProtoString(escaped: String): ByteArray {
|
|||
return out.toByteArray()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PaxcountInfo(
|
||||
pax: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
IconInfo(
|
||||
modifier = modifier,
|
||||
icon = MeshtasticIcons.Paxcount,
|
||||
contentDescription = stringResource(Res.string.pax_metrics_log),
|
||||
text = pax,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PaxMetricsItem(log: MeshLog, pax: PaxcountProtos.Paxcount, dateFormat: DateFormat) {
|
||||
Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp)) {
|
||||
|
|
|
|||
|
|
@ -34,10 +34,6 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -80,6 +76,10 @@ import org.meshtastic.core.strings.save
|
|||
import org.meshtastic.core.strings.speed
|
||||
import org.meshtastic.core.strings.timestamp
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.icon.Delete
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.core.ui.icon.Save
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.util.formatPositionTime
|
||||
import org.meshtastic.feature.node.detail.NodeRequestEffect
|
||||
|
|
@ -157,13 +157,13 @@ private fun ActionButtons(
|
|||
enabled = clearButtonEnabled,
|
||||
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.error),
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.clear))
|
||||
Icon(imageVector = MeshtasticIcons.Delete, contentDescription = stringResource(Res.string.clear))
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(text = stringResource(Res.string.clear))
|
||||
}
|
||||
|
||||
OutlinedButton(modifier = Modifier.weight(1f), onClick = onSave, enabled = saveButtonEnabled) {
|
||||
Icon(imageVector = Icons.Default.Save, contentDescription = stringResource(Res.string.save))
|
||||
Icon(imageVector = MeshtasticIcons.Save, contentDescription = stringResource(Res.string.save))
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(text = stringResource(Res.string.save))
|
||||
}
|
||||
|
|
@ -207,7 +207,7 @@ fun PositionLogScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigateU
|
|||
actions = {
|
||||
if (!state.isLocal) {
|
||||
IconButton(onClick = { viewModel.requestPosition() }) {
|
||||
Icon(imageVector = Icons.Default.Refresh, contentDescription = null)
|
||||
Icon(imageVector = MeshtasticIcons.Refresh, contentDescription = null)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.rounded.Refresh
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -161,7 +161,7 @@ fun PowerMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigate
|
|||
if (!state.isLocal) {
|
||||
IconButton(onClick = { viewModel.requestTelemetry(TelemetryType.POWER) }) {
|
||||
androidx.compose.material3.Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
imageVector = Icons.Rounded.Refresh,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -74,6 +72,8 @@ import org.meshtastic.core.ui.component.MainAppBar
|
|||
import org.meshtastic.core.ui.component.OptionLabel
|
||||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.core.ui.component.SnrAndRssi
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.feature.node.detail.NodeRequestEffect
|
||||
import org.meshtastic.feature.node.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import org.meshtastic.feature.node.metrics.CommonCharts.MS_PER_SEC
|
||||
|
|
@ -133,7 +133,7 @@ fun SignalMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat
|
|||
if (!state.isLocal) {
|
||||
IconButton(onClick = { viewModel.requestTelemetry(TelemetryType.LOCAL_STATS) }) {
|
||||
androidx.compose.material3.Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
imageVector = MeshtasticIcons.Refresh,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,12 +32,6 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Group
|
||||
import androidx.compose.material.icons.filled.Groups
|
||||
import androidx.compose.material.icons.filled.PersonOff
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
|
|
@ -92,6 +86,12 @@ import org.meshtastic.core.ui.component.MainAppBar
|
|||
import org.meshtastic.core.ui.component.SNR_FAIR_THRESHOLD
|
||||
import org.meshtastic.core.ui.component.SNR_GOOD_THRESHOLD
|
||||
import org.meshtastic.core.ui.component.SimpleAlertDialog
|
||||
import org.meshtastic.core.ui.icon.Delete
|
||||
import org.meshtastic.core.ui.icon.Group
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.PersonOff
|
||||
import org.meshtastic.core.ui.icon.Refresh
|
||||
import org.meshtastic.core.ui.icon.Route
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusOrange
|
||||
|
|
@ -163,7 +163,7 @@ fun TracerouteLogScreen(
|
|||
onClick = { viewModel.requestTraceroute() },
|
||||
cooldownTimestamp = lastTracerouteTime,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Refresh, contentDescription = null)
|
||||
Icon(imageVector = MeshtasticIcons.Refresh, contentDescription = null)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -329,7 +329,7 @@ private fun DeleteItem(onClick: () -> Unit) {
|
|||
text = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
imageVector = MeshtasticIcons.Delete,
|
||||
contentDescription = stringResource(Res.string.delete),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
|
|
@ -357,23 +357,23 @@ private fun TracerouteItem(icon: ImageVector, text: String, modifier: Modifier =
|
|||
@Composable
|
||||
private fun MeshProtos.RouteDiscovery?.getTextAndIcon(): Pair<String, ImageVector> = when {
|
||||
this == null -> {
|
||||
stringResource(Res.string.routing_error_no_response) to Icons.Default.PersonOff
|
||||
stringResource(Res.string.routing_error_no_response) to MeshtasticIcons.PersonOff
|
||||
}
|
||||
// A direct route means the sender and receiver are the only two nodes in the route.
|
||||
routeCount <= 2 && routeBackCount <= 2 -> { // also check routeBackCount for direct to be more robust
|
||||
stringResource(Res.string.traceroute_direct) to Icons.Default.Group
|
||||
stringResource(Res.string.traceroute_direct) to MeshtasticIcons.Group
|
||||
}
|
||||
|
||||
routeCount == routeBackCount -> {
|
||||
val hops = routeCount - 2
|
||||
pluralStringResource(Res.plurals.traceroute_hops, hops, hops) to Icons.Default.Groups
|
||||
pluralStringResource(Res.plurals.traceroute_hops, hops, hops) to MeshtasticIcons.Route
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Asymmetric route
|
||||
val towards = maxOf(0, routeCount - 2)
|
||||
val back = maxOf(0, routeBackCount - 2)
|
||||
stringResource(Res.string.traceroute_diff, towards, back) to Icons.Default.Groups
|
||||
stringResource(Res.string.traceroute_diff, towards, back) to MeshtasticIcons.Route
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -424,5 +424,5 @@ private fun TracerouteItemPreview() {
|
|||
System.currentTimeMillis(),
|
||||
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_ABBREV_ALL,
|
||||
)
|
||||
AppTheme { TracerouteItem(icon = Icons.Default.Group, text = "$time - Direct") }
|
||||
AppTheme { TracerouteItem(icon = MeshtasticIcons.Group, text = "$time - Direct") }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.metrics
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -24,8 +23,6 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Route
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -52,6 +49,8 @@ import org.meshtastic.core.strings.traceroute_outgoing_route
|
|||
import org.meshtastic.core.strings.traceroute_return_route
|
||||
import org.meshtastic.core.strings.traceroute_showing_nodes
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Route
|
||||
import org.meshtastic.core.ui.theme.TracerouteColors
|
||||
import org.meshtastic.feature.map.MapView
|
||||
import org.meshtastic.feature.map.model.TracerouteOverlay
|
||||
|
|
@ -172,7 +171,7 @@ private fun TracerouteNodeCount(modifier: Modifier = Modifier, shown: Int, total
|
|||
private fun LegendRow(color: Color, label: String) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Route,
|
||||
imageVector = MeshtasticIcons.Route,
|
||||
contentDescription = null,
|
||||
tint = color,
|
||||
modifier = Modifier.padding(end = 8.dp).size(18.dp),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,19 +14,16 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.node.model
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ChargingStation
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material.icons.filled.Map
|
||||
import androidx.compose.material.icons.filled.Memory
|
||||
import androidx.compose.material.icons.filled.People
|
||||
import androidx.compose.material.icons.filled.Power
|
||||
import androidx.compose.material.icons.filled.Route
|
||||
import androidx.compose.material.icons.filled.SignalCellularAlt
|
||||
import androidx.compose.material.icons.filled.Thermostat
|
||||
import androidx.compose.material.icons.rounded.ChargingStation
|
||||
import androidx.compose.material.icons.rounded.LocationOn
|
||||
import androidx.compose.material.icons.rounded.Map
|
||||
import androidx.compose.material.icons.rounded.Memory
|
||||
import androidx.compose.material.icons.rounded.Power
|
||||
import androidx.compose.material.icons.rounded.SignalCellularAlt
|
||||
import androidx.compose.material.icons.rounded.Thermostat
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.meshtastic.core.navigation.NodeDetailRoutes
|
||||
|
|
@ -41,15 +38,18 @@ import org.meshtastic.core.strings.position_log
|
|||
import org.meshtastic.core.strings.power_metrics_log
|
||||
import org.meshtastic.core.strings.sig_metrics_log
|
||||
import org.meshtastic.core.strings.traceroute_log
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Paxcount
|
||||
import org.meshtastic.core.ui.icon.Route
|
||||
|
||||
enum class LogsType(val titleRes: StringResource, val icon: ImageVector, val routeFactory: (Int) -> Route) {
|
||||
DEVICE(Res.string.device_metrics_log, Icons.Default.ChargingStation, { NodeDetailRoutes.DeviceMetrics(it) }),
|
||||
NODE_MAP(Res.string.node_map, Icons.Default.Map, { NodeDetailRoutes.NodeMap(it) }),
|
||||
POSITIONS(Res.string.position_log, Icons.Default.LocationOn, { NodeDetailRoutes.PositionLog(it) }),
|
||||
ENVIRONMENT(Res.string.env_metrics_log, Icons.Default.Thermostat, { NodeDetailRoutes.EnvironmentMetrics(it) }),
|
||||
SIGNAL(Res.string.sig_metrics_log, Icons.Default.SignalCellularAlt, { NodeDetailRoutes.SignalMetrics(it) }),
|
||||
POWER(Res.string.power_metrics_log, Icons.Default.Power, { NodeDetailRoutes.PowerMetrics(it) }),
|
||||
TRACEROUTE(Res.string.traceroute_log, Icons.Default.Route, { NodeDetailRoutes.TracerouteLog(it) }),
|
||||
HOST(Res.string.host_metrics_log, Icons.Default.Memory, { NodeDetailRoutes.HostMetricsLog(it) }),
|
||||
PAX(Res.string.pax_metrics_log, Icons.Default.People, { NodeDetailRoutes.PaxMetrics(it) }),
|
||||
DEVICE(Res.string.device_metrics_log, Icons.Rounded.ChargingStation, { NodeDetailRoutes.DeviceMetrics(it) }),
|
||||
NODE_MAP(Res.string.node_map, Icons.Rounded.Map, { NodeDetailRoutes.NodeMap(it) }),
|
||||
POSITIONS(Res.string.position_log, Icons.Rounded.LocationOn, { NodeDetailRoutes.PositionLog(it) }),
|
||||
ENVIRONMENT(Res.string.env_metrics_log, Icons.Rounded.Thermostat, { NodeDetailRoutes.EnvironmentMetrics(it) }),
|
||||
SIGNAL(Res.string.sig_metrics_log, Icons.Rounded.SignalCellularAlt, { NodeDetailRoutes.SignalMetrics(it) }),
|
||||
POWER(Res.string.power_metrics_log, Icons.Rounded.Power, { NodeDetailRoutes.PowerMetrics(it) }),
|
||||
TRACEROUTE(Res.string.traceroute_log, MeshtasticIcons.Route, { NodeDetailRoutes.TracerouteLog(it) }),
|
||||
HOST(Res.string.host_metrics_log, Icons.Rounded.Memory, { NodeDetailRoutes.HostMetricsLog(it) }),
|
||||
PAX(Res.string.pax_metrics_log, MeshtasticIcons.Paxcount, { NodeDetailRoutes.PaxMetrics(it) }),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.BugReport
|
||||
import androidx.compose.material.icons.rounded.AppSettingsAlt
|
||||
import androidx.compose.material.icons.rounded.BugReport
|
||||
import androidx.compose.material.icons.rounded.FilterList
|
||||
import androidx.compose.material.icons.rounded.FormatPaint
|
||||
import androidx.compose.material.icons.rounded.Info
|
||||
|
|
@ -283,7 +284,7 @@ fun SettingsScreen(
|
|||
SwitchListItem(
|
||||
text = stringResource(Res.string.analytics_okay),
|
||||
checked = allowed,
|
||||
leadingIcon = Icons.Default.BugReport,
|
||||
leadingIcon = Icons.Rounded.BugReport,
|
||||
onClick = { viewModel.toggleAnalyticsAllowed() },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.outlined.FileDownload
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material.icons.twotone.FilterAltOff
|
||||
import androidx.compose.material3.Card
|
||||
|
|
@ -416,7 +416,7 @@ fun DebugMenuActions(deleteLogs: () -> Unit, modifier: Modifier = Modifier) {
|
|||
var showDeleteLogsDialog by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(onClick = { showDeleteLogsDialog = true }, modifier = modifier.padding(4.dp)) {
|
||||
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.debug_clear))
|
||||
Icon(imageVector = Icons.Rounded.Delete, contentDescription = stringResource(Res.string.debug_clear))
|
||||
}
|
||||
if (showDeleteLogsDialog) {
|
||||
SimpleAlertDialog(
|
||||
|
|
@ -664,7 +664,7 @@ private fun DebugMenuActionsPreview() {
|
|||
)
|
||||
}
|
||||
IconButton(onClick = { /* Preview only */ }, modifier = Modifier.padding(4.dp)) {
|
||||
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.debug_clear))
|
||||
Icon(imageVector = Icons.Rounded.Delete, contentDescription = stringResource(Res.string.debug_clear))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,9 +29,10 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.Done
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Clear
|
||||
import androidx.compose.material.icons.twotone.FilterAlt
|
||||
import androidx.compose.material.icons.twotone.FilterAltOff
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
|
|
@ -103,7 +104,7 @@ fun DebugCustomFilterInput(
|
|||
},
|
||||
enabled = customFilterText.isNotBlank(),
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(Res.string.debug_filter_add))
|
||||
Icon(imageVector = Icons.Rounded.Add, contentDescription = stringResource(Res.string.debug_filter_add))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -266,7 +267,7 @@ internal fun DebugActiveFilters(
|
|||
}
|
||||
IconButton(onClick = { onFilterTextsChange(emptyList()) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Clear,
|
||||
imageVector = Icons.Rounded.Clear,
|
||||
contentDescription = stringResource(Res.string.debug_filter_clear),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.debugging
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -29,10 +28,10 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.material.icons.outlined.FileDownload
|
||||
import androidx.compose.material.icons.rounded.Clear
|
||||
import androidx.compose.material.icons.rounded.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.rounded.KeyboardArrowUp
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -83,14 +82,14 @@ internal fun DebugSearchNavigation(
|
|||
)
|
||||
IconButton(onClick = onPreviousMatch, enabled = searchState.hasMatches, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowUp,
|
||||
imageVector = Icons.Rounded.KeyboardArrowUp,
|
||||
contentDescription = stringResource(Res.string.debug_search_prev),
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onNextMatch, enabled = searchState.hasMatches, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowDown,
|
||||
imageVector = Icons.Rounded.KeyboardArrowDown,
|
||||
contentDescription = stringResource(Res.string.debug_search_next),
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
|
|
@ -136,7 +135,7 @@ internal fun DebugSearchBar(
|
|||
if (searchState.searchText.isNotEmpty()) {
|
||||
IconButton(onClick = onClearSearch, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Clear,
|
||||
imageVector = Icons.Rounded.Clear,
|
||||
contentDescription = stringResource(Res.string.debug_search_clear),
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -156,7 +156,7 @@ private fun FilterWordsInputCard(newWord: String, onNewWordChange: (String) -> U
|
|||
keyboardActions = KeyboardActions(onDone = { onAddWord() }),
|
||||
)
|
||||
IconButton(onClick = onAddWord) {
|
||||
Icon(Icons.Default.Add, contentDescription = stringResource(Res.string.add))
|
||||
Icon(Icons.Rounded.Add, contentDescription = stringResource(Res.string.add))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,7 +184,7 @@ private fun FilterWordItem(word: String, onRemove: () -> Unit) {
|
|||
)
|
||||
}
|
||||
IconButton(onClick = onRemove) {
|
||||
Icon(Icons.Default.Delete, contentDescription = stringResource(Res.string.delete))
|
||||
Icon(Icons.Rounded.Delete, contentDescription = stringResource(Res.string.delete))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,20 +14,19 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.navigation
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.List
|
||||
import androidx.compose.material.icons.filled.Bluetooth
|
||||
import androidx.compose.material.icons.filled.CellTower
|
||||
import androidx.compose.material.icons.filled.DisplaySettings
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.Power
|
||||
import androidx.compose.material.icons.filled.Router
|
||||
import androidx.compose.material.icons.filled.Security
|
||||
import androidx.compose.material.icons.filled.Wifi
|
||||
import androidx.compose.material.icons.rounded.Bluetooth
|
||||
import androidx.compose.material.icons.rounded.CellTower
|
||||
import androidx.compose.material.icons.rounded.DisplaySettings
|
||||
import androidx.compose.material.icons.rounded.LocationOn
|
||||
import androidx.compose.material.icons.rounded.Person
|
||||
import androidx.compose.material.icons.rounded.Power
|
||||
import androidx.compose.material.icons.rounded.Router
|
||||
import androidx.compose.material.icons.rounded.Security
|
||||
import androidx.compose.material.icons.rounded.Wifi
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.meshtastic.core.navigation.Route
|
||||
|
|
@ -47,54 +46,54 @@ import org.meshtastic.proto.AdminProtos
|
|||
import org.meshtastic.proto.MeshProtos.DeviceMetadata
|
||||
|
||||
enum class ConfigRoute(val title: StringResource, val route: Route, val icon: ImageVector?, val type: Int = 0) {
|
||||
USER(Res.string.user, SettingsRoutes.User, Icons.Default.Person, 0),
|
||||
USER(Res.string.user, SettingsRoutes.User, Icons.Rounded.Person, 0),
|
||||
CHANNELS(Res.string.channels, SettingsRoutes.ChannelConfig, Icons.AutoMirrored.Default.List, 0),
|
||||
DEVICE(
|
||||
Res.string.device,
|
||||
SettingsRoutes.Device,
|
||||
Icons.Default.Router,
|
||||
Icons.Rounded.Router,
|
||||
AdminProtos.AdminMessage.ConfigType.DEVICE_CONFIG_VALUE,
|
||||
),
|
||||
POSITION(
|
||||
Res.string.position,
|
||||
SettingsRoutes.Position,
|
||||
Icons.Default.LocationOn,
|
||||
Icons.Rounded.LocationOn,
|
||||
AdminProtos.AdminMessage.ConfigType.POSITION_CONFIG_VALUE,
|
||||
),
|
||||
POWER(
|
||||
Res.string.power,
|
||||
SettingsRoutes.Power,
|
||||
Icons.Default.Power,
|
||||
Icons.Rounded.Power,
|
||||
AdminProtos.AdminMessage.ConfigType.POWER_CONFIG_VALUE,
|
||||
),
|
||||
NETWORK(
|
||||
Res.string.network,
|
||||
SettingsRoutes.Network,
|
||||
Icons.Default.Wifi,
|
||||
Icons.Rounded.Wifi,
|
||||
AdminProtos.AdminMessage.ConfigType.NETWORK_CONFIG_VALUE,
|
||||
),
|
||||
DISPLAY(
|
||||
Res.string.display,
|
||||
SettingsRoutes.Display,
|
||||
Icons.Default.DisplaySettings,
|
||||
Icons.Rounded.DisplaySettings,
|
||||
AdminProtos.AdminMessage.ConfigType.DISPLAY_CONFIG_VALUE,
|
||||
),
|
||||
LORA(
|
||||
Res.string.lora,
|
||||
SettingsRoutes.LoRa,
|
||||
Icons.Default.CellTower,
|
||||
Icons.Rounded.CellTower,
|
||||
AdminProtos.AdminMessage.ConfigType.LORA_CONFIG_VALUE,
|
||||
),
|
||||
BLUETOOTH(
|
||||
Res.string.bluetooth,
|
||||
SettingsRoutes.Bluetooth,
|
||||
Icons.Default.Bluetooth,
|
||||
Icons.Rounded.Bluetooth,
|
||||
AdminProtos.AdminMessage.ConfigType.BLUETOOTH_CONFIG_VALUE,
|
||||
),
|
||||
SECURITY(
|
||||
Res.string.security,
|
||||
SettingsRoutes.Security,
|
||||
Icons.Default.Security,
|
||||
Icons.Rounded.Security,
|
||||
AdminProtos.AdminMessage.ConfigType.SECURITY_CONFIG_VALUE,
|
||||
),
|
||||
;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,16 @@ import androidx.compose.material.icons.filled.Sensors
|
|||
import androidx.compose.material.icons.filled.SettingsRemote
|
||||
import androidx.compose.material.icons.filled.Speed
|
||||
import androidx.compose.material.icons.filled.Usb
|
||||
import androidx.compose.material.icons.rounded.Cloud
|
||||
import androidx.compose.material.icons.rounded.DataUsage
|
||||
import androidx.compose.material.icons.rounded.LightMode
|
||||
import androidx.compose.material.icons.rounded.Notifications
|
||||
import androidx.compose.material.icons.rounded.People
|
||||
import androidx.compose.material.icons.rounded.PermScanWifi
|
||||
import androidx.compose.material.icons.rounded.Sensors
|
||||
import androidx.compose.material.icons.rounded.SettingsRemote
|
||||
import androidx.compose.material.icons.rounded.Speed
|
||||
import androidx.compose.material.icons.rounded.Usb
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.meshtastic.core.navigation.Route
|
||||
|
|
@ -56,19 +66,19 @@ enum class ModuleRoute(val title: StringResource, val route: Route, val icon: Im
|
|||
MQTT(
|
||||
Res.string.mqtt,
|
||||
SettingsRoutes.MQTT,
|
||||
Icons.Default.Cloud,
|
||||
Icons.Rounded.Cloud,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.MQTT_CONFIG_VALUE,
|
||||
),
|
||||
SERIAL(
|
||||
Res.string.serial,
|
||||
SettingsRoutes.Serial,
|
||||
Icons.Default.Usb,
|
||||
Icons.Rounded.Usb,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.SERIAL_CONFIG_VALUE,
|
||||
),
|
||||
EXT_NOTIFICATION(
|
||||
Res.string.external_notification,
|
||||
SettingsRoutes.ExtNotification,
|
||||
Icons.Default.Notifications,
|
||||
Icons.Rounded.Notifications,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.EXTNOTIF_CONFIG_VALUE,
|
||||
),
|
||||
STORE_FORWARD(
|
||||
|
|
@ -80,13 +90,13 @@ enum class ModuleRoute(val title: StringResource, val route: Route, val icon: Im
|
|||
RANGE_TEST(
|
||||
Res.string.range_test,
|
||||
SettingsRoutes.RangeTest,
|
||||
Icons.Default.Speed,
|
||||
Icons.Rounded.Speed,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.RANGETEST_CONFIG_VALUE,
|
||||
),
|
||||
TELEMETRY(
|
||||
Res.string.telemetry,
|
||||
SettingsRoutes.Telemetry,
|
||||
Icons.Default.DataUsage,
|
||||
Icons.Rounded.DataUsage,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.TELEMETRY_CONFIG_VALUE,
|
||||
),
|
||||
CANNED_MESSAGE(
|
||||
|
|
@ -104,31 +114,31 @@ enum class ModuleRoute(val title: StringResource, val route: Route, val icon: Im
|
|||
REMOTE_HARDWARE(
|
||||
Res.string.remote_hardware,
|
||||
SettingsRoutes.RemoteHardware,
|
||||
Icons.Default.SettingsRemote,
|
||||
Icons.Rounded.SettingsRemote,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.REMOTEHARDWARE_CONFIG_VALUE,
|
||||
),
|
||||
NEIGHBOR_INFO(
|
||||
Res.string.neighbor_info,
|
||||
SettingsRoutes.NeighborInfo,
|
||||
Icons.Default.People,
|
||||
Icons.Rounded.People,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.NEIGHBORINFO_CONFIG_VALUE,
|
||||
),
|
||||
AMBIENT_LIGHTING(
|
||||
Res.string.ambient_lighting,
|
||||
SettingsRoutes.AmbientLighting,
|
||||
Icons.Default.LightMode,
|
||||
Icons.Rounded.LightMode,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.AMBIENTLIGHTING_CONFIG_VALUE,
|
||||
),
|
||||
DETECTION_SENSOR(
|
||||
Res.string.detection_sensor,
|
||||
SettingsRoutes.DetectionSensor,
|
||||
Icons.Default.Sensors,
|
||||
Icons.Rounded.Sensors,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.DETECTIONSENSOR_CONFIG_VALUE,
|
||||
),
|
||||
PAXCOUNTER(
|
||||
Res.string.paxcounter,
|
||||
SettingsRoutes.Paxcounter,
|
||||
Icons.Default.PermScanWifi,
|
||||
Icons.Rounded.PermScanWifi,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.PAXCOUNTER_CONFIG_VALUE,
|
||||
),
|
||||
STATUS_MESSAGE(
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@ import androidx.compose.material.icons.filled.Download
|
|||
import androidx.compose.material.icons.filled.Upload
|
||||
import androidx.compose.material.icons.rounded.BugReport
|
||||
import androidx.compose.material.icons.rounded.CleaningServices
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.PowerSettingsNew
|
||||
import androidx.compose.material.icons.rounded.RestartAlt
|
||||
import androidx.compose.material.icons.rounded.Restore
|
||||
import androidx.compose.material.icons.rounded.Storage
|
||||
import androidx.compose.material.icons.rounded.SystemUpdate
|
||||
import androidx.compose.material.icons.rounded.Upload
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
|
|
@ -143,13 +145,13 @@ fun RadioConfigItemList(
|
|||
|
||||
ListItem(
|
||||
text = stringResource(Res.string.import_configuration),
|
||||
leadingIcon = Icons.Default.Download,
|
||||
leadingIcon = Icons.Rounded.Download,
|
||||
enabled = enabled,
|
||||
onClick = onImport,
|
||||
)
|
||||
ListItem(
|
||||
text = stringResource(Res.string.export_configuration),
|
||||
leadingIcon = Icons.Default.Upload,
|
||||
leadingIcon = Icons.Rounded.Upload,
|
||||
enabled = enabled,
|
||||
onClick = onExport,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import android.media.MediaPlayer
|
||||
|
|
@ -25,8 +24,8 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.FolderOpen
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.rounded.FolderOpen
|
||||
import androidx.compose.material.icons.rounded.PlayArrow
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -289,7 +288,7 @@ fun ExternalNotificationConfigScreen(
|
|||
Row {
|
||||
IconButton(onClick = { launcher.launch("*/*") }, enabled = state.connected) {
|
||||
Icon(
|
||||
Icons.Default.FolderOpen,
|
||||
Icons.Rounded.FolderOpen,
|
||||
contentDescription = stringResource(Res.string.import_label),
|
||||
)
|
||||
}
|
||||
|
|
@ -313,7 +312,7 @@ fun ExternalNotificationConfigScreen(
|
|||
},
|
||||
enabled = state.connected,
|
||||
) {
|
||||
Icon(Icons.Default.PlayArrow, contentDescription = stringResource(Res.string.play))
|
||||
Icon(Icons.Rounded.PlayArrow, contentDescription = stringResource(Res.string.play))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue