diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt b/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt
index a53500e64..5cc46bc67 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt
@@ -477,7 +477,7 @@ constructor(
dataPacket.alert ?: getString(Res.string.critical_alert),
)
} else if (updateNotification) {
- scope.handledLaunch { updateMessageNotification(contactKey, dataPacket) }
+ scope.handledLaunch { updateNotification(contactKey, dataPacket) }
}
}
}
@@ -487,30 +487,37 @@ constructor(
private fun getSenderName(packet: DataPacket): String =
nodeManager.nodeDBbyID[packet.from]?.user?.longName ?: getString(Res.string.unknown_username)
- private suspend fun updateMessageNotification(contactKey: String, dataPacket: DataPacket) {
- val message =
- when (dataPacket.dataType) {
- Portnums.PortNum.TEXT_MESSAGE_APP_VALUE -> dataPacket.text!!
- Portnums.PortNum.WAYPOINT_APP_VALUE ->
- getString(Res.string.waypoint_received, dataPacket.waypoint!!.name)
-
- else -> return
+ private suspend fun updateNotification(contactKey: String, dataPacket: DataPacket) {
+ when (dataPacket.dataType) {
+ Portnums.PortNum.TEXT_MESSAGE_APP_VALUE -> {
+ val message = dataPacket.text!!
+ val channelName =
+ if (dataPacket.to == DataPacket.ID_BROADCAST) {
+ radioConfigRepository.channelSetFlow.first().settingsList.getOrNull(dataPacket.channel)?.name
+ } else {
+ null
+ }
+ serviceNotifications.updateMessageNotification(
+ contactKey,
+ getSenderName(dataPacket),
+ message,
+ dataPacket.to == DataPacket.ID_BROADCAST,
+ channelName,
+ )
}
- val channelName =
- if (dataPacket.to == DataPacket.ID_BROADCAST) {
- radioConfigRepository.channelSetFlow.first().settingsList.getOrNull(dataPacket.channel)?.name
- } else {
- null
+ Portnums.PortNum.WAYPOINT_APP_VALUE -> {
+ val message = getString(Res.string.waypoint_received, dataPacket.waypoint!!.name)
+ serviceNotifications.updateWaypointNotification(
+ contactKey,
+ getSenderName(dataPacket),
+ message,
+ dataPacket.waypoint!!.id,
+ )
}
- serviceNotifications.updateMessageNotification(
- contactKey,
- getSenderName(dataPacket),
- message,
- dataPacket.to == DataPacket.ID_BROADCAST,
- channelName,
- )
+ else -> return
+ }
}
private fun rememberReaction(packet: MeshPacket) = scope.handledLaunch {
diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt b/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt
index b56fc0791..223d6505e 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package com.geeksville.mesh.service
import android.app.Notification
@@ -55,6 +54,7 @@ import org.meshtastic.core.strings.meshtastic_low_battery_temporary_remote_notif
import org.meshtastic.core.strings.meshtastic_messages_notifications
import org.meshtastic.core.strings.meshtastic_new_nodes_notifications
import org.meshtastic.core.strings.meshtastic_service_notifications
+import org.meshtastic.core.strings.meshtastic_waypoints_notifications
import org.meshtastic.core.strings.new_node_seen
import org.meshtastic.core.strings.no_local_stats
import org.meshtastic.core.strings.reply
@@ -111,6 +111,13 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
NotificationManager.IMPORTANCE_DEFAULT,
)
+ object Waypoint :
+ NotificationType(
+ "my_waypoints",
+ Res.string.meshtastic_waypoints_notifications,
+ NotificationManager.IMPORTANCE_DEFAULT,
+ )
+
object Alert :
NotificationType(
"my_alerts",
@@ -152,6 +159,7 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
ServiceState,
DirectMessage,
BroadcastMessage,
+ Waypoint,
Alert,
NewNode,
LowBatteryLocal,
@@ -190,6 +198,7 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
NotificationType.DirectMessage,
NotificationType.BroadcastMessage,
+ NotificationType.Waypoint,
NotificationType.NewNode,
NotificationType.LowBatteryLocal,
NotificationType.LowBatteryRemote,
@@ -283,6 +292,11 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
notificationManager.notify(contactKey.hashCode(), notification)
}
+ override fun updateWaypointNotification(contactKey: String, name: String, message: String, waypointId: Int) {
+ val notification = createWaypointNotification(name, message, waypointId)
+ notificationManager.notify(contactKey.hashCode(), notification)
+ }
+
override fun showAlertNotification(contactKey: String, name: String, alert: String) {
val notification = createAlertNotification(contactKey, name, alert)
// Use a consistent, unique ID for each alert source.
@@ -373,6 +387,20 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
return builder.build()
}
+ private fun createWaypointNotification(name: String, message: String, waypointId: Int): Notification {
+ val person = Person.Builder().setName(name).build()
+ val style = NotificationCompat.MessagingStyle(person).addMessage(message, System.currentTimeMillis(), person)
+
+ return commonBuilder(NotificationType.Waypoint, createOpenWaypointIntent(waypointId))
+ .setCategory(Notification.CATEGORY_MESSAGE)
+ .setAutoCancel(true)
+ .setStyle(style)
+ .setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
+ .setWhen(System.currentTimeMillis())
+ .setShowWhen(true)
+ .build()
+ }
+
private fun createAlertNotification(contactKey: String, name: String, alert: String): Notification {
val person = Person.Builder().setName(name).build()
val style = NotificationCompat.MessagingStyle(person).addMessage(alert, System.currentTimeMillis(), person)
@@ -454,6 +482,19 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
}
}
+ private fun createOpenWaypointIntent(waypointId: Int): PendingIntent {
+ val deepLinkUri = "$DEEP_LINK_BASE_URI/map?waypointId=$waypointId".toUri()
+ val deepLinkIntent =
+ Intent(Intent.ACTION_VIEW, deepLinkUri, context, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
+ }
+
+ return TaskStackBuilder.create(context).run {
+ addNextIntentWithParentStack(deepLinkIntent)
+ getPendingIntent(waypointId, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
+ }
+ }
+
private fun createReplyAction(contactKey: String): NotificationCompat.Action {
val replyLabel = getString(Res.string.reply)
val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build()
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 ea07f4f46..26e910ee8 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
@file:Suppress("MatchingDeclarationName")
package com.geeksville.mesh.ui
@@ -161,7 +160,7 @@ import org.meshtastic.proto.MeshProtos
enum class TopLevelDestination(val label: StringResource, val icon: ImageVector, val route: Route) {
Conversations(Res.string.conversations, MeshtasticIcons.Conversations, ContactsRoutes.ContactsGraph),
Nodes(Res.string.nodes, MeshtasticIcons.Nodes, NodesRoutes.NodesGraph),
- Map(Res.string.map, MeshtasticIcons.Map, MapRoutes.Map),
+ Map(Res.string.map, MeshtasticIcons.Map, MapRoutes.Map()),
Settings(Res.string.bottom_nav_settings, MeshtasticIcons.Settings, SettingsRoutes.SettingsGraph()),
Connections(Res.string.connections, Icons.Rounded.Wifi, ConnectionsRoutes.ConnectionsGraph),
;
diff --git a/app/src/test/java/com/geeksville/mesh/service/Fakes.kt b/app/src/test/java/com/geeksville/mesh/service/Fakes.kt
index 1727762bb..8e1f784b3 100644
--- a/app/src/test/java/com/geeksville/mesh/service/Fakes.kt
+++ b/app/src/test/java/com/geeksville/mesh/service/Fakes.kt
@@ -89,6 +89,8 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
channelName: String?,
) {}
+ override fun updateWaypointNotification(contactKey: String, name: String, message: String, waypointId: Int) {}
+
override fun showAlertNotification(contactKey: String, name: String, alert: String) {}
override fun showNewNodeSeenNotification(node: NodeEntity) {}
diff --git a/core/navigation/src/main/kotlin/org/meshtastic/core/navigation/Routes.kt b/core/navigation/src/main/kotlin/org/meshtastic/core/navigation/Routes.kt
index 343c15ea1..b7a923c66 100644
--- a/core/navigation/src/main/kotlin/org/meshtastic/core/navigation/Routes.kt
+++ b/core/navigation/src/main/kotlin/org/meshtastic/core/navigation/Routes.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package org.meshtastic.core.navigation
import kotlinx.serialization.Serializable
@@ -50,7 +49,7 @@ object ContactsRoutes {
}
object MapRoutes {
- @Serializable data object Map : Route
+ @Serializable data class Map(val waypointId: Int? = null) : Route
}
object NodesRoutes {
diff --git a/core/service/src/main/kotlin/org/meshtastic/core/service/MeshServiceNotifications.kt b/core/service/src/main/kotlin/org/meshtastic/core/service/MeshServiceNotifications.kt
index 14dc2e17d..54ce8031a 100644
--- a/core/service/src/main/kotlin/org/meshtastic/core/service/MeshServiceNotifications.kt
+++ b/core/service/src/main/kotlin/org/meshtastic/core/service/MeshServiceNotifications.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package org.meshtastic.core.service
import android.app.Notification
@@ -24,6 +23,7 @@ import org.meshtastic.proto.TelemetryProtos
const val SERVICE_NOTIFY_ID = 101
+@Suppress("TooManyFunctions")
interface MeshServiceNotifications {
fun clearNotifications()
@@ -39,6 +39,8 @@ interface MeshServiceNotifications {
channelName: String?,
)
+ fun updateWaypointNotification(contactKey: String, name: String, message: String, waypointId: Int)
+
fun showAlertNotification(contactKey: String, name: String, alert: String)
fun showNewNodeSeenNotification(node: NodeEntity)
diff --git a/core/strings/src/commonMain/composeResources/values/strings.xml b/core/strings/src/commonMain/composeResources/values/strings.xml
index 2d3f112d8..ab66293c0 100644
--- a/core/strings/src/commonMain/composeResources/values/strings.xml
+++ b/core/strings/src/commonMain/composeResources/values/strings.xml
@@ -256,6 +256,7 @@
New messages below
Direct message notifications
Broadcast message notifications
+ Waypoint notifications
Alert notifications
Firmware update required.
The radio firmware is too old to talk to this application. For more information on this see our Firmware Installation guide.
diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt
index 606004375..e87821579 100644
--- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt
+++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package org.meshtastic.feature.map
import android.Manifest // Added for Accompanist
@@ -336,6 +335,18 @@ fun MapView(
val nodes by mapViewModel.nodes.collectAsStateWithLifecycle()
val waypoints by mapViewModel.waypoints.collectAsStateWithLifecycle(emptyMap())
+ val selectedWaypointId by mapViewModel.selectedWaypointId.collectAsStateWithLifecycle()
+
+ LaunchedEffect(selectedWaypointId, waypoints) {
+ if (selectedWaypointId != null && waypoints.containsKey(selectedWaypointId)) {
+ waypoints[selectedWaypointId]?.data?.waypoint?.let { pt ->
+ val geoPoint = GeoPoint(pt.latitudeI * 1e-7, pt.longitudeI * 1e-7)
+ map.controller.setCenter(geoPoint)
+ map.controller.setZoom(WAYPOINT_ZOOM)
+ }
+ }
+ }
+
val tracerouteSelection =
remember(tracerouteOverlay, tracerouteNodePositions, nodes) {
mapViewModel.tracerouteNodeSelection(
@@ -502,7 +513,7 @@ fun MapView(
}
@Suppress("MagicNumber")
- fun MapView.onWaypointChanged(waypoints: Collection): List {
+ fun MapView.onWaypointChanged(waypoints: Collection, selectedWaypointId: Int?): List {
val dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
return waypoints.mapNotNull { waypoint ->
val pt = waypoint.data.waypoint ?: return@mapNotNull null
@@ -540,7 +551,9 @@ fun MapView(
com.meshtastic.core.strings.getString(Res.string.expires) +
": $expireTimeStr"
position = GeoPoint(pt.latitudeI * 1e-7, pt.longitudeI * 1e-7)
- setVisible(false) // This seems to be always false, was this intended?
+ if (selectedWaypointId == pt.id) {
+ showInfoWindow()
+ }
setOnLongClickListener {
showMarkerLongPressDialog(pt.id)
true
@@ -716,7 +729,7 @@ fun MapView(
with(mapView) {
updateMarkers(
onNodesChanged(nodesForMarkers),
- onWaypointChanged(waypoints.values),
+ onWaypointChanged(waypoints.values, selectedWaypointId),
nodeClusterer,
)
}
@@ -1082,6 +1095,7 @@ private const val EARTH_RADIUS_METERS = 6_371_000.0
private const val TRACEROUTE_OFFSET_METERS = 100.0
private const val TRACEROUTE_SINGLE_POINT_ZOOM = 12.0
private const val TRACEROUTE_ZOOM_OUT_LEVELS = 0.5
+private const val WAYPOINT_ZOOM = 15.0
private fun Double.toRad(): Double = Math.toRadians(this)
diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewModel.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewModel.kt
index 93e5c03bf..2ae3ac387 100644
--- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewModel.kt
+++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,21 +14,27 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package org.meshtastic.feature.map
+import androidx.lifecycle.SavedStateHandle
+import androidx.navigation.toRoute
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import org.meshtastic.core.common.BuildConfigProvider
import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.data.repository.PacketRepository
import org.meshtastic.core.data.repository.RadioConfigRepository
import org.meshtastic.core.model.DataPacket
+import org.meshtastic.core.navigation.MapRoutes
import org.meshtastic.core.prefs.map.MapPrefs
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
import javax.inject.Inject
+@Suppress("LongParameterList")
@HiltViewModel
class MapViewModel
@Inject
@@ -39,8 +45,12 @@ constructor(
serviceRepository: ServiceRepository,
radioConfigRepository: RadioConfigRepository,
buildConfigProvider: BuildConfigProvider,
+ savedStateHandle: SavedStateHandle,
) : BaseMapViewModel(mapPrefs, nodeRepository, packetRepository, serviceRepository) {
+ private val _selectedWaypointId = MutableStateFlow(savedStateHandle.toRoute().waypointId)
+ val selectedWaypointId: StateFlow = _selectedWaypointId.asStateFlow()
+
var mapStyleId: Int
get() = mapPrefs.mapStyle
set(value) {
diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt
index d19568b17..5494020ed 100644
--- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt
+++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
@file:Suppress("MagicNumber")
package org.meshtastic.feature.map
@@ -263,6 +262,8 @@ fun MapView(
.collectAsStateWithLifecycle(listOf())
val waypoints by mapViewModel.waypoints.collectAsStateWithLifecycle(emptyMap())
val displayableWaypoints = waypoints.values.mapNotNull { it.data.waypoint }
+ val selectedWaypointId by mapViewModel.selectedWaypointId.collectAsStateWithLifecycle()
+
val tracerouteSelection =
remember(tracerouteOverlay, tracerouteNodePositions, allNodes) {
mapViewModel.tracerouteNodeSelection(
@@ -581,6 +582,7 @@ fun MapView(
isConnected = isConnected,
unicodeEmojiToBitmapProvider = ::unicodeEmojiToBitmap,
onEditWaypointRequest = { waypointToEdit -> editingWaypoint = waypointToEdit },
+ selectedWaypointId = selectedWaypointId,
)
MapEffect(mapLayers) { map ->
diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt
index 6aaaa4b55..3e4a19d18 100644
--- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt
+++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,13 +14,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package org.meshtastic.feature.map
import android.app.Application
import android.net.Uri
import androidx.core.net.toFile
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
+import androidx.navigation.toRoute
import co.touchlab.kermit.Logger
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.CameraPosition
@@ -52,6 +53,7 @@ import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.data.repository.PacketRepository
import org.meshtastic.core.data.repository.RadioConfigRepository
import org.meshtastic.core.datastore.UiPreferencesDataSource
+import org.meshtastic.core.navigation.MapRoutes
import org.meshtastic.core.prefs.map.GoogleMapsPrefs
import org.meshtastic.core.prefs.map.MapPrefs
import org.meshtastic.core.service.ServiceRepository
@@ -91,8 +93,12 @@ constructor(
serviceRepository: ServiceRepository,
private val customTileProviderRepository: CustomTileProviderRepository,
uiPreferencesDataSource: UiPreferencesDataSource,
+ savedStateHandle: SavedStateHandle,
) : BaseMapViewModel(mapPrefs, nodeRepository, packetRepository, serviceRepository) {
+ private val _selectedWaypointId = MutableStateFlow(savedStateHandle.toRoute().waypointId)
+ val selectedWaypointId: StateFlow = _selectedWaypointId.asStateFlow()
+
private val targetLatLng =
googleMapsPrefs.cameraTargetLat
.takeIf { it != 0.0 }
@@ -258,6 +264,17 @@ constructor(
loadPersistedMapType()
}
loadPersistedLayers()
+
+ selectedWaypointId.value?.let { wpId ->
+ viewModelScope.launch {
+ val wpMap = waypoints.first { it.containsKey(wpId) }
+ wpMap[wpId]?.let { packet ->
+ val waypoint = packet.data.waypoint!!
+ val latLng = LatLng(waypoint.latitudeI / 1e7, waypoint.longitudeI / 1e7)
+ cameraPositionState.position = CameraPosition.fromLatLngZoom(latLng, 15f)
+ }
+ }
+ }
}
fun saveCameraPosition(cameraPosition: CameraPosition) {
diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt
index 89ae93a40..b5a376e10 100644
--- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt
+++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025 Meshtastic LLC
+ * Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,10 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
package org.meshtastic.feature.map.component
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import com.google.android.gms.maps.model.BitmapDescriptor
@@ -41,6 +41,7 @@ fun WaypointMarkers(
isConnected: Boolean,
unicodeEmojiToBitmapProvider: (Int) -> BitmapDescriptor,
onEditWaypointRequest: (MeshProtos.Waypoint) -> Unit,
+ selectedWaypointId: Int? = null,
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
@@ -49,6 +50,12 @@ fun WaypointMarkers(
val markerState =
rememberUpdatedMarkerState(position = LatLng(waypoint.latitudeI * DEG_D, waypoint.longitudeI * DEG_D))
+ LaunchedEffect(selectedWaypointId) {
+ if (selectedWaypointId == waypoint.id) {
+ markerState.showInfoWindow()
+ }
+ }
+
Marker(
state = markerState,
icon =