diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/CooldownOutlinedIconButton.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/CooldownIconButton.kt similarity index 96% rename from feature/node/src/main/kotlin/org/meshtastic/feature/node/component/CooldownOutlinedIconButton.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/CooldownIconButton.kt index b6c27c3be..91348bc2c 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/CooldownOutlinedIconButton.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/CooldownIconButton.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.node.component +package org.meshtastic.core.ui.component import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween @@ -36,8 +36,8 @@ 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 -internal const val REQUEST_NEIGHBORS_COOL_DOWN_TIME_MS = 180000L // 3 minutes +const val COOL_DOWN_TIME_MS = 30000L +const val REQUEST_NEIGHBORS_COOL_DOWN_TIME_MS = 180000L // 3 minutes @Composable fun CooldownIconButton( diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/DeliveryInfoDialog.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/DeliveryInfoDialog.kt similarity index 94% rename from feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/DeliveryInfoDialog.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/DeliveryInfoDialog.kt index f02ac9218..3a53f4310 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/DeliveryInfoDialog.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/DeliveryInfoDialog.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.messaging +package org.meshtastic.core.ui.component import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -33,15 +33,13 @@ import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.close import org.meshtastic.core.resources.relays import org.meshtastic.core.resources.resend -import org.meshtastic.core.ui.component.MeshtasticDialog -@Suppress("UnusedParameter") @Composable fun DeliveryInfo( title: StringResource, resendOption: Boolean, text: StringResource? = null, - relayNodeName: String? = null, + @Suppress("UNUSED_PARAMETER") relayNodeName: String? = null, relays: Int = 0, onConfirm: (() -> Unit) = {}, onDismiss: () -> Unit = {}, diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCard.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/InfoCard.kt similarity index 96% rename from feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCard.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/InfoCard.kt index 927f37592..4175cdd2c 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCard.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/InfoCard.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.node.component +package org.meshtastic.core.ui.component import android.content.ClipData import androidx.compose.foundation.ExperimentalFoundationApi @@ -120,6 +120,6 @@ fun InfoCard( } @Composable -internal fun DrawableInfoCard(iconRes: DrawableResource, text: String, value: String, rotateIcon: Float = 0f) { +fun DrawableInfoCard(iconRes: DrawableResource, text: String, value: String, rotateIcon: Float = 0f) { InfoCard(iconRes = iconRes, text = text, value = value, rotateIcon = rotateIcon) } diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActions.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageActions.kt similarity index 93% rename from feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActions.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageActions.kt index 3ef1e3ccb..6efe377bb 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActions.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageActions.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.messaging.component +package org.meshtastic.core.ui.component import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade @@ -49,7 +49,7 @@ import org.meshtastic.core.resources.reply import org.meshtastic.core.ui.emoji.EmojiPickerDialog @Composable -internal fun ReactionButton(onSendReaction: (String) -> Unit = {}) { +fun ReactionButton(onSendReaction: (String) -> Unit = {}) { var showEmojiPickerDialog by remember { mutableStateOf(false) } if (showEmojiPickerDialog) { EmojiPickerDialog( @@ -66,7 +66,7 @@ internal fun ReactionButton(onSendReaction: (String) -> Unit = {}) { } @Composable -private fun ReplyButton(onClick: () -> Unit = {}) = IconButton( +fun ReplyButton(onClick: () -> Unit = {}) = IconButton( onClick = onClick, content = { Icon(imageVector = Icons.AutoMirrored.Filled.Reply, contentDescription = stringResource(Res.string.reply)) @@ -74,7 +74,7 @@ private fun ReplyButton(onClick: () -> Unit = {}) = IconButton( ) @Composable -internal fun MessageStatusButton(onStatusClick: () -> Unit = {}, status: MessageStatus, fromLocal: Boolean) = +fun MessageStatusButton(onStatusClick: () -> Unit = {}, status: MessageStatus, fromLocal: Boolean) = AnimatedVisibility(visible = fromLocal) { IconButton(onClick = onStatusClick) { Crossfade(targetState = status, label = "MessageStatusIcon") { currentStatus -> @@ -98,7 +98,7 @@ internal fun MessageStatusButton(onStatusClick: () -> Unit = {}, status: Message @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -internal fun MessageActions( +fun MessageActions( modifier: Modifier = Modifier, isLocal: Boolean = false, status: MessageStatus?, diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageActionsBottomSheet.kt similarity index 99% rename from feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageActionsBottomSheet.kt index f95c64b45..6eb30397c 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageActionsBottomSheet.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.messaging.component +package org.meshtastic.core.ui.component import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageBubble.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageBubble.kt similarity index 95% rename from feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageBubble.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageBubble.kt index 01466613b..2d0bf72e8 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageBubble.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageBubble.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.messaging.component +package org.meshtastic.core.ui.component import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -29,7 +29,7 @@ import androidx.compose.ui.unit.dp * @param hasSamePrev Whether the previous message in the list is from the same sender. * @param hasSameNext Whether the next message in the list is from the same sender. */ -internal fun getMessageBubbleShape( +fun getMessageBubbleShape( cornerRadius: Dp, isSender: Boolean, hasSamePrev: Boolean = false, diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageItem.kt similarity index 98% rename from feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageItem.kt index 115e3633e..c582573bb 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MessageItem.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.messaging.component +package org.meshtastic.core.ui.component import android.content.ClipData import androidx.compose.foundation.background @@ -71,11 +71,6 @@ import org.meshtastic.core.resources.filter_message_label import org.meshtastic.core.resources.message_delivery_status import org.meshtastic.core.resources.reply import org.meshtastic.core.resources.sample_message -import org.meshtastic.core.ui.component.AutoLinkText -import org.meshtastic.core.ui.component.NodeChip -import org.meshtastic.core.ui.component.Rssi -import org.meshtastic.core.ui.component.Snr -import org.meshtastic.core.ui.component.TransportIcon import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider import org.meshtastic.core.ui.emoji.EmojiPicker import org.meshtastic.core.ui.icon.Acknowledged @@ -92,7 +87,7 @@ import org.meshtastic.core.ui.theme.MessageItemColors @OptIn(ExperimentalMaterial3Api::class) @Suppress("LongMethod", "CyclomaticComplexMethod") @Composable -internal fun MessageItem( +fun MessageItem( modifier: Modifier = Modifier, node: Node, ourNode: Node, @@ -449,7 +444,7 @@ private fun OriginalMessageSnippet( @PreviewLightDark @Composable -private fun MessageItemPreview() { +fun MessageItemPreview() { val sent = Message( text = stringResource(Res.string.sample_message), diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeItem.kt similarity index 98% rename from feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeItem.kt index f8b895552..1021cd6b8 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeItem.kt @@ -16,7 +16,7 @@ */ @file:Suppress("MagicNumber") -package org.meshtastic.feature.node.component +package org.meshtastic.core.ui.component import android.content.res.Configuration import androidx.compose.foundation.combinedClickable @@ -64,6 +64,7 @@ import org.meshtastic.core.resources.elevation_suffix import org.meshtastic.core.resources.signal_quality import org.meshtastic.core.resources.unknown_username import org.meshtastic.core.resources.voltage +import org.meshtastic.core.service.ConnectionState import org.meshtastic.core.ui.component.AirQualityInfo import org.meshtastic.core.ui.component.ChannelInfo import org.meshtastic.core.ui.component.DistanceInfo @@ -117,7 +118,7 @@ fun NodeItem( val isFavorite = remember(thatNode) { thatNode.isFavorite } val isMuted = remember(thatNode) { thatNode.isMuted } val isIgnored = thatNode.isIgnored - val originalLongName = (thatNode.user.long_name ?: "").ifEmpty { stringResource(Res.string.unknown_username) } + val originalLongName = thatNode.user.long_name.ifEmpty { stringResource(Res.string.unknown_username) } val isThisNode = remember(thatNode) { thisNode?.num == thatNode.num } val system = @@ -318,7 +319,7 @@ private fun gatherSensors(node: Node, tempInFahrenheit: Boolean, contentColor: C val env = node.environmentMetrics val pax = node.paxcounter - if ((pax.ble ?: 0) != 0 || (pax.wifi ?: 0) != 0) { + if (pax.ble != 0 || pax.wifi != 0) { items.add { PaxcountInfo(pax = "B:${pax.ble ?: 0} W:${pax.wifi ?: 0}", contentColor = contentColor) } } if ((env.temperature ?: 0f) != 0f) { diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeStatusIcons.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeStatusIcons.kt similarity index 98% rename from feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeStatusIcons.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeStatusIcons.kt index 5546b3cbe..d86bdc4c8 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeStatusIcons.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeStatusIcons.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.node.component +package org.meshtastic.core.ui.component import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding @@ -182,7 +182,7 @@ private fun StatusBadge( tint: Color = LocalContentColor.current, ) { TooltipBox( - positionProvider = TooltipDefaults.rememberTooltipPositionProvider(), + positionProvider = TooltipDefaults.rememberTooltipPositionProvider(TooltipAnchorPosition.Above), tooltip = { PlainTooltip { Text(stringResource(tooltipText)) } }, state = rememberTooltipState(), ) { diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/Reaction.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/Reaction.kt similarity index 97% rename from feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/Reaction.kt rename to core/ui/src/main/kotlin/org/meshtastic/core/ui/component/Reaction.kt index 0011e1e5c..cb91d83da 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/Reaction.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/Reaction.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.feature.messaging.component +package org.meshtastic.core.ui.component import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.BorderStroke @@ -72,14 +72,10 @@ import org.meshtastic.core.resources.message_status_enroute import org.meshtastic.core.resources.message_status_queued import org.meshtastic.core.resources.react import org.meshtastic.core.resources.you -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.User @Composable @@ -137,7 +133,7 @@ private fun ReactionItem( @OptIn(ExperimentalLayoutApi::class) @Composable -internal fun ReactionRow( +fun ReactionRow( modifier: Modifier = Modifier, reactions: List = emptyList(), myId: String? = null, @@ -193,7 +189,7 @@ private fun AddReactionButton(modifier: Modifier = Modifier, onSendReaction: (St @Suppress("LongMethod", "CyclomaticComplexMethod") @Composable -internal fun ReactionDialog( +fun ReactionDialog( reactions: List, onDismiss: () -> Unit = {}, myId: String? = null, @@ -334,7 +330,7 @@ private fun ReactionItemPreview() { @Preview @Composable -private fun ReactionRowPreview() { +fun ReactionRowPreview() { AppTheme { ReactionRow( reactions = diff --git a/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/AppScreenshotTest.kt b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/AppScreenshotTest.kt new file mode 100644 index 000000000..52a6952d5 --- /dev/null +++ b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/AppScreenshotTest.kt @@ -0,0 +1,94 @@ +/* + * 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 . + */ +package org.meshtastic.core.ui + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import com.android.tools.screenshot.PreviewTest +import org.meshtastic.core.model.Channel +import org.meshtastic.core.ui.component.AdaptiveTwoPane +import org.meshtastic.core.ui.component.MainAppBar +import org.meshtastic.core.ui.component.preview.previewNode +import org.meshtastic.core.ui.theme.AppTheme +import org.meshtastic.core.ui.qr.ScannedQrCodeDialog +import org.meshtastic.proto.ChannelSet + +class AppScreenshotTest { + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun MainAppBarTest() { + AppTheme { + MainAppBar( + title = "Meshtastic", + subtitle = "Connected to Node", + ourNode = previewNode, + showNodeChip = true, + canNavigateUp = false, + onNavigateUp = {}, + actions = {}, + onClickChip = {} + ) + } + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun ScannedQrCodeDialogTest() { + AppTheme { + ScannedQrCodeDialog( + channels = ChannelSet( + settings = listOf(Channel.default.settings), + lora_config = Channel.default.loraConfig + ), + incoming = ChannelSet( + settings = listOf(Channel.default.settings), + lora_config = Channel.default.loraConfig + ), + onDismiss = {}, + onConfirm = {}, + ) + } + } + + @PreviewTest + @Preview(showBackground = true, widthDp = 800) + @Composable + fun AdaptiveTwoPaneExpandedTest() { + AppTheme { + AdaptiveTwoPane( + first = { Text("Left Pane") }, + second = { Text("Right Pane") } + ) + } + } + + @PreviewTest + @Preview(showBackground = true, widthDp = 400) + @Composable + fun AdaptiveTwoPaneCompactTest() { + AppTheme { + AdaptiveTwoPane( + first = { Text("Top Pane") }, + second = { Text("Bottom Pane") } + ) + } + } +} diff --git a/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/ComponentScreenshotTest.kt b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/ComponentScreenshotTest.kt index 9da44d81e..f02ecd106 100644 --- a/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/ComponentScreenshotTest.kt +++ b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/ComponentScreenshotTest.kt @@ -12,11 +12,14 @@ * 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 . + * along with this program. Of not, see . */ package org.meshtastic.core.ui +import android.graphics.Bitmap +import android.net.Uri import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material.icons.Icons @@ -29,8 +32,11 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import com.android.tools.screenshot.PreviewTest import org.meshtastic.core.database.model.Node +import org.meshtastic.core.model.Channel +import org.meshtastic.core.ui.component.AutoLinkText import org.meshtastic.core.ui.component.BatteryInfoPreviewParameterProvider import org.meshtastic.core.ui.component.ChannelInfo +import org.meshtastic.core.ui.component.ChannelSelection import org.meshtastic.core.ui.component.DistanceInfo import org.meshtastic.core.ui.component.ElevationInfo import org.meshtastic.core.ui.component.HopsInfo @@ -40,13 +46,18 @@ import org.meshtastic.core.ui.component.IndoorAirQuality import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.component.MaterialBatteryInfo import org.meshtastic.core.ui.component.NodeChip +import org.meshtastic.core.ui.component.QrDialog import org.meshtastic.core.ui.component.SatelliteCountInfo +import org.meshtastic.core.ui.component.SecurityIcon +import org.meshtastic.core.ui.component.SecurityState import org.meshtastic.core.ui.component.SignalInfo import org.meshtastic.core.ui.component.SwitchListItem import org.meshtastic.core.ui.component.TitledCard +import org.meshtastic.core.ui.component.TransportIcon import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.proto.Config +import org.meshtastic.proto.MeshPacket class ComponentScreenshotTest { @@ -170,4 +181,69 @@ class ComponentScreenshotTest { IndoorAirQuality(iaq = 101, displayMode = IaqDisplayMode.Pill) } } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun AutoLinkTextTest() { + AppTheme { + AutoLinkText("Check out https://meshtastic.org for more info!") + } + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun SecurityIconTest() { + AppTheme { + Column { + SecurityState.entries.forEach { state -> + SecurityIcon(securityState = state) + } + } + } + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun TransportIconTest() { + AppTheme { + Column { + TransportIcon(transport = MeshPacket.TransportMechanism.TRANSPORT_INTERNAL.value, viaMqtt = false) + TransportIcon(transport = MeshPacket.TransportMechanism.TRANSPORT_MQTT.value, viaMqtt = true) + TransportIcon(transport = MeshPacket.TransportMechanism.TRANSPORT_MULTICAST_UDP.value, viaMqtt = false) + } + } + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun ChannelSelectionTest() { + AppTheme { + ChannelSelection( + index = 0, + title = "LongFast", + enabled = true, + isSelected = true, + onSelected = {}, + channel = Channel.default + ) + } + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun QrDialogTest() { + AppTheme { + QrDialog( + title = "Share Contact", + uri = Uri.parse("https://meshtastic.org/u/dummy"), + qrCode = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), + onDismiss = {} + ) + } + } } diff --git a/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/MessageItemScreenshotTest.kt b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/MessageItemScreenshotTest.kt new file mode 100644 index 000000000..fe54b728a --- /dev/null +++ b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/MessageItemScreenshotTest.kt @@ -0,0 +1,40 @@ +/* + * 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 . + */ +package org.meshtastic.core.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import com.android.tools.screenshot.PreviewTest +import org.meshtastic.core.ui.component.MessageItemPreview +import org.meshtastic.core.ui.component.ReactionRowPreview + +class MessageItemScreenshotTest { + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun MessageItemTest() { + MessageItemPreview() + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun ReactionRowTest() { + ReactionRowPreview() + } +} diff --git a/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/NodeItemScreenshotTest.kt b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/NodeItemScreenshotTest.kt new file mode 100644 index 000000000..b25f500f2 --- /dev/null +++ b/core/ui/src/screenshotTest/kotlin/org/meshtastic/core/ui/NodeItemScreenshotTest.kt @@ -0,0 +1,59 @@ +/* + * 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 . + */ +package org.meshtastic.core.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import com.android.tools.screenshot.PreviewTest +import org.meshtastic.core.database.model.Node +import org.meshtastic.core.ui.component.NodeInfoPreview +import org.meshtastic.core.ui.component.NodeInfoSignalPreview +import org.meshtastic.core.ui.component.NodeInfoSimplePreview +import org.meshtastic.core.ui.component.NodeInfoStatusPreview +import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider + +class NodeItemScreenshotTest { + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun NodeInfoSimpleTest() { + NodeInfoSimplePreview() + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun NodeInfoStatusTest() { + NodeInfoStatusPreview() + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun NodeInfoSignalTest() { + NodeInfoSignalPreview() + } + + @PreviewTest + @Preview(showBackground = true) + @Composable + fun NodeInfoTest(@PreviewParameter(NodePreviewParameterProvider::class) node: Node) { + NodeInfoPreview(thatNode = node) + } +} diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/AdaptiveTwoPaneCompactTest_e7711029_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/AdaptiveTwoPaneCompactTest_e7711029_0.png new file mode 100644 index 000000000..e697fecaa Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/AdaptiveTwoPaneCompactTest_e7711029_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/AdaptiveTwoPaneExpandedTest_37699c78_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/AdaptiveTwoPaneExpandedTest_37699c78_0.png new file mode 100644 index 000000000..7cb896446 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/AdaptiveTwoPaneExpandedTest_37699c78_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/MainAppBarTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/MainAppBarTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/MainAppBarTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/ScannedQrCodeDialogTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/ScannedQrCodeDialogTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/AppScreenshotTest/ScannedQrCodeDialogTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/AutoLinkTextTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/AutoLinkTextTest_748aa731_0.png new file mode 100644 index 000000000..b26fff5b0 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/AutoLinkTextTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/ChannelSelectionTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/ChannelSelectionTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/ChannelSelectionTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_1.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_1.png index 10a143fca..3b2254435 100644 Binary files a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_1.png and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_1.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_2.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_2.png index 6244babaf..de1ff97dc 100644 Binary files a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_2.png and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_2.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_3.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_3.png index 41ce867ae..7454e8756 100644 Binary files a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_3.png and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_3.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_4.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_4.png index d032a70d5..9a094dedf 100644 Binary files a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_4.png and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/NodeChipTest_748aa731_41783942_4.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/QrDialogTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/QrDialogTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/QrDialogTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/SecurityIconTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/SecurityIconTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/SecurityIconTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/TransportIconTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/TransportIconTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/ComponentScreenshotTest/TransportIconTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/MessageItemScreenshotTest/MessageItemTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/MessageItemScreenshotTest/MessageItemTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/MessageItemScreenshotTest/MessageItemTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/MessageItemScreenshotTest/ReactionRowTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/MessageItemScreenshotTest/ReactionRowTest_748aa731_0.png new file mode 100644 index 000000000..a6dce867e Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/MessageItemScreenshotTest/ReactionRowTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoSignalTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoSignalTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoSignalTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoSimpleTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoSimpleTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoSimpleTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoStatusTest_748aa731_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoStatusTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoStatusTest_748aa731_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_0.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_0.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_1.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_1.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_1.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_2.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_2.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_2.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_3.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_3.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_3.png differ diff --git a/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_4.png b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_4.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/core/ui/src/screenshotTestFdroidDebug/reference/org/meshtastic/core/ui/NodeItemScreenshotTest/NodeInfoTest_748aa731_41783942_4.png differ diff --git a/feature/messaging/build.gradle.kts b/feature/messaging/build.gradle.kts index 97b81c776..90a9d6fa1 100644 --- a/feature/messaging/build.gradle.kts +++ b/feature/messaging/build.gradle.kts @@ -20,9 +20,15 @@ plugins { alias(libs.plugins.meshtastic.android.library) alias(libs.plugins.meshtastic.android.library.compose) alias(libs.plugins.meshtastic.hilt) + alias(libs.plugins.screenshot) } -configure { namespace = "org.meshtastic.feature.messaging" } +configure { + namespace = "org.meshtastic.feature.messaging" + experimentalProperties["android.experimental.enableScreenshotTest"] = true + + testOptions { unitTests { isIncludeAndroidResources = true } } +} dependencies { implementation(projects.core.analytics) @@ -68,4 +74,10 @@ dependencies { testImplementation(libs.androidx.work.testing) testImplementation(libs.androidx.test.core) testImplementation(libs.robolectric) + + screenshotTestImplementation(libs.screenshot.validation.api) + screenshotTestImplementation(libs.androidx.compose.ui.tooling) + screenshotTestImplementation(libs.compose.multiplatform.runtime) + screenshotTestImplementation(libs.compose.multiplatform.resources) + screenshotTestImplementation(projects.core.resources) } diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt index 25be10430..772e88c77 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt @@ -68,8 +68,9 @@ import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.MessageStatus import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.new_messages_below -import org.meshtastic.feature.messaging.component.MessageItem -import org.meshtastic.feature.messaging.component.ReactionDialog +import org.meshtastic.core.ui.component.DeliveryInfo +import org.meshtastic.core.ui.component.MessageItem +import org.meshtastic.core.ui.component.ReactionDialog internal data class MessageListHandlers( val onUnreadChanged: (Long, Long) -> Unit, diff --git a/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/MessageItemTest_748aa731_0.png b/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/MessageItemTest_748aa731_0.png new file mode 100644 index 000000000..bd0192643 Binary files /dev/null and b/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/MessageItemTest_748aa731_0.png differ diff --git a/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/RawStringTest_748aa731_0.png b/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/RawStringTest_748aa731_0.png new file mode 100644 index 000000000..0f3d0eab5 Binary files /dev/null and b/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/RawStringTest_748aa731_0.png differ diff --git a/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/SimpleBoxTest_748aa731_0.png b/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/SimpleBoxTest_748aa731_0.png new file mode 100644 index 000000000..7dd2ab5b5 Binary files /dev/null and b/feature/messaging/src/screenshotTestDebug/reference/org/meshtastic/feature/messaging/MessageScreenshotTest/SimpleBoxTest_748aa731_0.png differ diff --git a/feature/node/build.gradle.kts b/feature/node/build.gradle.kts index de857e9d9..b2ed1f1f9 100644 --- a/feature/node/build.gradle.kts +++ b/feature/node/build.gradle.kts @@ -21,6 +21,7 @@ plugins { alias(libs.plugins.meshtastic.android.library.flavors) alias(libs.plugins.meshtastic.android.library.compose) alias(libs.plugins.meshtastic.hilt) + alias(libs.plugins.screenshot) } configure { @@ -29,6 +30,8 @@ configure { defaultConfig { manifestPlaceholders["MAPS_API_KEY"] = "DEBUG_KEY" } testOptions { unitTests { isIncludeAndroidResources = true } } + + experimentalProperties["android.experimental.enableScreenshotTest"] = true } dependencies { @@ -70,4 +73,10 @@ dependencies { testImplementation(libs.androidx.test.ext.junit) testImplementation(libs.robolectric) debugImplementation(libs.androidx.compose.ui.test.manifest) + + screenshotTestImplementation(libs.screenshot.validation.api) + screenshotTestImplementation(libs.androidx.compose.ui.tooling) + screenshotTestImplementation(libs.compose.multiplatform.runtime) + screenshotTestImplementation(libs.compose.multiplatform.resources) + screenshotTestImplementation(projects.core.resources) } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt index 05cfd5fc5..d581c8a14 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt @@ -61,6 +61,8 @@ import org.meshtastic.core.resources.uv_lux import org.meshtastic.core.resources.voltage import org.meshtastic.core.resources.weight import org.meshtastic.core.resources.wind +import org.meshtastic.core.ui.component.DrawableInfoCard +import org.meshtastic.core.ui.component.InfoCard import org.meshtastic.feature.node.model.DrawableMetricInfo import org.meshtastic.feature.node.model.VectorMetricInfo import org.meshtastic.proto.Config diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCardPreview.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCardPreview.kt deleted file mode 100644 index f7d46a939..000000000 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCardPreview.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 org.meshtastic.feature.node.component - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Navigation -import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview - -@Preview(name = "Wind Dir -359°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirectionn359() { - PreviewWindDirectionItem(-359f) -} - -@Preview(name = "Wind Dir 0°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirection0() { - PreviewWindDirectionItem(0f) -} - -@Preview(name = "Wind Dir 45°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirection45() { - PreviewWindDirectionItem(45f) -} - -@Preview(name = "Wind Dir 90°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirection90() { - PreviewWindDirectionItem(90f) -} - -@Preview(name = "Wind Dir 180°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirection180() { - PreviewWindDirectionItem(180f) -} - -@Preview(name = "Wind Dir 225°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirection225() { - PreviewWindDirectionItem(225f) -} - -@Preview(name = "Wind Dir 270°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirection270() { - PreviewWindDirectionItem(270f) -} - -@Preview(name = "Wind Dir 315°") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirection315() { - PreviewWindDirectionItem(315f) -} - -@Preview(name = "Wind Dir -45") -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirectionN45() { - PreviewWindDirectionItem(-45f) -} - -@Suppress("detekt:MagicNumber") -@Composable -private fun PreviewWindDirectionItem(windDirection: Float, windSpeed: String = "5 m/s") { - val normalizedBearing = (windDirection + 180) % 360 - InfoCard(icon = Icons.Outlined.Navigation, text = "Wind", value = windSpeed, rotateIcon = normalizedBearing) -} diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt index 6927a7861..a60c27821 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt @@ -31,6 +31,7 @@ import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.channel_1 import org.meshtastic.core.resources.channel_2 import org.meshtastic.core.resources.channel_3 +import org.meshtastic.core.ui.component.InfoCard import org.meshtastic.feature.node.model.VectorMetricInfo /** diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt index 0cee70ea8..61e551467 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt @@ -55,6 +55,9 @@ import org.meshtastic.core.resources.request_air_quality_metrics import org.meshtastic.core.resources.request_telemetry import org.meshtastic.core.resources.telemetry import org.meshtastic.core.resources.userinfo +import org.meshtastic.core.ui.component.COOL_DOWN_TIME_MS +import org.meshtastic.core.ui.component.CooldownOutlinedIconButton +import org.meshtastic.core.ui.component.REQUEST_NEIGHBORS_COOL_DOWN_TIME_MS import org.meshtastic.core.ui.icon.AirQuality import org.meshtastic.core.ui.icon.LineAxis import org.meshtastic.core.ui.icon.MeshtasticIcons diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt index f2a823296..77fe78a9a 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt @@ -88,7 +88,7 @@ import org.meshtastic.core.ui.qr.ScannedQrCodeDialog import org.meshtastic.core.ui.theme.StatusColors.StatusRed import org.meshtastic.core.ui.util.showToast import org.meshtastic.feature.node.component.NodeFilterTextField -import org.meshtastic.feature.node.component.NodeItem +import org.meshtastic.core.ui.component.NodeItem import org.meshtastic.proto.SharedContact @OptIn(ExperimentalMaterial3ExpressiveApi::class) diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt index 006e02fcf..37f5437c2 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt @@ -55,7 +55,7 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusGreen import org.meshtastic.core.ui.theme.StatusColors.StatusOrange import org.meshtastic.core.ui.theme.StatusColors.StatusYellow import org.meshtastic.core.ui.util.annotateNeighborInfo -import org.meshtastic.feature.node.component.CooldownIconButton +import org.meshtastic.core.ui.component.CooldownIconButton import org.meshtastic.feature.node.detail.NodeRequestEffect @OptIn(ExperimentalFoundationApi::class) diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt index 1fdd5cf5b..fab5f95ed 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt @@ -73,7 +73,7 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusOrange import org.meshtastic.core.ui.theme.StatusColors.StatusYellow import org.meshtastic.core.ui.util.annotateTraceroute import org.meshtastic.feature.map.model.TracerouteOverlay -import org.meshtastic.feature.node.component.CooldownIconButton +import org.meshtastic.core.ui.component.CooldownIconButton import org.meshtastic.feature.node.detail.NodeRequestEffect import org.meshtastic.feature.node.metrics.CommonCharts.MS_PER_SEC import org.meshtastic.proto.RouteDiscovery