mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(map): Add pulsing animation to recently heard nodes (#3495)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
c2ccd18959
commit
411a8b5dbb
3 changed files with 74 additions and 5 deletions
|
|
@ -402,7 +402,7 @@ fun MapView(
|
|||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
}
|
||||
val alpha = (index.toFloat() / (sortedPositions.size.toFloat() - 1))
|
||||
val color = Color(focusedNode!!.colors.second).copy(alpha = alpha)
|
||||
val color = Color(focusedNode.colors.second).copy(alpha = alpha)
|
||||
if (index == sortedPositions.lastIndex) {
|
||||
MarkerComposable(state = markerState, zIndex = 1f) { NodeChip(node = focusedNode) }
|
||||
} else {
|
||||
|
|
@ -428,7 +428,7 @@ fun MapView(
|
|||
}
|
||||
}
|
||||
|
||||
if (sortedPositions.size > 1 && focusedNode != null) {
|
||||
if (sortedPositions.size > 1) {
|
||||
val segments = sortedPositions.windowed(size = 2, step = 1, partialWindows = false)
|
||||
segments.forEachIndexed { index, segmentPoints ->
|
||||
val alpha = (index.toFloat() / (segments.size.toFloat() - 1))
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import com.google.maps.android.clustering.view.DefaultClusterRenderer
|
|||
import com.google.maps.android.compose.Circle
|
||||
import com.google.maps.android.compose.MapsComposeExperimentalApi
|
||||
import com.google.maps.android.compose.clustering.Clustering
|
||||
import org.meshtastic.core.ui.component.NodeChip
|
||||
import org.meshtastic.feature.map.BaseMapViewModel
|
||||
import org.meshtastic.feature.map.model.NodeClusterItem
|
||||
|
||||
|
|
@ -50,7 +49,7 @@ fun NodeClusterMarkers(
|
|||
fillColor = Color(clusterItem.node.colors.second).copy(alpha = 0.2f),
|
||||
strokeColor = Color(clusterItem.node.colors.second),
|
||||
strokeWidth = 2f,
|
||||
zIndex = 1f, // Ensure circles are drawn above markers
|
||||
zIndex = 0f,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +63,7 @@ fun NodeClusterMarkers(
|
|||
navigateToNodeDetails(item.node.num)
|
||||
false
|
||||
},
|
||||
clusterItemContent = { clusterItem -> NodeChip(node = clusterItem.node) },
|
||||
clusterItemContent = { clusterItem -> PulsingNodeChip(node = clusterItem.node) },
|
||||
onClusterManager = { clusterManager ->
|
||||
(clusterManager.renderer as DefaultClusterRenderer).minClusterSize = 10
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.map.component
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.ui.component.NodeChip
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Composable
|
||||
fun PulsingNodeChip(node: Node, modifier: Modifier = Modifier) {
|
||||
val animatedProgress = remember { Animatable(0f) }
|
||||
|
||||
LaunchedEffect(node) {
|
||||
if ((System.currentTimeMillis().milliseconds.inWholeSeconds - node.lastHeard.seconds.inWholeSeconds) <= 5) {
|
||||
launch {
|
||||
animatedProgress.snapTo(0f)
|
||||
animatedProgress.animateTo(
|
||||
targetValue = 1f,
|
||||
animationSpec = tween(durationMillis = 1000, easing = LinearEasing),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier =
|
||||
modifier.drawWithContent {
|
||||
drawContent()
|
||||
if (animatedProgress.value > 0 && animatedProgress.value < 1f) {
|
||||
val alpha = (1f - animatedProgress.value) * 0.3f
|
||||
drawRoundRect(
|
||||
size = size,
|
||||
cornerRadius = CornerRadius(8.dp.toPx()),
|
||||
color = Color.White.copy(alpha = alpha),
|
||||
)
|
||||
}
|
||||
},
|
||||
) {
|
||||
NodeChip(node = node)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue