diff --git a/automotive/src/main/java/org/obd/graphs/aa/screen/CarScreen.kt b/automotive/src/main/java/org/obd/graphs/aa/screen/CarScreen.kt
index 4c292746..e6f540e8 100644
--- a/automotive/src/main/java/org/obd/graphs/aa/screen/CarScreen.kt
+++ b/automotive/src/main/java/org/obd/graphs/aa/screen/CarScreen.kt
@@ -29,10 +29,7 @@ import org.obd.graphs.AA_EDIT_PREF_SCREEN
import org.obd.graphs.RenderingThread
import org.obd.graphs.aa.*
import org.obd.graphs.aa.mapColor
-import org.obd.graphs.aa.screen.nav.CHANGE_SCREEN_EVENT
-import org.obd.graphs.aa.screen.nav.DRAG_RACING_SCREEN_ID
-import org.obd.graphs.aa.screen.nav.GIULIA_SCREEN_ID
-import org.obd.graphs.aa.screen.nav.ROUTINES_SCREEN_ID
+import org.obd.graphs.aa.screen.nav.*
import org.obd.graphs.aa.toast
import org.obd.graphs.bl.collector.MetricsCollector
import org.obd.graphs.bl.datalogger.WorkflowStatus
@@ -102,13 +99,21 @@ internal abstract class CarScreen(
}
dataLogger.start(query)
}
- DRAG_RACING_SCREEN_ID -> {
- query.setStrategy(QueryStrategyType.DRAG_RACING_QUERY)
- dataLogger.start(query)
- }
+
+ DRAG_RACING_SCREEN_ID ->
+ dataLogger.start(query.apply{
+ setStrategy(QueryStrategyType.DRAG_RACING_QUERY)
+ })
+
+ TRIP_INFO_SCREEN_ID ->
+ dataLogger.start(query.apply{
+ setStrategy(QueryStrategyType.TRIP_INFO_QUERY)
+ })
+
ROUTINES_SCREEN_ID -> {
- query.setStrategy(QueryStrategyType.ROUTINES_QUERY)
- dataLogger.start(query)
+ dataLogger.start(query.apply{
+ setStrategy(QueryStrategyType.ROUTINES_QUERY)
+ })
}
}
})
diff --git a/automotive/src/main/java/org/obd/graphs/aa/screen/nav/AvailableFeaturesScreen.kt b/automotive/src/main/java/org/obd/graphs/aa/screen/nav/AvailableFeaturesScreen.kt
index b215c885..ff1421c6 100644
--- a/automotive/src/main/java/org/obd/graphs/aa/screen/nav/AvailableFeaturesScreen.kt
+++ b/automotive/src/main/java/org/obd/graphs/aa/screen/nav/AvailableFeaturesScreen.kt
@@ -31,6 +31,7 @@ import org.obd.graphs.bl.datalogger.*
const val GIULIA_SCREEN_ID = 0
const val DRAG_RACING_SCREEN_ID = 1
const val ROUTINES_SCREEN_ID = 2
+const val TRIP_INFO_SCREEN_ID = 3
internal class AvailableFeaturesScreen(
carContext: CarContext,
@@ -59,6 +60,13 @@ internal class AvailableFeaturesScreen(
val items = ItemList.Builder().apply {
+ addItem(
+ row(
+ TRIP_INFO_SCREEN_ID, R.drawable.action_giulia,
+ carContext.getString(R.string.available_features_trip_info_screen_title)
+ )
+ )
+
addItem(
row(
DRAG_RACING_SCREEN_ID, R.drawable.action_drag_race_screen,
diff --git a/automotive/src/main/java/org/obd/graphs/aa/screen/nav/NavTemplateCarScreen.kt b/automotive/src/main/java/org/obd/graphs/aa/screen/nav/NavTemplateCarScreen.kt
index 684fba91..00eb14ad 100644
--- a/automotive/src/main/java/org/obd/graphs/aa/screen/nav/NavTemplateCarScreen.kt
+++ b/automotive/src/main/java/org/obd/graphs/aa/screen/nav/NavTemplateCarScreen.kt
@@ -72,7 +72,7 @@ internal class NavTemplateCarScreen(
Log.d(LOG_KEY, "Selected new screen id=$it")
it?.let {
val newScreen = it.toString().toInt()
- if (newScreen == GIULIA_SCREEN_ID || newScreen == DRAG_RACING_SCREEN_ID) {
+ if (newScreen == GIULIA_SCREEN_ID || newScreen == DRAG_RACING_SCREEN_ID || newScreen == TRIP_INFO_SCREEN_ID) {
surfaceScreen.toggleSurfaceRenderer(newScreen)
invalidate()
} else {
diff --git a/automotive/src/main/java/org/obd/graphs/aa/screen/nav/SurfaceController.kt b/automotive/src/main/java/org/obd/graphs/aa/screen/nav/SurfaceController.kt
index fb42eff4..a427a9ac 100644
--- a/automotive/src/main/java/org/obd/graphs/aa/screen/nav/SurfaceController.kt
+++ b/automotive/src/main/java/org/obd/graphs/aa/screen/nav/SurfaceController.kt
@@ -151,10 +151,20 @@ class SurfaceController(
}
DRAG_RACING_SCREEN_ID -> {
- query.setStrategy(QueryStrategyType.DRAG_RACING_QUERY)
+ dataLogger.updateQuery(query = query.apply {
+ setStrategy(QueryStrategyType.DRAG_RACING_QUERY)
+ })
+
dataLogger.updateQuery(query = query)
surfaceRenderer = SurfaceRenderer.allocate(carContext, settings, metricsCollector, fps, surfaceRendererType = SurfaceRendererType.DRAG_RACING)
}
+
+ TRIP_INFO_SCREEN_ID -> {
+ dataLogger.updateQuery(query = query.apply {
+ setStrategy(QueryStrategyType.TRIP_INFO_QUERY)
+ })
+ surfaceRenderer = SurfaceRenderer.allocate(carContext, settings, metricsCollector, fps, surfaceRendererType = SurfaceRendererType.TRIP_INFO)
+ }
}
surfaceRenderer.applyMetricsFilter(query)
diff --git a/automotive/src/main/res/values/strings.xml b/automotive/src/main/res/values/strings.xml
index d746f9e0..c3f5e15f 100644
--- a/automotive/src/main/res/values/strings.xml
+++ b/automotive/src/main/res/values/strings.xml
@@ -17,4 +17,6 @@
Routines
Drag Racing
Metrics
+ Trip info
+
\ No newline at end of file
diff --git a/datalogger/src/main/java/org/obd/graphs/bl/query/ObdMetricExt.kt b/datalogger/src/main/java/org/obd/graphs/bl/query/ObdMetricExt.kt
index 172e62a8..29da38b6 100644
--- a/datalogger/src/main/java/org/obd/graphs/bl/query/ObdMetricExt.kt
+++ b/datalogger/src/main/java/org/obd/graphs/bl/query/ObdMetricExt.kt
@@ -26,8 +26,33 @@ private const val VEHICLE_SPEED_PID_ID = 14L
private const val EXT_ENGINE_RPM_PID_ID = 7008L
private const val ENGINE_RPM_PID_ID = 13L
+private const val FUEL_CONSUMPTION_PID_ID = 7035L
+private const val FUEL_LEVEL_PID_ID = 7037L
+private const val GEARBOX_OIL_TEMP_PID_ID = 7025L
+private const val GEARBOX_ENGAGED_PID_ID = 7029L
+private const val OIL_TEMP_PID_ID = 7003L
+private const val COOLANT_TEMP_PID_ID = 7009L
+private const val EXHAUST_TEMP_PID_ID = 7016L
+private const val AIR_TEMP_PID_ID = 7002L
+
+
class PIDsNamesRegistry {
+ fun getFuelConsumptionPID(): Long = FUEL_CONSUMPTION_PID_ID
+ fun getFuelLevelPID(): Long = FUEL_LEVEL_PID_ID
+ fun getGearboxOilTempPID(): Long = GEARBOX_OIL_TEMP_PID_ID
+
+ fun getGearboxEngagedPID(): Long = GEARBOX_ENGAGED_PID_ID
+
+ fun getOilTempPID(): Long = OIL_TEMP_PID_ID
+
+ fun getCoolantTempPID(): Long = COOLANT_TEMP_PID_ID
+
+ fun getExhaustTempPID(): Long = EXHAUST_TEMP_PID_ID
+
+ fun getAirTempPID(): Long = AIR_TEMP_PID_ID
+
+
fun getAtmPressurePID(): Long = EXT_ATM_PRESSURE_PID_ID
fun getAmbientTempPID(): Long = EXT_AMBIENT_TEMP_PID_ID
fun getMeasuredIntakePressurePID (): Long = EXT_MEASURED_INTAKE_PRESSURE_PID_ID
@@ -38,5 +63,4 @@ class PIDsNamesRegistry {
fun getDynamicSelectorPID(): Long = EXT_DYNAMIC_SELECTOR_PID_ID
private fun isProfileExtensionsEnabled() = Prefs.getBoolean(PREF_PROFILE_2_0_GME_EXTENSION_ENABLED, false)
-
}
\ No newline at end of file
diff --git a/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyOrchestrator.kt b/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyOrchestrator.kt
index fe72f794..42bfceee 100644
--- a/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyOrchestrator.kt
+++ b/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyOrchestrator.kt
@@ -16,6 +16,7 @@ internal class QueryStrategyOrchestrator : java.io.Serializable, Query {
this[QueryStrategyType.DRAG_RACING_QUERY] = DragRacingQueryStrategy()
this[QueryStrategyType.INDIVIDUAL_QUERY_FOR_EACH_VIEW] = IndividualQueryStrategy()
this[QueryStrategyType.ROUTINES_QUERY] = RoutinesQueryStrategy()
+ this[QueryStrategyType.TRIP_INFO_QUERY] = TripInfoQueryStrategy()
}
}
diff --git a/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyType.kt b/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyType.kt
index da775ce0..c75525c3 100644
--- a/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyType.kt
+++ b/datalogger/src/main/java/org/obd/graphs/bl/query/QueryStrategyType.kt
@@ -20,6 +20,7 @@ package org.obd.graphs.bl.query
enum class QueryStrategyType {
ROUTINES_QUERY,
DRAG_RACING_QUERY,
+ TRIP_INFO_QUERY,
SHARED_QUERY,
INDIVIDUAL_QUERY_FOR_EACH_VIEW
}
\ No newline at end of file
diff --git a/datalogger/src/main/java/org/obd/graphs/bl/query/TripInfoQueryStrategy.kt b/datalogger/src/main/java/org/obd/graphs/bl/query/TripInfoQueryStrategy.kt
new file mode 100644
index 00000000..e3b4f1a7
--- /dev/null
+++ b/datalogger/src/main/java/org/obd/graphs/bl/query/TripInfoQueryStrategy.kt
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2019-2024, Tomasz Żebrowski
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+package org.obd.graphs.bl.query
+
+
+internal class TripInfoQueryStrategy : QueryStrategy() {
+ override fun getPIDs(): MutableSet =
+ mutableSetOf(
+
+ namesRegistry.getVehicleSpeedPID(),
+ namesRegistry.getFuelConsumptionPID(),
+ namesRegistry.getFuelLevelPID(),
+ namesRegistry.getAtmPressurePID(),
+ namesRegistry.getAmbientTempPID(),
+ namesRegistry.getGearboxEngagedPID(),
+ namesRegistry.getGearboxOilTempPID(),
+
+ namesRegistry.getOilTempPID(),
+ namesRegistry.getCoolantTempPID(),
+ namesRegistry.getExhaustTempPID(),
+ namesRegistry.getAirTempPID(),
+ )
+}
+
diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/SurfaceRenderer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/SurfaceRenderer.kt
index c50f13d3..a66b4863 100644
--- a/screen_renderer/src/main/java/org/obd/graphs/renderer/SurfaceRenderer.kt
+++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/SurfaceRenderer.kt
@@ -26,9 +26,10 @@ import org.obd.graphs.bl.query.Query
import org.obd.graphs.renderer.drag.DragRacingSurfaceRenderer
import org.obd.graphs.renderer.gauge.GaugeSurfaceRenderer
import org.obd.graphs.renderer.giulia.GiuliaSurfaceRenderer
+import org.obd.graphs.renderer.trip.TripSurfaceRenderer
enum class SurfaceRendererType {
- GIULIA, GAUGE, DRAG_RACING
+ GIULIA, GAUGE, DRAG_RACING, TRIP_INFO
}
data class ViewSettings(var marginTop: Int = 0)
@@ -37,6 +38,7 @@ interface SurfaceRenderer {
fun applyMetricsFilter(query: Query)
fun onDraw(canvas: Canvas, drawArea: Rect?)
fun recycle()
+
companion object {
fun allocate(
context: Context,
@@ -50,6 +52,7 @@ interface SurfaceRenderer {
SurfaceRendererType.GAUGE -> GaugeSurfaceRenderer(context, settings, metricsCollector, fps, viewSettings)
SurfaceRendererType.GIULIA -> GiuliaSurfaceRenderer(context, settings, metricsCollector, fps, viewSettings)
SurfaceRendererType.DRAG_RACING -> DragRacingSurfaceRenderer(context, settings, metricsCollector, fps, viewSettings)
+ SurfaceRendererType.TRIP_INFO -> TripSurfaceRenderer(context, settings, metricsCollector, fps, viewSettings)
}
}
}
\ No newline at end of file
diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/trip/TripDrawer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/trip/TripDrawer.kt
new file mode 100644
index 00000000..0bf99efe
--- /dev/null
+++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/trip/TripDrawer.kt
@@ -0,0 +1,357 @@
+/**
+ * Copyright 2019-2024, Tomasz Żebrowski
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+package org.obd.graphs.renderer.trip
+
+import android.content.Context
+import android.graphics.*
+import org.obd.graphs.bl.collector.Metric
+import org.obd.graphs.bl.drag.DragRacingEntry
+import org.obd.graphs.bl.drag.DragRacingResults
+import org.obd.graphs.bl.drag.VALUE_NOT_SET
+import org.obd.graphs.renderer.AbstractDrawer
+import org.obd.graphs.renderer.ScreenSettings
+import org.obd.graphs.round
+import org.obd.graphs.ui.common.COLOR_CARDINAL
+import org.obd.graphs.ui.common.COLOR_WHITE
+
+private const val FOOTER_SIZE_RATIO = 1.3f
+private const val CURRENT_MIN = 22f
+private const val CURRENT_MAX = 72f
+private const val NEW_MAX = 1.6f
+private const val NEW_MIN = 0.6f
+const val MARGIN_END = 30
+
+private const val SHIFT_LIGHTS_MAX_SEGMENTS = 14
+const val SHIFT_LIGHTS_WIDTH = 30
+
+@Suppress("NOTHING_TO_INLINE")
+internal class TripDrawer(context: Context, settings: ScreenSettings) : AbstractDrawer(context, settings) {
+
+ private val shiftLightPaint = Paint()
+ private var segmentCounter = SHIFT_LIGHTS_MAX_SEGMENTS
+
+ inline fun drawShiftLights(
+ canvas: Canvas,
+ area: Rect,
+ color: Int = settings.getColorTheme().progressColor,
+ shiftLightsWidth: Int = SHIFT_LIGHTS_WIDTH,
+ blinking: Boolean = false
+ ) {
+ val segmentHeight = area.height().toFloat() / SHIFT_LIGHTS_MAX_SEGMENTS
+ val leftMargin = 4f
+ val topMargin = 6f
+
+ shiftLightPaint.color = Color.WHITE
+ for (i in 1..SHIFT_LIGHTS_MAX_SEGMENTS) {
+
+ val top = area.top + (i * segmentHeight)
+ val bottom = top + segmentHeight - topMargin
+
+ canvas.drawRect(area.left - shiftLightsWidth + leftMargin, top, area.left.toFloat() + leftMargin,
+ bottom, shiftLightPaint)
+
+ val left = area.left + area.width().toFloat() - leftMargin
+ canvas.drawRect(
+ left, top, left + shiftLightsWidth,
+ bottom, shiftLightPaint
+ )
+ }
+ if (blinking) {
+ shiftLightPaint.color = color
+
+ for (i in SHIFT_LIGHTS_MAX_SEGMENTS downTo segmentCounter) {
+
+ val top = area.top + (i * segmentHeight)
+ val bottom = top + segmentHeight - topMargin
+
+ canvas.drawRect(area.left - shiftLightsWidth + leftMargin, top, area.left.toFloat() + leftMargin,
+ bottom, shiftLightPaint)
+
+ val left = area.left + area.width().toFloat() - leftMargin
+
+ canvas.drawRect(
+ left, top, left + shiftLightsWidth,
+ bottom, shiftLightPaint
+ )
+ }
+
+ segmentCounter--
+
+ if (segmentCounter == 0) {
+ segmentCounter = SHIFT_LIGHTS_MAX_SEGMENTS
+ }
+ }
+ }
+
+ inline fun drawDragRaceResults(
+ canvas: Canvas,
+ area: Rect,
+ left: Float,
+ top: Float,
+ dragRacingResults: DragRacingResults
+ ) {
+
+ val (_, textSizeBase) = calculateFontSize(area)
+
+ val currentXPos = area.centerX() / 1.5f
+ val lastXPos = area.centerX() + 60f
+ val bestXPos = area.centerX() * 1.60f
+
+ // legend
+ drawText(canvas, "Current", currentXPos, top, textSizeBase, color = Color.LTGRAY)
+ drawText(canvas, "Last", lastXPos, top, textSizeBase, color = Color.LTGRAY)
+ drawText(canvas, "Best", bestXPos, top, textSizeBase, color = Color.LTGRAY)
+
+ // 0-60
+ var rowTop = top + textSizeBase + 12f
+ drawDragRacingEntry(area, dragRacingResults._0_60, "0-60 km/h", rowTop, left,canvas, textSizeBase)
+
+ // 0 - 100
+ rowTop = top + (2 * textSizeBase) + 24f
+ drawDragRacingEntry(area, dragRacingResults._0_100, "0-100 km/h", rowTop, left, canvas, textSizeBase)
+
+ // 60 - 140
+ rowTop = top + (3 * textSizeBase) + 36f
+ drawDragRacingEntry(area,dragRacingResults._60_140, "60-140 km/h", rowTop, left,canvas, textSizeBase)
+
+ // 0 - 160
+ rowTop = top + (4 * textSizeBase) + 48f
+ drawDragRacingEntry(area, dragRacingResults._0_160, "0-160 km/h", rowTop, left, canvas, textSizeBase)
+
+ // 100 - 200
+ rowTop = top + (5 * textSizeBase) + 60f
+ drawDragRacingEntry(area, dragRacingResults._100_200, "100-200 km/h", rowTop, left, canvas, textSizeBase)
+ }
+
+ inline fun drawMetric(
+ canvas: Canvas,
+ area: Rect,
+ metric: Metric,
+ left: Float,
+ top: Float
+ ): Float {
+
+ val (valueTextSize, textSizeBase) = calculateFontSize(area)
+
+ var top1 = top
+ var left1 = left
+
+ if (settings.getDragRacingSettings().vehicleSpeedEnabled) {
+ drawText(
+ canvas,
+ metric.source.command.pid.description,
+ left1,
+ top1,
+ textSizeBase
+ )
+ }
+
+ if (settings.getDragRacingSettings().vehicleSpeedEnabled) {
+
+ drawValue(
+ canvas,
+ metric,
+ area,
+ top1 + 10,
+ valueTextSize
+ )
+
+ if (settings.getDragRacingSettings().vehicleSpeedFrequencyReadEnabled) {
+
+ val frequencyTextSize = textSizeBase / FOOTER_SIZE_RATIO / FOOTER_SIZE_RATIO
+ top1 += textSizeBase / FOOTER_SIZE_RATIO
+ left1 = drawText(
+ canvas,
+ "freq:",
+ left,
+ top1,
+ Color.DKGRAY,
+ frequencyTextSize
+ )
+ drawText(
+ canvas,
+ "${metric.rate?.round(2)} read/sec",
+ left1,
+ top1,
+ Color.WHITE,
+ frequencyTextSize
+ )
+ }
+
+ top1 += 12f
+
+ drawProgressBar(
+ canvas,
+ left,
+ getAreaWidth(area), top1, metric,
+ color = settings.getColorTheme().progressColor
+ )
+
+ top1 += calculateDividerSpacing()
+
+ drawDivider(
+ canvas,
+ left, getAreaWidth(area), top1,
+ color = settings.getColorTheme().dividerColor
+ )
+ }
+
+ top1 += 10f + (textSizeBase).toInt()
+ return top1
+ }
+
+
+ fun drawText(
+ canvas: Canvas,
+ text: String,
+ left: Float,
+ top: Float,
+ color: Int,
+ textSize: Float
+
+ ): Float = drawText(canvas, text, left, top, color, textSize, paint)
+
+ fun drawProgressBar(
+ canvas: Canvas,
+ left: Float,
+ width: Float,
+ top: Float,
+ it: Metric,
+ color: Int
+ ) {
+ paint.color = color
+
+ val progress = valueScaler.scaleToNewRange(
+ it.source.value?.toFloat() ?: it.source.command.pid.min.toFloat(),
+ it.source.command.pid.min.toFloat(), it.source.command.pid.max.toFloat(), left, left + width - MARGIN_END
+ )
+
+ canvas.drawRect(
+ left - 6,
+ top + 4,
+ progress,
+ top + calculateProgressBarHeight(),
+ paint
+ )
+ }
+
+
+ fun drawValue(
+ canvas: Canvas,
+ metric: Metric,
+ area: Rect,
+ top: Float,
+ textSize: Float
+ ) {
+
+ valuePaint.color = COLOR_WHITE
+ val x = area.right - 50f
+
+ valuePaint.setShadowLayer(80f, 0f, 0f, Color.WHITE)
+ valuePaint.textSize = textSize
+ valuePaint.textAlign = Paint.Align.RIGHT
+ val text = metric.source.valueToString()
+ canvas.drawText(text, x, top, valuePaint)
+
+ valuePaint.color = Color.LTGRAY
+ valuePaint.textAlign = Paint.Align.LEFT
+ valuePaint.textSize = (textSize * 0.4).toFloat()
+ canvas.drawText(metric.source.command.pid.units, (x + 2), top, valuePaint)
+ }
+
+ fun drawText(
+ canvas: Canvas,
+ text: String,
+ left: Float,
+ top: Float,
+ textSize: Float,
+ typeface: Typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL),
+ color: Int = Color.WHITE
+ ) {
+ titlePaint.textSize = textSize
+ titlePaint.typeface = typeface
+ titlePaint.color = color
+ canvas.drawText(
+ text.replace("\n", " "),
+ left,
+ top,
+ titlePaint
+ )
+ }
+
+ private fun calculateProgressBarHeight() = 16
+
+ private inline fun getAreaWidth(area: Rect): Float = area.width().toFloat()
+
+ private inline fun calculateDividerSpacing(): Int = 14
+
+ private inline fun calculateFontSize(
+ area: Rect
+ ): Pair {
+
+ val scaleRatio = valueScaler.scaleToNewRange(settings.getDragRacingSettings().fontSize.toFloat(),
+ CURRENT_MIN, CURRENT_MAX, NEW_MIN, NEW_MAX)
+
+ val areaWidth = area.width()
+ val valueTextSize = (areaWidth / 18f) * scaleRatio
+ val textSizeBase = (areaWidth / 21f) * scaleRatio
+ return Pair(valueTextSize, textSizeBase)
+ }
+
+
+ private inline fun drawDragRacingEntry(area: Rect,
+ dragRacingEntry: DragRacingEntry,
+ label: String,
+ top: Float,
+ left:Float,
+ canvas: Canvas,
+ textSizeBase: Float) {
+
+
+ val currentXPos = area.centerX() / 1.5f
+ val lastXPos = area.centerX() + 60f
+ val bestXPos = area.centerX() * 1.60f
+
+ drawText(canvas, label, left, top, textSizeBase, color = Color.LTGRAY)
+ drawText(canvas, timeToString(dragRacingEntry.current), currentXPos, top, textSizeBase)
+
+ if (settings.getDragRacingSettings().vehicleSpeedDisplayDebugEnabled) {
+ val width = getTextWidth(timeToString(dragRacingEntry.current), titlePaint) * 1.25f
+ drawText(canvas, speedToString(dragRacingEntry.currentSpeed), currentXPos + width, top, textSizeBase / 1.5f)
+ }
+
+ drawText(canvas, timeToString(dragRacingEntry.last), lastXPos, top, textSizeBase)
+
+ drawText(canvas, timeToString(dragRacingEntry.best), bestXPos, top, textSizeBase, color = COLOR_CARDINAL)
+
+ if (dragRacingEntry.best != VALUE_NOT_SET){
+ val width = getTextWidth(timeToString(dragRacingEntry.best), titlePaint) * 1.15f
+ val height = getTextHeight(timeToString(dragRacingEntry.best), titlePaint) / 2
+ if (dragRacingEntry.bestAmbientTemp != VALUE_NOT_SET.toInt()){
+ drawText(canvas, "${dragRacingEntry.bestAmbientTemp}C", bestXPos + width, top - height, textSizeBase * 0.5f, color = Color.LTGRAY)
+ }
+ if (dragRacingEntry.bestAtmPressure != VALUE_NOT_SET.toInt()){
+ drawText(canvas, "${dragRacingEntry.bestAtmPressure}hpa", bestXPos + width, top + height/2, textSizeBase * 0.5f, color = Color.LTGRAY)
+ }
+ }
+ }
+
+ private inline fun timeToString(value: Long): String = if (value == VALUE_NOT_SET) "---" else (value / 1000.0).round(2).toString()
+ private inline fun speedToString(value: Int): String = if (value == VALUE_NOT_SET.toInt()) "" else "$value km/h"
+}
\ No newline at end of file
diff --git a/screen_renderer/src/main/java/org/obd/graphs/renderer/trip/TripSurfaceRenderer.kt b/screen_renderer/src/main/java/org/obd/graphs/renderer/trip/TripSurfaceRenderer.kt
new file mode 100644
index 00000000..7e212c97
--- /dev/null
+++ b/screen_renderer/src/main/java/org/obd/graphs/renderer/trip/TripSurfaceRenderer.kt
@@ -0,0 +1,125 @@
+/**
+ * Copyright 2019-2024, Tomasz Żebrowski
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+package org.obd.graphs.renderer.trip
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Rect
+import org.obd.graphs.bl.collector.MetricsCollector
+import org.obd.graphs.bl.drag.DragRacingResults
+import org.obd.graphs.bl.drag.dragRacingResultRegistry
+import org.obd.graphs.bl.query.Query
+import org.obd.graphs.bl.query.QueryStrategyType
+import org.obd.graphs.bl.query.isVehicleSpeed
+import org.obd.graphs.renderer.*
+
+
+@Suppress("NOTHING_TO_INLINE")
+internal class TripSurfaceRenderer(
+ context: Context,
+ settings: ScreenSettings,
+ metricsCollector: MetricsCollector,
+ fps: Fps,
+ viewSettings: ViewSettings
+) : AbstractSurfaceRenderer(settings, context, fps, metricsCollector, viewSettings) {
+
+ private val tripDrawer = TripDrawer(context, settings)
+ override fun applyMetricsFilter(query: Query) {
+ metricsCollector.applyFilter(
+ enabled = query.getIDs()
+ )
+ }
+
+ override fun onDraw(canvas: Canvas, drawArea: Rect?) {
+
+ drawArea?.let { it ->
+
+ val dragRaceResults = dragRacingResultRegistry.getResult()
+ tripDrawer.drawBackground(canvas, it)
+
+ val margin = if (settings.getDragRacingSettings().shiftLightsEnabled || dragRaceResults.readyToRace) SHIFT_LIGHTS_WIDTH else 0
+ val area = getArea(it, canvas, margin)
+ var top = getDrawTop(area)
+ var left = tripDrawer.getMarginLeft(area.left.toFloat())
+
+ if (settings.getDragRacingSettings().shiftLightsEnabled) {
+ dragRacingResultRegistry.setShiftLightsRevThreshold(settings.getDragRacingSettings().shiftLightsRevThreshold)
+ // permanent white boxes
+ tripDrawer.drawShiftLights(canvas, area, blinking = false)
+ }
+
+ if (isShiftLight(dragRaceResults)) {
+ tripDrawer.drawShiftLights(canvas, area, blinking = true)
+ }
+
+ if (dragRaceResults.readyToRace){
+ tripDrawer.drawShiftLights(canvas, area, color = Color.GREEN, blinking = true)
+ }
+
+ left += 5
+
+ if (settings.isStatusPanelEnabled()) {
+ tripDrawer.drawStatusPanel(canvas, top, left, fps, metricsCollector)
+ top += 4
+ tripDrawer.drawDivider(canvas, left, area.width().toFloat(), top, Color.DKGRAY)
+ top += 40
+ }
+
+ metricsCollector.getMetrics().firstOrNull { it.source.isVehicleSpeed() }?.let {
+ top = tripDrawer.drawMetric(
+ canvas = canvas,
+ area = area,
+ metric = it,
+ left = left,
+ top = top
+ )
+ }
+
+ tripDrawer.drawDragRaceResults(
+ canvas = canvas,
+ area = area,
+ left = left,
+ top = top,
+ dragRacingResults = dragRaceResults)
+ }
+ }
+
+ private fun getArea(area: Rect, canvas: Canvas, margin: Int) : Rect {
+ val newArea = Rect()
+ if (area.isEmpty) {
+ newArea[0 + margin, viewSettings.marginTop, canvas.width - 1 - margin] = canvas.height - 1
+ } else {
+ val width = canvas.width - 1 - (margin)
+ newArea[area.left + margin, area.top + viewSettings.marginTop, width] = canvas.height
+ }
+ return newArea
+ }
+
+ private fun isShiftLight(dragRaceResults: DragRacingResults) =
+ settings.getDragRacingSettings().shiftLightsEnabled && dragRaceResults.enableShiftLights
+
+ override fun recycle() {
+ tripDrawer.recycle()
+ }
+
+ init {
+ applyMetricsFilter(Query.instance(QueryStrategyType.DRAG_RACING_QUERY))
+ }
+}
\ No newline at end of file