diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt
index 400638ff8..1c2be4ef4 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt
@@ -34,16 +34,9 @@ import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.twotone.Chat
-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.Contactless
-import androidx.compose.material.icons.twotone.Map
-import androidx.compose.material.icons.twotone.People
+import androidx.compose.material.icons.rounded.QrCode2
+import androidx.compose.material.icons.rounded.Wifi
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
-import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Scaffold
@@ -86,6 +79,7 @@ import com.geeksville.mesh.R
import com.geeksville.mesh.android.AddNavigationTracking
import com.geeksville.mesh.android.BuildUtils.debug
import com.geeksville.mesh.android.setAttributes
+import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.BluetoothViewModel
import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.model.Node
@@ -106,10 +100,14 @@ import com.geeksville.mesh.ui.common.components.MainMenuAction
import com.geeksville.mesh.ui.common.components.MultipleChoiceAlertDialog
import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
+import com.geeksville.mesh.ui.common.icons.Map
+import com.geeksville.mesh.ui.common.icons.MeshtasticIcons
+import com.geeksville.mesh.ui.common.icons.Messages
+import com.geeksville.mesh.ui.common.icons.Nodes
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusBlue
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
-import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
-import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
+import com.geeksville.mesh.ui.connections.DeviceType
+import com.geeksville.mesh.ui.connections.components.TopLevelNavIcon
import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.sharing.SharedContactDialog
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -119,11 +117,11 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector, val route: Route) {
- Contacts(R.string.contacts, Icons.AutoMirrored.TwoTone.Chat, ContactsRoutes.ContactsGraph),
- Nodes(R.string.nodes, Icons.TwoTone.People, NodesRoutes.NodesGraph),
- Map(R.string.map, Icons.TwoTone.Map, MapRoutes.Map),
- Channels(R.string.channels, Icons.TwoTone.Contactless, ChannelsRoutes.ChannelsGraph),
- Connections(R.string.connections, Icons.TwoTone.CloudOff, ConnectionsRoutes.ConnectionsGraph),
+ Messages(R.string.contacts, MeshtasticIcons.Messages, ContactsRoutes.ContactsGraph),
+ Nodes(R.string.nodes, MeshtasticIcons.Nodes, NodesRoutes.NodesGraph),
+ Map(R.string.map, MeshtasticIcons.Map, MapRoutes.Map),
+ Share(R.string.bottom_nav_share, Icons.Rounded.QrCode2, ChannelsRoutes.ChannelsGraph),
+ Connections(R.string.connections, Icons.Rounded.Wifi, ConnectionsRoutes.ConnectionsGraph),
;
companion object {
@@ -147,6 +145,7 @@ enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector,
fun MainScreen(
uIViewModel: UIViewModel = hiltViewModel(),
bluetoothViewModel: BluetoothViewModel = hiltViewModel(),
+ scanModel: BTScanModel = hiltViewModel(),
onAction: (MainMenuAction) -> Unit,
) {
val navController = rememberNavController()
@@ -226,6 +225,9 @@ fun MainScreen(
val currentDestination = navController.currentBackStackEntryAsState().value?.destination
val topLevelDestination = TopLevelDestination.fromNavDestination(currentDestination)
+ // State for determining the connection type icon to display
+ val selectedDevice by scanModel.selectedNotNullFlow.collectAsStateWithLifecycle()
+
// State for managing the glow animation around the Connections icon
var currentGlowColor by remember { mutableStateOf(Color.Transparent) }
val animatedGlowAlpha = remember { Animatable(0f) }
@@ -272,7 +274,11 @@ fun MainScreen(
PlainTooltip {
Text(
if (isConnectionsRoute) {
- connectionState.getTooltipString()
+ when (connectionState) {
+ ConnectionState.CONNECTED -> stringResource(R.string.connected)
+ ConnectionState.DEVICE_SLEEP -> stringResource(R.string.device_sleeping)
+ ConnectionState.DISCONNECTED -> stringResource(R.string.disconnected)
+ }
} else {
stringResource(id = destination.label)
},
@@ -313,7 +319,9 @@ fun MainScreen(
} else {
Modifier
}
- Box(modifier = iconModifier) { TopLevelNavIcon(destination, connectionState) }
+ Box(modifier = iconModifier) {
+ TopLevelNavIcon(destination, connectionState, DeviceType.fromAddress(selectedDevice))
+ }
}
},
selected = isSelected,
@@ -380,25 +388,6 @@ fun MainScreen(
}
}
-@Composable
-private fun TopLevelNavIcon(destination: TopLevelDestination, connectionState: ConnectionState) {
- val iconTint =
- when {
- destination == TopLevelDestination.Connections -> connectionState.getConnectionColor()
- else -> LocalContentColor.current
- }
- Icon(
- imageVector =
- if (destination == TopLevelDestination.Connections) {
- connectionState.getConnectionIcon()
- } else {
- destination.icon
- },
- contentDescription = stringResource(id = destination.label),
- tint = iconTint,
- )
-}
-
@Composable
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun VersionChecks(viewModel: UIViewModel) {
@@ -483,23 +472,3 @@ private fun VersionChecks(viewModel: UIViewModel) {
}
}
}
-
-@Composable
-private fun ConnectionState.getConnectionColor(): Color = when (this) {
- ConnectionState.CONNECTED -> colorScheme.StatusGreen
- ConnectionState.DEVICE_SLEEP -> colorScheme.StatusYellow
- ConnectionState.DISCONNECTED -> colorScheme.StatusRed
-}
-
-private fun ConnectionState.getConnectionIcon(): ImageVector = when (this) {
- ConnectionState.CONNECTED -> Icons.TwoTone.CloudDone
- ConnectionState.DEVICE_SLEEP -> Icons.TwoTone.CloudUpload
- ConnectionState.DISCONNECTED -> Icons.TwoTone.CloudOff
-}
-
-@Composable
-private fun ConnectionState.getTooltipString(): String = when (this) {
- ConnectionState.CONNECTED -> stringResource(R.string.connected)
- ConnectionState.DEVICE_SLEEP -> stringResource(R.string.device_sleeping)
- ConnectionState.DISCONNECTED -> stringResource(R.string.disconnected)
-}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/components/MaterialBatteryInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/common/components/MaterialBatteryInfo.kt
index 2ff3b1fc1..39908318f 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/common/components/MaterialBatteryInfo.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/components/MaterialBatteryInfo.kt
@@ -42,6 +42,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.ui.common.icons.BatteryEmpty
import com.geeksville.mesh.ui.common.icons.BatteryUnknown
+import com.geeksville.mesh.ui.common.icons.MeshtasticIcons
import com.geeksville.mesh.ui.common.theme.AppTheme
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusOrange
@@ -72,7 +73,7 @@ fun MaterialBatteryInfo(modifier: Modifier = Modifier, level: Int) {
} else if (level < 0) {
Icon(
modifier = Modifier.size(SIZE_ICON.dp),
- imageVector = BatteryUnknown,
+ imageVector = MeshtasticIcons.BatteryUnknown,
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = null,
)
@@ -104,7 +105,7 @@ fun MaterialBatteryInfo(modifier: Modifier = Modifier, level: Int) {
size = Size(fillWidth, availableHeight),
)
},
- imageVector = BatteryEmpty,
+ imageVector = MeshtasticIcons.BatteryEmpty,
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = null,
)
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/icons/Battery.kt b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Battery.kt
index 382844980..eb1e99618 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/common/icons/Battery.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Battery.kt
@@ -27,9 +27,9 @@ import androidx.compose.ui.unit.dp
* This is from Material Symbols.
*
* @see
- * [battery_android_0](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:battery_android_0:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=battery&icon.set=Material+Symbols&icon.size=24&icon.color=%23000000&icon.platform=android)
+ * [battery_android_0](https://fonts.google.com/icons?icon.query=battery+android+0&icon.size=24&icon.color=%23e3e3e3&icon.style=Rounded)
*/
-val BatteryEmpty: ImageVector
+val MeshtasticIcons.BatteryEmpty: ImageVector
get() {
if (batteryEmpty != null) {
return batteryEmpty!!
@@ -99,9 +99,9 @@ private var batteryEmpty: ImageVector? = null
* This is from Material Symbols.
*
* @see
- * [battery_android_question](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:battery_android_question:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=battery&icon.set=Material+Symbols&icon.size=24&icon.color=%23000000&icon.platform=android)
+ * [battery_android_question](https://fonts.google.com/icons?icon.query=battery+android+question&icon.size=24&icon.color=%23e3e3e3&icon.style=Rounded)
*/
-val BatteryUnknown: ImageVector
+val MeshtasticIcons.BatteryUnknown: ImageVector
get() {
if (batteryUnknown != null) {
return batteryUnknown!!
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/icons/Device.kt b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Device.kt
new file mode 100644
index 000000000..5ae2b21e1
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Device.kt
@@ -0,0 +1,151 @@
+/*
+ * 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 .
+ */
+
+package com.geeksville.mesh.ui.common.icons
+
+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
+
+/**
+ * This is from Material Symbols.
+ *
+ * @see
+ * [router](https://fonts.google.com/icons?selected=Material+Symbols+Rounded:router:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=router&icon.size=24&icon.color=%23e3e3e3&icon.platform=android&icon.style=Rounded)
+ */
+val MeshtasticIcons.Device: ImageVector
+ get() {
+ if (device != null) {
+ return device!!
+ }
+ device =
+ ImageVector.Builder(
+ name = "Outlined.Device",
+ defaultWidth = 24.dp,
+ defaultHeight = 24.dp,
+ viewportWidth = 960f,
+ viewportHeight = 960f,
+ )
+ .apply {
+ path(fill = SolidColor(Color(0xFFE3E3E3))) {
+ moveTo(200f, 840f)
+ quadToRelative(-33f, 0f, -56.5f, -23.5f)
+ reflectiveQuadTo(120f, 760f)
+ verticalLineToRelative(-160f)
+ quadToRelative(0f, -33f, 23.5f, -56.5f)
+ reflectiveQuadTo(200f, 520f)
+ horizontalLineToRelative(400f)
+ verticalLineToRelative(-120f)
+ quadToRelative(0f, -17f, 11.5f, -28.5f)
+ reflectiveQuadTo(640f, 360f)
+ quadToRelative(17f, 0f, 28.5f, 11.5f)
+ reflectiveQuadTo(680f, 400f)
+ verticalLineToRelative(120f)
+ horizontalLineToRelative(80f)
+ quadToRelative(33f, 0f, 56.5f, 23.5f)
+ reflectiveQuadTo(840f, 600f)
+ verticalLineToRelative(160f)
+ quadToRelative(0f, 33f, -23.5f, 56.5f)
+ reflectiveQuadTo(760f, 840f)
+ lineTo(200f, 840f)
+ close()
+ moveTo(200f, 760f)
+ horizontalLineToRelative(560f)
+ verticalLineToRelative(-160f)
+ lineTo(200f, 600f)
+ verticalLineToRelative(160f)
+ close()
+ moveTo(280f, 720f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(320f, 680f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(280f, 640f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(240f, 680f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(280f, 720f)
+ close()
+ moveTo(420f, 720f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(460f, 680f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(420f, 640f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(380f, 680f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(420f, 720f)
+ close()
+ moveTo(560f, 720f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(600f, 680f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(560f, 640f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(520f, 680f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(560f, 720f)
+ close()
+ moveTo(640f, 300f)
+ quadToRelative(-11f, 0f, -20f, 2f)
+ reflectiveQuadToRelative(-18f, 6f)
+ quadToRelative(-16f, 7f, -32.5f, 6f)
+ reflectiveQuadTo(541f, 301f)
+ quadToRelative(-12f, -12f, -11.5f, -29f)
+ reflectiveQuadToRelative(14.5f, -25f)
+ quadToRelative(21f, -13f, 45.5f, -20f)
+ reflectiveQuadToRelative(50.5f, -7f)
+ quadToRelative(27f, 0f, 51f, 7f)
+ reflectiveQuadToRelative(45f, 20f)
+ quadToRelative(14f, 8f, 14.5f, 25f)
+ reflectiveQuadTo(739f, 301f)
+ quadToRelative(-12f, 12f, -29f, 13f)
+ reflectiveQuadToRelative(-33f, -6f)
+ quadToRelative(-8f, -4f, -17.5f, -6f)
+ reflectiveQuadToRelative(-19.5f, -2f)
+ close()
+ moveTo(640f, 160f)
+ quadToRelative(-39f, 0f, -74.5f, 11.5f)
+ reflectiveQuadTo(500f, 205f)
+ quadToRelative(-14f, 10f, -30.5f, 9f)
+ reflectiveQuadTo(442f, 202f)
+ quadToRelative(-12f, -12f, -12f, -28f)
+ reflectiveQuadToRelative(13f, -26f)
+ quadToRelative(41f, -32f, 91f, -50f)
+ reflectiveQuadToRelative(106f, -18f)
+ quadToRelative(56f, 0f, 106f, 18f)
+ reflectiveQuadToRelative(91f, 50f)
+ quadToRelative(13f, 10f, 13f, 26f)
+ reflectiveQuadToRelative(-12f, 28f)
+ quadToRelative(-11f, 11f, -27.5f, 12f)
+ reflectiveQuadToRelative(-30.5f, -9f)
+ quadToRelative(-30f, -22f, -65.5f, -33.5f)
+ reflectiveQuadTo(640f, 160f)
+ close()
+ moveTo(200f, 760f)
+ verticalLineToRelative(-160f)
+ verticalLineToRelative(160f)
+ close()
+ }
+ }
+ .build()
+
+ return device!!
+ }
+
+private var device: ImageVector? = null
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/icons/Map.kt b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Map.kt
new file mode 100644
index 000000000..62c3d23eb
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Map.kt
@@ -0,0 +1,110 @@
+/*
+ * 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 .
+ */
+
+package com.geeksville.mesh.ui.common.icons
+
+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
+
+/**
+ * This is from Material Symbols.
+ *
+ * @see
+ * [map](https://fonts.google.com/icons?selected=Material+Symbols+Rounded:map:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=map&icon.size=24&icon.color=%23e3e3e3&icon.platform=android&icon.style=Rounded)
+ */
+val MeshtasticIcons.Map: ImageVector
+ get() {
+ if (map != null) {
+ return map!!
+ }
+ map =
+ ImageVector.Builder(
+ name = "Outlined.Map",
+ defaultWidth = 24.dp,
+ defaultHeight = 24.dp,
+ viewportWidth = 960f,
+ viewportHeight = 960f,
+ )
+ .apply {
+ path(fill = SolidColor(Color.Black)) {
+ moveToRelative(574f, 831f)
+ lineToRelative(-214f, -75f)
+ lineToRelative(-186f, 72f)
+ quadToRelative(-10f, 4f, -19.5f, 2.5f)
+ reflectiveQuadTo(137f, 824f)
+ quadToRelative(-8f, -5f, -12.5f, -13.5f)
+ reflectiveQuadTo(120f, 791f)
+ verticalLineToRelative(-561f)
+ quadToRelative(0f, -13f, 7.5f, -23f)
+ reflectiveQuadToRelative(20.5f, -15f)
+ lineToRelative(186f, -63f)
+ quadToRelative(6f, -2f, 12.5f, -3f)
+ reflectiveQuadToRelative(13.5f, -1f)
+ quadToRelative(7f, 0f, 13.5f, 1f)
+ reflectiveQuadToRelative(12.5f, 3f)
+ lineToRelative(214f, 75f)
+ lineToRelative(186f, -72f)
+ quadToRelative(10f, -4f, 19.5f, -2.5f)
+ reflectiveQuadTo(823f, 136f)
+ quadToRelative(8f, 5f, 12.5f, 13.5f)
+ reflectiveQuadTo(840f, 169f)
+ verticalLineToRelative(561f)
+ quadToRelative(0f, 13f, -7.5f, 23f)
+ reflectiveQuadTo(812f, 768f)
+ lineToRelative(-186f, 63f)
+ quadToRelative(-6f, 2f, -12.5f, 3f)
+ reflectiveQuadToRelative(-13.5f, 1f)
+ quadToRelative(-7f, 0f, -13.5f, -1f)
+ reflectiveQuadToRelative(-12.5f, -3f)
+ close()
+ moveTo(560f, 742f)
+ verticalLineToRelative(-468f)
+ lineToRelative(-160f, -56f)
+ verticalLineToRelative(468f)
+ lineToRelative(160f, 56f)
+ close()
+ moveTo(640f, 742f)
+ lineTo(760f, 702f)
+ verticalLineToRelative(-474f)
+ lineToRelative(-120f, 46f)
+ verticalLineToRelative(468f)
+ close()
+ moveTo(200f, 732f)
+ lineTo(320f, 686f)
+ verticalLineToRelative(-468f)
+ lineToRelative(-120f, 40f)
+ verticalLineToRelative(474f)
+ close()
+ moveTo(640f, 274f)
+ verticalLineToRelative(468f)
+ verticalLineToRelative(-468f)
+ close()
+ moveTo(320f, 218f)
+ verticalLineToRelative(468f)
+ verticalLineToRelative(-468f)
+ close()
+ }
+ }
+ .build()
+
+ return map!!
+ }
+
+private var map: ImageVector? = null
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/icons/MeshtasticIcons.kt b/app/src/main/java/com/geeksville/mesh/ui/common/icons/MeshtasticIcons.kt
new file mode 100644
index 000000000..4afd06b93
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/icons/MeshtasticIcons.kt
@@ -0,0 +1,20 @@
+/*
+ * 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 .
+ */
+
+package com.geeksville.mesh.ui.common.icons
+
+object MeshtasticIcons
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/icons/Messages.kt b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Messages.kt
new file mode 100644
index 000000000..3d4dfd9d3
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Messages.kt
@@ -0,0 +1,101 @@
+/*
+ * 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 .
+ */
+
+package com.geeksville.mesh.ui.common.icons
+
+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
+
+/**
+ * This is from Material Symbols.
+ *
+ * @see
+ * [forum](https://fonts.google.com/icons?selected=Material+Symbols+Rounded:forum:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=forum&icon.size=24&icon.color=%23e3e3e3&icon.platform=android&icon.style=Rounded)
+ */
+val MeshtasticIcons.Messages: ImageVector
+ get() {
+ if (messages != null) {
+ return messages!!
+ }
+ messages =
+ ImageVector.Builder(
+ name = "Outlined.Messages",
+ defaultWidth = 24.dp,
+ defaultHeight = 24.dp,
+ viewportWidth = 960f,
+ viewportHeight = 960f,
+ )
+ .apply {
+ path(fill = SolidColor(Color.Black)) {
+ moveTo(840f, 824f)
+ quadToRelative(-8f, 0f, -15f, -3f)
+ reflectiveQuadToRelative(-13f, -9f)
+ lineToRelative(-92f, -92f)
+ lineTo(320f, 720f)
+ quadToRelative(-33f, 0f, -56.5f, -23.5f)
+ reflectiveQuadTo(240f, 640f)
+ verticalLineToRelative(-40f)
+ horizontalLineToRelative(440f)
+ quadToRelative(33f, 0f, 56.5f, -23.5f)
+ reflectiveQuadTo(760f, 520f)
+ verticalLineToRelative(-280f)
+ horizontalLineToRelative(40f)
+ quadToRelative(33f, 0f, 56.5f, 23.5f)
+ reflectiveQuadTo(880f, 320f)
+ verticalLineToRelative(463f)
+ quadToRelative(0f, 18f, -12f, 29.5f)
+ reflectiveQuadTo(840f, 824f)
+ close()
+ moveTo(160f, 487f)
+ lineToRelative(47f, -47f)
+ horizontalLineToRelative(393f)
+ verticalLineToRelative(-280f)
+ lineTo(160f, 160f)
+ verticalLineToRelative(327f)
+ close()
+ moveTo(120f, 624f)
+ quadToRelative(-16f, 0f, -28f, -11.5f)
+ reflectiveQuadTo(80f, 583f)
+ verticalLineToRelative(-423f)
+ quadToRelative(0f, -33f, 23.5f, -56.5f)
+ reflectiveQuadTo(160f, 80f)
+ horizontalLineToRelative(440f)
+ quadToRelative(33f, 0f, 56.5f, 23.5f)
+ reflectiveQuadTo(680f, 160f)
+ verticalLineToRelative(280f)
+ quadToRelative(0f, 33f, -23.5f, 56.5f)
+ reflectiveQuadTo(600f, 520f)
+ lineTo(240f, 520f)
+ lineToRelative(-92f, 92f)
+ quadToRelative(-6f, 6f, -13f, 9f)
+ reflectiveQuadToRelative(-15f, 3f)
+ close()
+ moveTo(160f, 440f)
+ verticalLineToRelative(-280f)
+ verticalLineToRelative(280f)
+ close()
+ }
+ }
+ .build()
+
+ return messages!!
+ }
+
+private var messages: ImageVector? = null
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/icons/NoDevice.kt b/app/src/main/java/com/geeksville/mesh/ui/common/icons/NoDevice.kt
new file mode 100644
index 000000000..d06e27724
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/icons/NoDevice.kt
@@ -0,0 +1,165 @@
+/*
+ * 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 .
+ */
+
+package com.geeksville.mesh.ui.common.icons
+
+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
+
+/**
+ * This is from Material Symbols.
+ *
+ * @see
+ * [router_off](https://fonts.google.com/icons?icon.query=router+off&icon.size=24&icon.color=%23e3e3e3&icon.style=Rounded)
+ */
+val MeshtasticIcons.NoDevice: ImageVector
+ get() {
+ if (noDevice != null) {
+ return noDevice!!
+ }
+ noDevice =
+ ImageVector.Builder(
+ name = "Outlined.NoDevice",
+ defaultWidth = 24.dp,
+ defaultHeight = 24.dp,
+ viewportWidth = 960f,
+ viewportHeight = 960f,
+ )
+ .apply {
+ path(fill = SolidColor(Color.Black)) {
+ moveTo(806f, 692f)
+ lineTo(600f, 486f)
+ verticalLineToRelative(-86f)
+ quadToRelative(0f, -17f, 11.5f, -28.5f)
+ reflectiveQuadTo(640f, 360f)
+ quadToRelative(17f, 0f, 28.5f, 11.5f)
+ reflectiveQuadTo(680f, 400f)
+ verticalLineToRelative(120f)
+ horizontalLineToRelative(80f)
+ quadToRelative(33f, 0f, 56.5f, 23.5f)
+ reflectiveQuadTo(840f, 600f)
+ verticalLineToRelative(78f)
+ quadToRelative(0f, 14f, -12f, 19f)
+ reflectiveQuadToRelative(-22f, -5f)
+ close()
+ moveTo(200f, 760f)
+ horizontalLineToRelative(446f)
+ lineTo(486f, 600f)
+ lineTo(200f, 600f)
+ verticalLineToRelative(160f)
+ close()
+ moveTo(200f, 840f)
+ quadToRelative(-33f, 0f, -56.5f, -23.5f)
+ reflectiveQuadTo(120f, 760f)
+ verticalLineToRelative(-160f)
+ quadToRelative(0f, -33f, 23.5f, -56.5f)
+ reflectiveQuadTo(200f, 520f)
+ horizontalLineToRelative(206f)
+ lineTo(83f, 197f)
+ quadToRelative(-12f, -12f, -12f, -28.5f)
+ reflectiveQuadTo(83f, 140f)
+ quadToRelative(12f, -12f, 28.5f, -12f)
+ reflectiveQuadToRelative(28.5f, 12f)
+ lineToRelative(680f, 680f)
+ quadToRelative(12f, 12f, 12f, 28f)
+ reflectiveQuadToRelative(-12f, 28f)
+ quadToRelative(-12f, 12f, -28.5f, 12f)
+ reflectiveQuadTo(763f, 876f)
+ lineToRelative(-37f, -36f)
+ lineTo(200f, 840f)
+ close()
+ moveTo(280f, 720f)
+ quadToRelative(-17f, 0f, -28.5f, -11.5f)
+ reflectiveQuadTo(240f, 680f)
+ quadToRelative(0f, -17f, 11.5f, -28.5f)
+ reflectiveQuadTo(280f, 640f)
+ quadToRelative(17f, 0f, 28.5f, 11.5f)
+ reflectiveQuadTo(320f, 680f)
+ quadToRelative(0f, 17f, -11.5f, 28.5f)
+ reflectiveQuadTo(280f, 720f)
+ close()
+ moveTo(420f, 720f)
+ quadToRelative(-17f, 0f, -28.5f, -11.5f)
+ reflectiveQuadTo(380f, 680f)
+ quadToRelative(0f, -17f, 11.5f, -28.5f)
+ reflectiveQuadTo(420f, 640f)
+ quadToRelative(17f, 0f, 28.5f, 11.5f)
+ reflectiveQuadTo(460f, 680f)
+ quadToRelative(0f, 17f, -11.5f, 28.5f)
+ reflectiveQuadTo(420f, 720f)
+ close()
+ moveTo(560f, 720f)
+ quadToRelative(-17f, 0f, -28.5f, -11.5f)
+ reflectiveQuadTo(520f, 680f)
+ quadToRelative(0f, -17f, 11.5f, -28.5f)
+ reflectiveQuadTo(560f, 640f)
+ quadToRelative(17f, 0f, 28.5f, 11.5f)
+ reflectiveQuadTo(600f, 680f)
+ quadToRelative(0f, 17f, -11.5f, 28.5f)
+ reflectiveQuadTo(560f, 720f)
+ close()
+ moveTo(200f, 760f)
+ verticalLineToRelative(-160f)
+ verticalLineToRelative(160f)
+ close()
+ moveTo(640f, 300f)
+ quadToRelative(-11f, 0f, -20f, 2f)
+ reflectiveQuadToRelative(-18f, 6f)
+ quadToRelative(-16f, 7f, -32.5f, 6f)
+ reflectiveQuadTo(541f, 301f)
+ quadToRelative(-12f, -12f, -11.5f, -29f)
+ reflectiveQuadToRelative(14.5f, -25f)
+ quadToRelative(21f, -13f, 45.5f, -20f)
+ reflectiveQuadToRelative(50.5f, -7f)
+ quadToRelative(27f, 0f, 51f, 7f)
+ reflectiveQuadToRelative(45f, 20f)
+ quadToRelative(14f, 8f, 14.5f, 25f)
+ reflectiveQuadTo(739f, 301f)
+ quadToRelative(-12f, 12f, -29f, 13f)
+ reflectiveQuadToRelative(-33f, -6f)
+ quadToRelative(-8f, -4f, -17.5f, -6f)
+ reflectiveQuadToRelative(-19.5f, -2f)
+ close()
+ moveTo(640f, 160f)
+ quadToRelative(-39f, 0f, -74.5f, 11.5f)
+ reflectiveQuadTo(500f, 205f)
+ quadToRelative(-14f, 10f, -30.5f, 9f)
+ reflectiveQuadTo(442f, 202f)
+ quadToRelative(-12f, -12f, -12f, -28f)
+ reflectiveQuadToRelative(13f, -26f)
+ quadToRelative(41f, -32f, 91f, -50f)
+ reflectiveQuadToRelative(106f, -18f)
+ quadToRelative(56f, 0f, 106f, 18f)
+ reflectiveQuadToRelative(91f, 50f)
+ quadToRelative(13f, 10f, 13f, 26f)
+ reflectiveQuadToRelative(-12f, 28f)
+ quadToRelative(-11f, 11f, -27.5f, 12f)
+ reflectiveQuadToRelative(-30.5f, -9f)
+ quadToRelative(-30f, -22f, -65.5f, -33.5f)
+ reflectiveQuadTo(640f, 160f)
+ close()
+ }
+ }
+ .build()
+
+ return noDevice!!
+ }
+
+private var noDevice: ImageVector? = null
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/icons/Nodes.kt b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Nodes.kt
new file mode 100644
index 000000000..a388cb79c
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/icons/Nodes.kt
@@ -0,0 +1,166 @@
+/*
+ * 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 .
+ */
+
+package com.geeksville.mesh.ui.common.icons
+
+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
+
+/**
+ * This is from Material Symbols.
+ *
+ * @see
+ * [graph_3](https://fonts.google.com/icons?icon.query=graph+3&icon.size=24&icon.color=%23e3e3e3&icon.style=Rounded)
+ */
+val MeshtasticIcons.Nodes: ImageVector
+ get() {
+ if (nodes != null) {
+ return nodes!!
+ }
+ nodes =
+ ImageVector.Builder(
+ name = "Outlined.Nodes",
+ defaultWidth = 24.dp,
+ defaultHeight = 24.dp,
+ viewportWidth = 960f,
+ viewportHeight = 960f,
+ )
+ .apply {
+ path(fill = SolidColor(Color.Black)) {
+ moveTo(480f, 880f)
+ quadToRelative(-50f, 0f, -85f, -35f)
+ reflectiveQuadToRelative(-35f, -85f)
+ quadToRelative(0f, -5f, 0.5f, -11f)
+ reflectiveQuadToRelative(1.5f, -11f)
+ lineToRelative(-83f, -47f)
+ quadToRelative(-16f, 14f, -36f, 21.5f)
+ reflectiveQuadToRelative(-43f, 7.5f)
+ quadToRelative(-50f, 0f, -85f, -35f)
+ reflectiveQuadToRelative(-35f, -85f)
+ quadToRelative(0f, -50f, 35f, -85f)
+ reflectiveQuadToRelative(85f, -35f)
+ quadToRelative(24f, 0f, 45f, 9f)
+ reflectiveQuadToRelative(38f, 25f)
+ lineToRelative(119f, -60f)
+ quadToRelative(-3f, -23f, 2.5f, -45f)
+ reflectiveQuadToRelative(19.5f, -41f)
+ lineToRelative(-34f, -52f)
+ quadToRelative(-7f, 2f, -14.5f, 3f)
+ reflectiveQuadToRelative(-15.5f, 1f)
+ quadToRelative(-50f, 0f, -85f, -35f)
+ reflectiveQuadToRelative(-35f, -85f)
+ quadToRelative(0f, -50f, 35f, -85f)
+ reflectiveQuadToRelative(85f, -35f)
+ quadToRelative(50f, 0f, 85f, 35f)
+ reflectiveQuadToRelative(35f, 85f)
+ quadToRelative(0f, 20f, -6.5f, 38.5f)
+ reflectiveQuadTo(456f, 272f)
+ lineToRelative(35f, 52f)
+ quadToRelative(8f, -2f, 15f, -3f)
+ reflectiveQuadToRelative(15f, -1f)
+ quadToRelative(17f, 0f, 32f, 4f)
+ reflectiveQuadToRelative(29f, 12f)
+ lineToRelative(66f, -54f)
+ quadToRelative(-4f, -10f, -6f, -20.5f)
+ reflectiveQuadToRelative(-2f, -21.5f)
+ quadToRelative(0f, -50f, 35f, -85f)
+ reflectiveQuadToRelative(85f, -35f)
+ quadToRelative(50f, 0f, 85f, 35f)
+ reflectiveQuadToRelative(35f, 85f)
+ quadToRelative(0f, 50f, -35f, 85f)
+ reflectiveQuadToRelative(-85f, 35f)
+ quadToRelative(-17f, 0f, -32f, -4.5f)
+ reflectiveQuadTo(699f, 343f)
+ lineToRelative(-66f, 55f)
+ quadToRelative(4f, 10f, 6f, 20.5f)
+ reflectiveQuadToRelative(2f, 21.5f)
+ quadToRelative(0f, 50f, -35f, 85f)
+ reflectiveQuadToRelative(-85f, 35f)
+ quadToRelative(-24f, 0f, -45.5f, -9f)
+ reflectiveQuadTo(437f, 526f)
+ lineToRelative(-118f, 59f)
+ quadToRelative(2f, 9f, 1.5f, 18f)
+ reflectiveQuadToRelative(-2.5f, 18f)
+ lineToRelative(84f, 48f)
+ quadToRelative(16f, -14f, 35.5f, -21.5f)
+ reflectiveQuadTo(480f, 640f)
+ quadToRelative(50f, 0f, 85f, 35f)
+ reflectiveQuadToRelative(35f, 85f)
+ quadToRelative(0f, 50f, -35f, 85f)
+ reflectiveQuadToRelative(-85f, 35f)
+ close()
+ moveTo(200f, 640f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(240f, 600f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(200f, 560f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(160f, 600f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(200f, 640f)
+ close()
+ moveTo(360f, 240f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(400f, 200f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(360f, 160f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(320f, 200f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(360f, 240f)
+ close()
+ moveTo(480f, 800f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(520f, 760f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(480f, 720f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(440f, 760f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(480f, 800f)
+ close()
+ moveTo(520f, 480f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(560f, 440f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(520f, 400f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(480f, 440f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(520f, 480f)
+ close()
+ moveTo(760f, 280f)
+ quadToRelative(17f, 0f, 28.5f, -11.5f)
+ reflectiveQuadTo(800f, 240f)
+ quadToRelative(0f, -17f, -11.5f, -28.5f)
+ reflectiveQuadTo(760f, 200f)
+ quadToRelative(-17f, 0f, -28.5f, 11.5f)
+ reflectiveQuadTo(720f, 240f)
+ quadToRelative(0f, 17f, 11.5f, 28.5f)
+ reflectiveQuadTo(760f, 280f)
+ close()
+ }
+ }
+ .build()
+
+ return nodes!!
+ }
+
+private var nodes: ImageVector? = null
diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/Connections.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/Connections.kt
index 3ae785020..18f224b19 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/connections/Connections.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/connections/Connections.kt
@@ -602,7 +602,7 @@ private tailrec fun Context.findActivity(): Activity = when (this) {
else -> error("No activity found")
}
-private enum class DeviceType {
+enum class DeviceType {
BLE,
TCP,
USB,
diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/TopLevelNavIcon.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/TopLevelNavIcon.kt
new file mode 100644
index 000000000..8f6be8e90
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/TopLevelNavIcon.kt
@@ -0,0 +1,147 @@
+/*
+ * 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 .
+ */
+
+package com.geeksville.mesh.ui.connections.components
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Bluetooth
+import androidx.compose.material.icons.rounded.Snooze
+import androidx.compose.material.icons.rounded.Usb
+import androidx.compose.material.icons.rounded.Wifi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewLightDark
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import com.geeksville.mesh.service.ConnectionState
+import com.geeksville.mesh.ui.TopLevelDestination
+import com.geeksville.mesh.ui.common.icons.Device
+import com.geeksville.mesh.ui.common.icons.MeshtasticIcons
+import com.geeksville.mesh.ui.common.icons.NoDevice
+import com.geeksville.mesh.ui.common.theme.AppTheme
+import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
+import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
+import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
+import com.geeksville.mesh.ui.connections.DeviceType
+
+@Composable
+fun TopLevelNavIcon(destination: TopLevelDestination, connectionState: ConnectionState, deviceType: DeviceType?) {
+ if (destination == TopLevelDestination.Connections) {
+ ConnectionsNavIcon(connectionState = connectionState, deviceType = deviceType)
+ } else {
+ Icon(
+ imageVector = destination.icon,
+ contentDescription = stringResource(id = destination.label),
+ tint = LocalContentColor.current,
+ )
+ }
+}
+
+@Composable
+private fun ConnectionsNavIcon(
+ modifier: Modifier = Modifier,
+ connectionState: ConnectionState,
+ deviceType: DeviceType?,
+) {
+ val tint =
+ when (connectionState) {
+ ConnectionState.DISCONNECTED -> colorScheme.StatusRed
+ ConnectionState.DEVICE_SLEEP -> colorScheme.StatusYellow
+ else -> colorScheme.StatusGreen
+ }
+
+ val (backgroundIcon, connectionTypeIcon) =
+ when (connectionState) {
+ ConnectionState.DISCONNECTED -> MeshtasticIcons.NoDevice to null
+ ConnectionState.DEVICE_SLEEP -> MeshtasticIcons.Device to Icons.Rounded.Snooze
+ else ->
+ MeshtasticIcons.Device to
+ when (deviceType) {
+ DeviceType.BLE -> Icons.Rounded.Bluetooth
+ DeviceType.TCP -> Icons.Rounded.Wifi
+ DeviceType.USB -> Icons.Rounded.Usb
+ else -> null
+ }
+ }
+
+ val foregroundPainter = connectionTypeIcon?.let { rememberVectorPainter(it) }
+
+ Icon(
+ imageVector = backgroundIcon,
+ contentDescription = null,
+ tint = tint,
+ modifier =
+ modifier.drawWithContent {
+ drawContent()
+ foregroundPainter?.let {
+ @Suppress("MagicNumber")
+ val badgeSize = size.width * .45f
+ with(it) { draw(Size(badgeSize, badgeSize), colorFilter = ColorFilter.tint(tint)) }
+ }
+ },
+ )
+}
+
+class TopLevelDestinationProvider : PreviewParameterProvider {
+ override val values: Sequence = TopLevelDestination.entries.asSequence()
+}
+
+class ConnectionStateProvider : PreviewParameterProvider {
+ override val values: Sequence =
+ sequenceOf(ConnectionState.CONNECTED, ConnectionState.DEVICE_SLEEP, ConnectionState.DISCONNECTED)
+}
+
+class DeviceTypeProvider : PreviewParameterProvider {
+ override val values: Sequence = sequenceOf(DeviceType.BLE, DeviceType.TCP, DeviceType.USB)
+}
+
+@PreviewLightDark
+@Composable
+private fun TopLevelNavIconPreviewConnectionStates(
+ @PreviewParameter(TopLevelDestinationProvider::class) destination: TopLevelDestination,
+) {
+ AppTheme {
+ TopLevelNavIcon(
+ destination = destination,
+ connectionState = ConnectionState.CONNECTED,
+ deviceType = DeviceType.BLE,
+ )
+ }
+}
+
+@PreviewLightDark
+@Composable
+private fun ConnectionsNavIconPreviewConnectionStates(
+ @PreviewParameter(ConnectionStateProvider::class) connectionState: ConnectionState,
+) {
+ AppTheme { ConnectionsNavIcon(connectionState = connectionState, deviceType = DeviceType.BLE) }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun ConnectionsNavIconPreviewDeviceTypes(@PreviewParameter(DeviceTypeProvider::class) deviceType: DeviceType) {
+ ConnectionsNavIcon(connectionState = ConnectionState.CONNECTED, deviceType = deviceType)
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 828b01baf..4a01508ab 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -120,7 +120,7 @@
Connected: %1$s online
IP Address:
Port:
- Connected to radio
+ Connected
Connected to radio (%s)
Not connected
Connected to radio, but it is sleeping
@@ -621,10 +621,11 @@
Load
User String
Navigate Into
- Connections
- Map
- Contacts
+ Connection
+ Mesh Map
+ Messages
Nodes
+ Share
Set your region
Reply
Your node will periodically send an unencrypted map report packet to the configured MQTT server, this includes id, long and short name, approximate location, hardware model, role, firmware version, LoRa region, modem preset and primary channel name.