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" /> + + + , +) + @Suppress("NOTHING_TO_INLINE") internal class GaugeDrawer( settings: ScreenSettings, @@ -88,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 } @@ -98,6 +112,8 @@ internal class GaugeDrawer( strokeCap = Paint.Cap.BUTT } + private val scaleNumbersCache = mutableMapOf() + fun drawGauge( canvas: Canvas, metric: Metric, @@ -214,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, @@ -224,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 @@ -274,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 } @@ -303,7 +328,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 @@ -319,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 + 10, unitY, valuePaint) centerY += unitRect.height() / 2 } @@ -349,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) } @@ -470,38 +497,79 @@ internal class GaugeDrawer( ) { 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) { + cachedEntry!!.numbers + } else { + 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..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 @@ -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 @@ -26,16 +25,18 @@ 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 - @Suppress("NOTHING_TO_INLINE") + +@Suppress("NOTHING_TO_INLINE") internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : AbstractDrawer(context, settings) { private val gaugeDrawer = GaugeDrawer( settings = settings, context = context, drawerSettings = DrawerSettings( - gaugeProgressBarType = GaugeProgressBarType.LONG) + gaugeProgressBarType = GaugeProgressBarType.LONG + ) ) private val tripInfoDrawer = TripInfoDrawer(context, settings) @@ -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() { - super.getBackground().recycle() this.background.recycle() } @@ -57,121 +63,109 @@ internal class PerformanceDrawer(context: Context, settings: ScreenSettings) : A top: Float, performanceInfoDetails: PerformanceInfoDetails ) { + val textSize = calculateFontSize( + multiplier = area.width() / 17f, + fontSize = settings.getPerformanceScreenSettings().fontSize + ) - val textSize = calculateFontSize(multiplier = area.width() / 17f, - fontSize = settings.getPerformanceScreenSettings().fontSize) - - val x = maxItemWidth(area) + 4 - - var rowTop = top + 12f - var leftAlignment = 0 - - performanceInfoDetails.postICAirTemp?.let { tripInfoDrawer - .drawMetric(it, top = rowTop, left = left + leftAlignment++, canvas, textSize, statsEnabled = true, area=area, castToInt = true) } - performanceInfoDetails.coolantTemp?.let { tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, statsEnabled = true,area=area, castToInt = true) } - performanceInfoDetails.oilTemp?.let{ tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, statsEnabled = true,area=area, castToInt = true) } - performanceInfoDetails.exhaustTemp?.let { tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, statsEnabled = true, area=area, castToInt = true) } - performanceInfoDetails.gearboxOilTemp?.let { tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, statsEnabled = true, area=area, castToInt = true) } - performanceInfoDetails.ambientTemp?.let{ tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, area=area) } - - - if (leftAlignment < MAX_ITEMS_IN_ROW){ - performanceInfoDetails.preICAirTemp?.let{ tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, - statsEnabled = true, area=area) } - } + val itemWidth = area.width() / MAX_ITEMS_IN_ROW.toFloat() + var rowTop = top + 2f + + val metricsToDraw = listOfNotNull( + performanceInfoDetails.postICAirTemp, + performanceInfoDetails.coolantTemp, + performanceInfoDetails.oilTemp, + performanceInfoDetails.exhaustTemp, + performanceInfoDetails.gearboxOilTemp, + performanceInfoDetails.ambientTemp, + performanceInfoDetails.preICAirTemp, + performanceInfoDetails.wcacTemp, + performanceInfoDetails.gearEngaged + ) - if (leftAlignment < MAX_ITEMS_IN_ROW){ - performanceInfoDetails.wcacTemp?.let{ tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, - statsEnabled = true, area=area) } - } + metricsToDraw.take(MAX_ITEMS_IN_ROW).forEachIndexed { index, metric -> + val itemLeft = left + (index * itemWidth) + + tripInfoDrawer.drawMetric( + metric, + rowTop, + itemLeft, + canvas, + textSize, + statsEnabled = metric.source.isNumber(), + area = area, + castToInt = true + ) - if (leftAlignment < MAX_ITEMS_IN_ROW){ - performanceInfoDetails.gearEngaged?.let{ tripInfoDrawer.drawMetric(it, rowTop, left + (leftAlignment++) * x, canvas, textSize, - statsEnabled = true, area=area) } + 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) 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(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(gauges[2], canvas, bottomRowTop, areaLeft + centerOffset - 10f, bottomRowWidth, labelCenterYPadding) + drawGauge(gauges[3], 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, @@ -179,22 +173,21 @@ 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 + scaleEnabled = false, + statsEnabled = metric.source.isNumber() ) true } - - private inline fun maxItemWidth(area: Rect) = (area.width() / 6) }