package com.evsuite.abrp; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** Lightweight SOC/power charging curve, avoiding a heavy chart dependency on AAOS 9. */ public final class ChargingGraphView extends View { private final Paint grid = new Paint(1), line = new Paint(1), text = new Paint(1); private final List points = new ArrayList<>(); private final float[] socPower = new float[101]; public ChargingGraphView(Context c, AttributeSet a) { super(c,a); java.util.Arrays.fill(socPower, Float.NaN); grid.setColor(AccentColor.withAlpha(AccentColor.resolve(c, R.attr.evAccentLight), 0x55)); line.setColor(0xFFFFC857); line.setStrokeWidth(4); text.setColor(0xFFBDC4C6); text.setTextSize(20); } synchronized void add(Float soc, Float power) { if (soc == null || power == null || power >= 0) return; float p = Math.abs(power); if (!points.isEmpty() && Math.abs(points.get(points.size()-1)[0] - soc) < .1f) points.remove(points.size()-1); points.add(new float[]{soc,p}); if (points.size() > 500) points.remove(0); socPower[Math.max(0, Math.min(100, Math.round(soc)))] = p; invalidate(); } synchronized void setPoints(List values) { points.clear(); java.util.Arrays.fill(socPower, Float.NaN); for (ChargeSessionTracker.Point value : values) { if (value.socPercent == null) continue; points.add(new float[]{value.socPercent, value.powerKw}); socPower[Math.max(0, Math.min(100, Math.round(value.socPercent)))] = value.powerKw; } invalidate(); } synchronized String tableText() { StringBuilder out = new StringBuilder("SOC Charge power\n"); boolean found = false; for (int soc=0; soc<=100; soc++) if (!Float.isNaN(socPower[soc])) { found = true; out.append(String.format(Locale.getDefault(), "%3d %% %6.1f kW\n", soc, socPower[soc])); } return found ? out.toString().trim() : "No charging measurements yet"; } /** * Axes with their labels clear of each other: "kW" in a row of its own above the plot, the * power values right-aligned against the y axis, and the SOC ticks as "0 %" … "100 %", * centred under their lines, the last one kept inside the view. */ @Override protected synchronized void onDraw(Canvas c) { super.onDraw(c); float left = 58, right = getWidth() - 12, top = 36, bottom = getHeight() - 38; float max = 20; for (float[] p : points) max = Math.max(max, p[1]); max = (float) Math.ceil(max / 20f) * 20f; text.setTextAlign(Paint.Align.RIGHT); for (int i = 0; i <= 5; i++) { float y = bottom - (bottom - top) * i / 5f; c.drawLine(left, y, right, y, grid); c.drawText(String.format(Locale.US, "%.0f", max * i / 5f), left - 8, y + 7, text); } text.setTextAlign(Paint.Align.LEFT); c.drawText("kW", 4, 20, text); for (int soc = 0; soc <= 100; soc += 20) { float x = left + (right - left) * soc / 100f; c.drawLine(x, top, x, bottom, grid); String label = soc + " %"; float half = text.measureText(label) / 2f; float cx = Math.min(x, getWidth() - 2 - half); text.setTextAlign(Paint.Align.CENTER); c.drawText(label, cx, getHeight() - 8, text); } text.setTextAlign(Paint.Align.LEFT); for (int i = 1; i < points.size(); i++) { float[] a = points.get(i - 1), b = points.get(i); float ax = left + a[0] * (right - left) / 100, ay = bottom - a[1] * (bottom - top) / max; float bx = left + b[0] * (right - left) / 100, by = bottom - b[1] * (bottom - top) / max; c.drawLine(ax, ay, bx, by, line); c.drawCircle(bx, by, 4, line); } } }