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