diff --git a/app/src/main/java/com/matedroid/ui/screens/drives/DriveDetailScreen.kt b/app/src/main/java/com/matedroid/ui/screens/drives/DriveDetailScreen.kt index c60dfbd..e1cf20c 100644 --- a/app/src/main/java/com/matedroid/ui/screens/drives/DriveDetailScreen.kt +++ b/app/src/main/java/com/matedroid/ui/screens/drives/DriveDetailScreen.kt @@ -78,7 +78,9 @@ import com.matedroid.data.api.models.Units import com.matedroid.data.repository.WeatherPoint import com.matedroid.domain.model.UnitFormatter import com.matedroid.ui.components.AnnotationRange +import com.matedroid.ui.components.BarChartData import com.matedroid.ui.components.FullscreenLineChart +import com.matedroid.ui.components.InteractiveBarChart import com.matedroid.ui.theme.CarColorPalettes import org.osmdroid.config.Configuration import org.osmdroid.tileprovider.tilesource.TileSourceFactory @@ -327,6 +329,11 @@ private fun DriveDetailContent( fractionToTimeLabel = fractionToTimeLabel ) } + + SpeedHistogramCard( + positions = detail.positions, + units = units + ) } } @@ -781,6 +788,103 @@ private fun ElevationChartCard( ) } +@Composable +private fun SpeedHistogramCard( + positions: List, + units: Units? +) { + val speeds = positions.mapNotNull { it.speed } + if (speeds.size < 2) return + + val isImperial = units?.isImperial == true + val bucketSize = if (isImperial) 5 else 10 + val speedUnit = UnitFormatter.getSpeedUnit(units) + + val histogramData = remember(speeds, bucketSize) { + buildSpeedHistogram(speeds, bucketSize) + } + + if (histogramData.isEmpty()) return + + Card( + modifier = Modifier.fillMaxWidth(), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surface + ) + ) { + Column( + modifier = Modifier.padding(16.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(bottom = 12.dp) + ) { + Icon( + imageVector = Icons.Default.Speed, + contentDescription = null, + modifier = Modifier.size(20.dp), + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = stringResource(R.string.speed_distribution), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold + ) + } + + InteractiveBarChart( + data = histogramData, + barColor = MaterialTheme.colorScheme.primary, + showEveryNthLabel = if (histogramData.size > 8) 2 else 1, + valueFormatter = { pct -> + if (pct < 10.0) "%.1f%%".format(pct) else "%.0f%%".format(pct) + }, + yAxisFormatter = { pct -> + if (pct < 10.0) "%.1f%%".format(pct) else "%.0f%%".format(pct) + }, + modifier = Modifier.fillMaxWidth() + ) + } + } +} + +/** + * Builds speed histogram data with buckets of [bucketSize] units. + * Returns bar chart data with percentage values. + * + * Note: The TeslaMate API pre-converts speed to the user's unit system, + * so speeds are already in km/h or mph — no conversion needed here. + */ +private fun buildSpeedHistogram( + speeds: List, + bucketSize: Int +): List { + if (speeds.isEmpty()) return emptyList() + + val minSpeed = (speeds.min() / bucketSize) * bucketSize + val maxSpeed = ((speeds.max() / bucketSize) + 1) * bucketSize + val total = speeds.size.toDouble() + + val buckets = mutableListOf() + var bucketStart = minSpeed + while (bucketStart < maxSpeed) { + val bucketEnd = bucketStart + bucketSize + val count = speeds.count { it >= bucketStart && it < bucketEnd } + val pct = (count / total) * 100.0 + buckets.add( + BarChartData( + label = "$bucketStart", + value = pct, + displayValue = if (pct < 10.0) "%.1f%%".format(pct) else "%.0f%%".format(pct) + ) + ) + bucketStart = bucketEnd + } + + return buckets +} + @Composable private fun ChartCard( title: String, diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 188cea9..a926804 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -346,6 +346,8 @@ Perfil de potència Nivell de bateria Perfil d\'altitud + + Distribució de velocitat Temps al llarg del trajecte diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3ca4158..9f0f637 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -346,6 +346,8 @@ Perfil de potencia Nivel de batería Perfil de altitud + + Distribución de velocidad Clima en el trayecto diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 8ce6faa..2f14e9b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -346,6 +346,8 @@ Profilo potenza Livello batteria Profilo altitudine + + Distribuzione velocità Meteo lungo il percorso diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7403a6..58c3615 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -346,6 +346,8 @@ Power Profile Battery Level Elevation Profile + + Speed Distribution Weather along the way