From 8626a6f629eb672f22b406327627ca691308aaae Mon Sep 17 00:00:00 2001 From: Tomek Zebrowski Date: Mon, 9 Feb 2026 13:16:00 +0100 Subject: [PATCH 1/4] feat: add caching for scale drawings --- .../obd/graphs/renderer/gauge/GaugeDrawer.kt | 112 ++++++++++++++---- .../renderer/performance/PerformanceDrawer.kt | 1 - 2 files changed, 87 insertions(+), 26 deletions(-) diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt index 96ec997f..2390e35a 100644 --- a/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt +++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt @@ -63,6 +63,21 @@ data class DrawerSettings( val dividerHighlightStart: Int = 9, ) +private data class CachedScaleNumber( + val x: Float, + val y: Float, + val text: String, + val color: Int, +) + +private data class ScaleCacheEntry( + val min: Double, + val max: Double, + val radius: Float, + val dividerCount: Int, + val numbers: List, +) + @Suppress("NOTHING_TO_INLINE") internal class GaugeDrawer( settings: ScreenSettings, @@ -98,6 +113,9 @@ internal class GaugeDrawer( strokeCap = Paint.Cap.BUTT } + // Cache map keyed by PID ID + private val scaleNumbersCache = mutableMapOf() + fun drawGauge( canvas: Canvas, metric: Metric, @@ -171,6 +189,7 @@ internal class GaugeDrawer( ) if (scaleEnabled) { + Log.e("EEEEEEEEE"," scaleEnabled: ${metric.pid().pid}") drawNumerals( metric, canvas, @@ -303,7 +322,6 @@ internal class GaugeDrawer( var centerY = (area.centerY() + labelCenterYPadding - (if (settings.isStatisticsEnabled()) 8 else 1) * scaleRationBasedOnScreenSize(area)) - Log.e("WWWWWWWWWw","centerY = $centerY ") val valueHeight = max(textRect.height(), MIN_TEXT_VALUE_HEIGHT) + settings.getGaugeRendererSetting().topOffset val valueY = centerY - valueHeight @@ -468,40 +486,84 @@ internal class GaugeDrawer( radius: Float, area: RectF, ) { + if (metric.source.isNumber()) { val pid = metric.pid() - val startValue = pid.min.toDouble() - val endValue = pid.max.toDouble() - val numberOfItems = (drawerSettings.dividersCount / drawerSettings.scaleStep) + val cachedEntry = scaleNumbersCache[pid.id] + + val isCacheValid = + cachedEntry != null && + cachedEntry.radius == radius && + cachedEntry.min == pid.min.toDouble() && + cachedEntry.max == pid.max.toDouble() && + cachedEntry.dividerCount == drawerSettings.dividersCount + + val scaleNumbers = + if (isCacheValid) { + // Return cached numbers (fast path) + cachedEntry!!.numbers + } else { + // Recalculate and update cache (slow path - only once per config change) + val newNumbers = calculateScaleNumbers(metric, radius, area) + scaleNumbersCache[pid.id] = + ScaleCacheEntry( + min = pid.min.toDouble(), + max = pid.max.toDouble(), + radius = radius, + dividerCount = drawerSettings.dividersCount, + numbers = newNumbers, + ) + newNumbers + } - val scaleRation = scaleRationBasedOnScreenSize(area, targetMin = 0.4f, targetMax = 1.9f) - val stepValue = (endValue - startValue) / numberOfItems - val baseRadius = radius * NUMERALS_RADIUS_SCALE_FACTOR + scaleNumbers.forEach { item -> + numbersPaint.color = item.color + canvas.drawText(item.text, item.x, item.y, numbersPaint) + } + } + } - val start = 0 - val end = drawerSettings.dividersCount + 1 + private fun calculateScaleNumbers( + metric: Metric, + radius: Float, + area: RectF, + ): List { + val result = mutableListOf() + val pid = metric.pid() + val startValue = pid.min.toDouble() + val endValue = pid.max.toDouble() - for (j in start..end step drawerSettings.scaleStep) { - val angle = (drawerSettings.startAngle + j * drawerSettings.dividersStepAngle) * (Math.PI / 180) - val text = valueAsString(metric, value = (startValue + stepValue * j / drawerSettings.scaleStep).round(1)) - val rect = Rect() - numbersPaint.getTextBounds(text, 0, text.length, rect) - numbersPaint.textSize = drawerSettings.scaleNumbersTextSize * scaleRation + val numberOfItems = (drawerSettings.dividersCount / drawerSettings.scaleStep) + val scaleRation = scaleRationBasedOnScreenSize(area, targetMin = 0.4f, targetMax = 1.9f) + val stepValue = (endValue - startValue) / numberOfItems + val baseRadius = radius * NUMERALS_RADIUS_SCALE_FACTOR - val x = area.left + (area.width() / 2.0f + cos(angle) * baseRadius - rect.width() / 2).toFloat() - val y = area.top + (area.height() / 2.0f + sin(angle) * baseRadius + rect.height() / 2).toFloat() + val start = 0 + val end = drawerSettings.dividersCount + 1 - numbersPaint.color = - if (j == (numberOfItems - 1) * drawerSettings.scaleStep || j == numberOfItems * drawerSettings.scaleStep) { - settings.getColorTheme().progressColor - } else { - color(R.color.gray) - } + for (j in start..end step drawerSettings.scaleStep) { + val angle = (drawerSettings.startAngle + j * drawerSettings.dividersStepAngle) * (Math.PI / 180) + val value = (startValue + stepValue * j / drawerSettings.scaleStep).round(1) + val text = valueAsString(metric, value) + + numbersPaint.textSize = drawerSettings.scaleNumbersTextSize * scaleRation + val rect = Rect() + numbersPaint.getTextBounds(text, 0, text.length, rect) + + val x = area.left + (area.width() / 2.0f + cos(angle) * baseRadius - rect.width() / 2).toFloat() + val y = area.top + (area.height() / 2.0f + sin(angle) * baseRadius + rect.height() / 2).toFloat() + + val color = + if (j == (numberOfItems - 1) * drawerSettings.scaleStep || j == numberOfItems * drawerSettings.scaleStep) { + settings.getColorTheme().progressColor + } else { + color(R.color.gray) + } - canvas.drawText(text, x, y, numbersPaint) - } + result.add(CachedScaleNumber(x, y, text, color)) } + return result } private inline fun valueAsString( diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt index 6f208c61..501851d7 100644 --- a/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt +++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt @@ -18,7 +18,6 @@ package org.obd.graphs.renderer.performance import android.content.Context import android.graphics.* -import android.util.Log import org.obd.graphs.bl.collector.Metric import org.obd.graphs.renderer.AbstractDrawer import org.obd.graphs.renderer.GaugeProgressBarType From 6fc4daef9c79024baa9f33c7bc1b353d9530dac6 Mon Sep 17 00:00:00 2001 From: Tomek Zebrowski Date: Mon, 9 Feb 2026 15:07:10 +0100 Subject: [PATCH 2/4] feat: allow to set top margin for performance screen --- .../ui/performance/PerformanceFragment.kt | 4 +- .../ui/performance/PerformanceSettings.kt | 2 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 16 +- .../obd/graphs/renderer/gauge/GaugeDrawer.kt | 2 - .../renderer/performance/PerformanceDrawer.kt | 176 ++++++++---------- 6 files changed, 93 insertions(+), 108 deletions(-) diff --git a/app/src/main/java/org/obd/graphs/ui/performance/PerformanceFragment.kt b/app/src/main/java/org/obd/graphs/ui/performance/PerformanceFragment.kt index 0629f389..e24b6af6 100644 --- a/app/src/main/java/org/obd/graphs/ui/performance/PerformanceFragment.kt +++ b/app/src/main/java/org/obd/graphs/ui/performance/PerformanceFragment.kt @@ -35,6 +35,8 @@ import org.obd.graphs.bl.datalogger.DATA_LOGGER_STOPPED_EVENT import org.obd.graphs.bl.datalogger.DataLoggerRepository import org.obd.graphs.bl.query.Query import org.obd.graphs.bl.query.QueryStrategyType +import org.obd.graphs.preferences.Prefs +import org.obd.graphs.preferences.getS import org.obd.graphs.registerReceiver import org.obd.graphs.renderer.Fps import org.obd.graphs.renderer.SurfaceRenderer @@ -128,7 +130,7 @@ open class PerformanceFragment : Fragment() { metricsCollector, fps, surfaceRendererType = SurfaceRendererType.PERFORMANCE, - viewSettings = ViewSettings(marginTop = 20), + viewSettings = ViewSettings(marginTop = settings.getMarginTop()), ) surfaceController = SurfaceController(renderer) diff --git a/app/src/main/java/org/obd/graphs/ui/performance/PerformanceSettings.kt b/app/src/main/java/org/obd/graphs/ui/performance/PerformanceSettings.kt index befea207..e9049fd0 100644 --- a/app/src/main/java/org/obd/graphs/ui/performance/PerformanceSettings.kt +++ b/app/src/main/java/org/obd/graphs/ui/performance/PerformanceSettings.kt @@ -41,4 +41,6 @@ class PerformanceSettings : ScreenSettings { override fun isStatusPanelEnabled(): Boolean = false override fun getMaxAllowedItemsInColumn(): Int = 8 + + fun getMarginTop(): Int = Prefs.getS("pref.performance.screen_top_margin", "20").toInt() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c6ff2e51..98b7c444 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,6 +3,7 @@ Close Gauge label top offset + Top margin Please wait. This may take a while.... This is an EXPERIMENTAL feature, controlled by the FF_SWITCH_NETWORK_ENABLED feature flag diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index aa774e25..e60ba4ac 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -733,12 +733,24 @@ android:defaultValue="22" android:dialogTitle="@string/pref.performance.screen_label_y_padding" android:key="pref.performance.screen_label_y_padding" - android:max="30" + android:max="34" android:title="@string/pref.performance.screen_label_y_padding" - app:min="-100" + app:min="-60" + app:showSeekBarValue="true" + app:singleLineTitle="false" + app:useSimpleSummaryProvider="true" /> + + + + tripInfoDrawer.drawMetric( + metric, + rowTop, + left + (index * itemWidth), + canvas, + textSize, + statsEnabled = true, + area = area, + castToInt = true + ) } drawDivider(canvas, left, area.width().toFloat(), rowTop + textSize + 4, Color.DKGRAY) rowTop += textSize + 16 - var numGauges = 4 - if (performanceInfoDetails.vehicleSpeed == null) numGauges-- - if (performanceInfoDetails.gas == null) numGauges-- - if (performanceInfoDetails.torque == null) numGauges-- - if (performanceInfoDetails.intakePressure == null) numGauges-- + val availableWidth = area.width().toFloat() + val areaLeft = area.left.toFloat() + + val gauges = listOfNotNull( + performanceInfoDetails.torque, + performanceInfoDetails.intakePressure, + performanceInfoDetails.gas, + performanceInfoDetails.vehicleSpeed + ) val labelCenterYPadding = settings.getPerformanceScreenSettings().labelCenterYPadding - 4 - when (numGauges) { - 4 ->{ - drawGauge(performanceInfoDetails.torque, canvas, rowTop, area.left.toFloat(), area.width() / 2.6f, labelCenterYPadding = labelCenterYPadding) - drawGauge(performanceInfoDetails.intakePressure, canvas, rowTop, (area.left + area.width() / 1.65f), area.width() / 2.6f, labelCenterYPadding = labelCenterYPadding) - drawGauge(performanceInfoDetails.gas, canvas, rowTop - 4f, (area.left + area.width() / 2.6f), area.width() / 4.5f) - drawGauge(performanceInfoDetails.vehicleSpeed, canvas, rowTop + area.height() / 3f, (area.left + area.width() / 2.65f), area.width() / 4.1f) - } - 3 -> { - if (drawGauge(performanceInfoDetails.torque, canvas, rowTop, area.left.toFloat(), area.width() / 2.6f, labelCenterYPadding = labelCenterYPadding)){ - drawGauge(performanceInfoDetails.gas, canvas, rowTop - 4f, (area.left + area.width() /2.9f) , area.width() / 3.7f) - } else { - drawGauge(performanceInfoDetails.gas, canvas, rowTop - 4f, area.left.toFloat(), area.width() / 2.6f, labelCenterYPadding = labelCenterYPadding) - } - if (drawGauge(performanceInfoDetails.intakePressure, canvas, rowTop, (area.left + area.width() / 1.65f), area.width() / 2.6f, labelCenterYPadding = labelCenterYPadding)){ - drawGauge(performanceInfoDetails.vehicleSpeed, canvas, rowTop - 4f, (area.left + area.width() / 2.9f) , area.width() / 3.7f) - } else { - drawGauge(performanceInfoDetails.vehicleSpeed, canvas, rowTop, (area.left + area.width() / 1.65f), area.width() / 2.6f, labelCenterYPadding = labelCenterYPadding) - } + when (gauges.size) { + 4 -> { + val topRowWidth = availableWidth / 2f + + drawGauge(performanceInfoDetails.torque, canvas, rowTop, areaLeft, topRowWidth, labelCenterYPadding) + drawGauge(performanceInfoDetails.intakePressure, canvas, rowTop, areaLeft + topRowWidth, topRowWidth, labelCenterYPadding) + + val topRowHeight = topRowWidth * 0.8f + val bottomRowTop = rowTop + (topRowHeight * 0.45f) + val bottomRowWidth = availableWidth / 3.5f + val centerOffset = (availableWidth - (bottomRowWidth * 2)) / 2f + + drawGauge(performanceInfoDetails.gas, canvas, bottomRowTop, areaLeft + centerOffset - 10f, bottomRowWidth, labelCenterYPadding) + drawGauge(performanceInfoDetails.vehicleSpeed, canvas, bottomRowTop, areaLeft + centerOffset + bottomRowWidth + 10f, bottomRowWidth, labelCenterYPadding) } + 3 -> { + val width = availableWidth / 3f + drawGauge(gauges[0], canvas, rowTop, areaLeft, width, labelCenterYPadding) + drawGauge(gauges[1], canvas, rowTop, areaLeft + width, width, labelCenterYPadding) + drawGauge(gauges[2], canvas, rowTop, areaLeft + (width * 2), width, labelCenterYPadding) + } 2 -> { - // left side - val labelCenterYPadding = 10f - val width = area.width() / 2.0f - - if (!drawGauge(performanceInfoDetails.torque, canvas, rowTop, area.left.toFloat(), width, labelCenterYPadding = labelCenterYPadding)){ - if (!drawGauge(performanceInfoDetails.gas, canvas, rowTop, area.left.toFloat(), width, labelCenterYPadding = labelCenterYPadding)){ - drawGauge(performanceInfoDetails.vehicleSpeed, canvas, rowTop, area.left.toFloat(), width, labelCenterYPadding = labelCenterYPadding) - } - } - //right side - if (!drawGauge(performanceInfoDetails.intakePressure, canvas, rowTop, (area.left + area.width() / 2f) - 6f, - width, labelCenterYPadding = labelCenterYPadding - )){ - if (!drawGauge(performanceInfoDetails.vehicleSpeed, canvas, rowTop, (area.left + area.width() / 2f) - 6f, - width, labelCenterYPadding = labelCenterYPadding - )){ - drawGauge(performanceInfoDetails.gas, canvas, rowTop, (area.left + area.width() / 2f) - 6f, - width, labelCenterYPadding = labelCenterYPadding - ) - } - } + val width = availableWidth / 2f + drawGauge(gauges[0], canvas, rowTop, areaLeft, width, labelCenterYPadding) + drawGauge(gauges[1], canvas, rowTop, areaLeft + width, width, labelCenterYPadding) } 1 -> { - drawGaugeSingle(performanceInfoDetails.torque, canvas, rowTop, area) - drawGaugeSingle(performanceInfoDetails.intakePressure, canvas, rowTop, area) - drawGaugeSingle(performanceInfoDetails.gas, canvas, rowTop, area) - drawGaugeSingle(performanceInfoDetails.vehicleSpeed, canvas, rowTop, area) + drawGauge(gauges[0], canvas, rowTop, areaLeft + (availableWidth / 4f), availableWidth / 2f, labelCenterYPadding = 6f) } } } - fun drawGaugeSingle( - metric: Metric?, - canvas: Canvas, - rowTop: Float, - area: Rect - ) { - drawGauge( - metric, - canvas, - rowTop, - area.left.toFloat() + (area.width() / 4f), - area.width().toFloat() / 2f, - labelCenterYPadding = 6f - ) - } - fun drawGauge( metric: Metric?, canvas: Canvas, @@ -178,22 +150,20 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A left: Float, width: Float, labelCenterYPadding: Float = settings.getPerformanceScreenSettings().labelCenterYPadding, - ): Boolean = - if (metric == null){ + ): Boolean = + if (metric == null) { false - }else { + } else { gaugeDrawer.drawGauge( - canvas = canvas, + canvas = canvas, left = left, - top = top , + top = top, width = width, metric = metric, - labelCenterYPadding = labelCenterYPadding, + labelCenterYPadding = labelCenterYPadding, fontSize = settings.getPerformanceScreenSettings().fontSize, scaleEnabled = false ) true } - - private inline fun maxItemWidth(area: Rect) = (area.width() / 6) } From d7e80f2e20fe1040890dd0dc791b01121f236ea4 Mon Sep 17 00:00:00 2001 From: Tomek Zebrowski Date: Mon, 9 Feb 2026 15:30:45 +0100 Subject: [PATCH 3/4] feat: formatting cleanup --- .../src/main/java/org/obd/graphs/Formatter.kt | 2 +- .../obd/graphs/renderer/gauge/GaugeDrawer.kt | 32 ++++++++----- .../renderer/performance/PerformanceDrawer.kt | 45 ++++++++++++++----- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/common/src/main/java/org/obd/graphs/Formatter.kt b/common/src/main/java/org/obd/graphs/Formatter.kt index c90b7f2f..64fd39ed 100644 --- a/common/src/main/java/org/obd/graphs/Formatter.kt +++ b/common/src/main/java/org/obd/graphs/Formatter.kt @@ -20,7 +20,7 @@ import org.obd.metrics.api.model.ObdMetric import org.obd.metrics.pid.PidDefinition import org.obd.metrics.pid.ValueType -private const val NO_DATA = "No data" +private const val NO_DATA = "--" fun ObdMetric.format( castToInt: Boolean = false, diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt index 3d1896b5..e43db5c0 100644 --- a/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt +++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt @@ -57,7 +57,7 @@ data class DrawerSettings( val dividerWidth: Float = 1f, val lineOffset: Float = 8f, val valueTextSize: Float = 46f, - val labelTextSize: Float = 16f, + val labelTextSize: Float = 12f, val scaleNumbersTextSize: Float = 12f, val dividerHighlightStart: Int = 9, ) @@ -102,7 +102,7 @@ internal class GaugeDrawer( private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - strokeCap = Paint.Cap.BUTT + strokeCap = Paint.Cap.ROUND style = Paint.Style.STROKE color = COLOR_WHITE } @@ -112,7 +112,6 @@ internal class GaugeDrawer( strokeCap = Paint.Cap.BUTT } - // Cache map keyed by PID ID private val scaleNumbersCache = mutableMapOf() fun drawGauge( @@ -231,7 +230,9 @@ internal class GaugeDrawer( val startValue = metric.pid().min.toFloat() val endValue = metric.pid().max.toFloat() - if (value == startValue) { + val clampedValue = value.coerceIn(startValue, endValue) + + if (clampedValue == startValue) { canvas.drawArc( progressRect, drawerSettings.startAngle, @@ -241,7 +242,7 @@ internal class GaugeDrawer( ) } else { val pointAngle = abs(drawerSettings.sweepAngle).toDouble() / (endValue - startValue) - val point = (drawerSettings.startAngle + (value - startValue) * pointAngle).toInt() + val point = (drawerSettings.startAngle + (clampedValue - startValue) * pointAngle).toInt() when (drawerSettings.gaugeProgressBarType) { GaugeProgressBarType.SHORT -> { progressPaint.strokeWidth = strokeWidth @@ -291,10 +292,17 @@ internal class GaugeDrawer( } private fun setProgressGradient(rect: RectF) { - val colors = intArrayOf(COLOR_WHITE, settings.getColorTheme().progressColor) - val gradient = SweepGradient(rect.centerY(), rect.centerX(), colors, null) + val colors = + intArrayOf( + color(R.color.gray), + settings.getColorTheme().progressColor, + Color.RED, + ) + val positions = floatArrayOf(0.0f, 0.75f, 1.0f) + + val gradient = SweepGradient(rect.centerY(), rect.centerX(), colors, positions) val matrix = Matrix() - matrix.postRotate(90f, rect.centerY(), rect.centerX()) + matrix.postRotate(drawerSettings.startAngle - 5, rect.centerY(), rect.centerX()) gradient.setLocalMatrix(matrix) paint.shader = gradient } @@ -335,7 +343,7 @@ internal class GaugeDrawer( pid.units?.let { valuePaint.getTextBounds(it, 0, it.length, unitRect) - canvas.drawText(it, area.centerX() + textRect.width() / 2 + 4, unitY, valuePaint) + canvas.drawText(it, area.centerX() + textRect.width() / 2 + 12, unitY, valuePaint) centerY += unitRect.height() / 2 } @@ -365,6 +373,9 @@ internal class GaugeDrawer( labelPaint.getTextBounds(label, 0, label.length, labelRect) labelY = unitY + labelRect.height() + if (labelY > area.bottom - 5) { + labelY = area.bottom - 5f + } canvas.drawText(label, area.centerX() - (labelRect.width() / 2), labelY, labelPaint) } @@ -484,7 +495,6 @@ internal class GaugeDrawer( radius: Float, area: RectF, ) { - if (metric.source.isNumber()) { val pid = metric.pid() @@ -499,10 +509,8 @@ internal class GaugeDrawer( val scaleNumbers = if (isCacheValid) { - // Return cached numbers (fast path) cachedEntry!!.numbers } else { - // Recalculate and update cache (slow path - only once per config change) val newNumbers = calculateScaleNumbers(metric, radius, area) scaleNumbersCache[pid.id] = ScaleCacheEntry( diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt index 97165560..e030b0a7 100644 --- a/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt +++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt @@ -25,6 +25,7 @@ import org.obd.graphs.renderer.ScreenSettings import org.obd.graphs.renderer.gauge.DrawerSettings import org.obd.graphs.renderer.gauge.GaugeDrawer import org.obd.graphs.renderer.trip.TripInfoDrawer +import org.obd.graphs.isNumber private const val MAX_ITEMS_IN_ROW = 6 @@ -43,10 +44,15 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A private val background: Bitmap = BitmapFactory.decodeResource(context.resources, org.obd.graphs.renderer.R.drawable.drag_race_bg) + private val dividerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.DKGRAY + strokeWidth = 2f + alpha = 100 + } + override fun getBackground(): Bitmap = background override fun recycle() { - // FIX: Removed dangerous super.getBackground().recycle() to prevent crashes this.background.recycle() } @@ -63,7 +69,6 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A ) val itemWidth = area.width() / MAX_ITEMS_IN_ROW.toFloat() - var rowTop = top + 2f val metricsToDraw = listOfNotNull( @@ -78,18 +83,30 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A performanceInfoDetails.gearEngaged ) - // Draw loop using positional arguments to match TripInfoDrawer signature metricsToDraw.take(MAX_ITEMS_IN_ROW).forEachIndexed { index, metric -> + val itemLeft = left + (index * itemWidth) + tripInfoDrawer.drawMetric( metric, rowTop, - left + (index * itemWidth), + itemLeft, canvas, textSize, - statsEnabled = true, + statsEnabled = metric.source.isNumber(), area = area, castToInt = true ) + + if (index < metricsToDraw.take(MAX_ITEMS_IN_ROW).size - 1) { + val lineX = itemLeft + itemWidth + canvas.drawLine( + lineX, + rowTop, + lineX, + rowTop + textSize + 10f, + dividerPaint + ) + } } drawDivider(canvas, left, area.width().toFloat(), rowTop + textSize + 4, Color.DKGRAY) @@ -111,17 +128,16 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A when (gauges.size) { 4 -> { val topRowWidth = availableWidth / 2f - - drawGauge(performanceInfoDetails.torque, canvas, rowTop, areaLeft, topRowWidth, labelCenterYPadding) - drawGauge(performanceInfoDetails.intakePressure, canvas, rowTop, areaLeft + topRowWidth, topRowWidth, labelCenterYPadding) + drawGauge(gauges[0], canvas, rowTop, areaLeft, topRowWidth, labelCenterYPadding) + drawGauge(gauges[1], canvas, rowTop, areaLeft + topRowWidth, topRowWidth, labelCenterYPadding) val topRowHeight = topRowWidth * 0.8f val bottomRowTop = rowTop + (topRowHeight * 0.45f) val bottomRowWidth = availableWidth / 3.5f val centerOffset = (availableWidth - (bottomRowWidth * 2)) / 2f - drawGauge(performanceInfoDetails.gas, canvas, bottomRowTop, areaLeft + centerOffset - 10f, bottomRowWidth, labelCenterYPadding) - drawGauge(performanceInfoDetails.vehicleSpeed, canvas, bottomRowTop, areaLeft + centerOffset + bottomRowWidth + 10f, bottomRowWidth, labelCenterYPadding) + drawGauge(gauges[2], canvas, bottomRowTop, areaLeft + centerOffset - 10f, bottomRowWidth, labelCenterYPadding) + drawGauge(gauges[3], canvas, bottomRowTop, areaLeft + centerOffset + bottomRowWidth + 10f, bottomRowWidth, labelCenterYPadding) } 3 -> { @@ -138,7 +154,14 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A } 1 -> { - drawGauge(gauges[0], canvas, rowTop, areaLeft + (availableWidth / 4f), availableWidth / 2f, labelCenterYPadding = 6f) + drawGauge( + gauges[0], + canvas, + rowTop, + areaLeft + (availableWidth / 4f), + availableWidth / 2f, + labelCenterYPadding = 6f + ) } } } From 7c6921317ef53fa6fa6cd0caa29a224b4e12a84b Mon Sep 17 00:00:00 2001 From: Tomek Zebrowski Date: Mon, 9 Feb 2026 15:45:58 +0100 Subject: [PATCH 4/4] feat: formatting cleanup --- .../src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt | 2 +- .../org/obd/graphs/renderer/performance/PerformanceDrawer.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt index e43db5c0..e1c08b0b 100644 --- a/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt +++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/gauge/GaugeDrawer.kt @@ -343,7 +343,7 @@ internal class GaugeDrawer( pid.units?.let { valuePaint.getTextBounds(it, 0, it.length, unitRect) - canvas.drawText(it, area.centerX() + textRect.width() / 2 + 12, unitY, valuePaint) + canvas.drawText(it, area.centerX() + textRect.width() / 2 + 10, unitY, valuePaint) centerY += unitRect.height() / 2 } diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt index e030b0a7..62e8f01a 100644 --- a/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt +++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/performance/PerformanceDrawer.kt @@ -185,7 +185,8 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A metric = metric, labelCenterYPadding = labelCenterYPadding, fontSize = settings.getPerformanceScreenSettings().fontSize, - scaleEnabled = false + scaleEnabled = false, + statsEnabled = metric.source.isNumber() ) true }