Files
mg4/app/src/main/java/com/evsuite/abrp/TerminalView.java
T
nilsandClaude Opus 5.5 1c85fd2d9f App 2.27.2: Terminal in the accent colour; Original as a colour choice
Retro and Terminal are one entry each; the accent colour drop-down
offers "Original" first for them, remembered per style. The old
"Retro (accent colour)" entry is folded in and its stored value
migrated. Emulator only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-24 21:38:01 +02:00

135 lines
5.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.evsuite.abrp;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.SystemClock;
import java.util.Locale;
/**
* The Terminal Overview style: text only, green phosphor on black, faint scan lines, as if a
* shell printed the car's state — each value on a row with an ASCII bar, the gear as [D], and
* a prompt with a blinking cursor (it blinks because readings redraw the view; it does not
* animate on its own). A low battery turns its row amber. Unread values show "—" and an empty
* bar.
*
* Two palettes: green phosphor, or the chosen accent colour ({@link #setAccentPalette}) — the
* text in its light shade, the command line in the accent itself, on plain black. The low
* battery stays amber in both.
*/
final class TerminalView extends ClusterCanvasView {
private static final int BAR = 24;
private final Paint text = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint scan = new Paint();
private final int accentText, accentDim;
private final int amber = 0xFFFFB000;
private int green, dim, background;
TerminalView(Context context) {
super(context);
accentText = AccentColor.resolve(context, R.attr.evAccentLight);
accentDim = AccentColor.resolve(context, R.attr.evAccent);
setAccentPalette(false);
text.setTypeface(Typeface.MONOSPACE);
scan.setColor(0x0CFFFFFF);
}
/** The chosen accent colour instead of green phosphor. */
void setAccentPalette(boolean accent) {
green = accent ? accentText : 0xFF33FF66;
dim = accent ? accentDim : 0xFF1C8A3A;
background = accent ? 0xFF050505 : 0xFF020A04;
invalidate();
}
@Override
protected void onDraw(Canvas c) {
super.onDraw(c);
float w = getWidth(), h = getHeight();
if (w <= 0 || h <= 0) return;
c.drawColor(background);
// About 14 rows at 1.32 em each plus margins; monospace is about 0.6 em wide and the
// longest row about 58 characters.
float x = 16f * dp;
float size = Math.min(h / 19.5f, (w - x) / 38f);
text.setTextSize(size);
float line = size * 1.32f, y = 12f * dp + line;
String gear = gearRow();
print(c, "nils@mg4:~$ watch -n 0.2 vehicle-status", x, y, dim);
y += line * 1.4f;
print(c, row("SPEED", fmt(v.speedKmh, "%.0f km/h", "—")) + bar(v.speedKmh, 0f, 180f), x, y, green);
y += line;
print(c, row("POWER", fmt(v.powerKw, "%.1f kW", "—")) + powerBar(), x, y, green);
y += line;
boolean low = v.socPercent != null && v.socPercent < 20f;
print(c, row("BATTERY", fmt(v.socPercent, "%.0f %%", "—")) + bar(v.socPercent, 0f, 100f)
+ (low ? " ! LOW" : ""), x, y, low ? amber : green);
y += line;
print(c, row("RANGE", fmt(v.rangeKm, "%.0f km", "—")), x, y, green);
y += line;
print(c, row("GEAR", gear), x, y, green);
y += line * 1.4f;
print(c, row("USAGE", fmt(v.usage5Min, "%.1f kWh/100km", "—")) + " # last 5 min", x, y, green);
y += line;
print(c, row("SINCE", fmt(v.usageTrip, "%.1f kWh/100km", "—")) + " # trip "
+ fmt(v.tripKm, "%.1f km", "—"), x, y, green);
y += line;
print(c, row("CLIMATE", fmt(v.climateCelsius, "%.0f C", "—")) + " ODO " + fmt(v.odometerKm, "%06.0f km", "—"), x, y, green);
y += line;
print(c, row("TYRES", "FL " + v.tyreFl + " FR " + v.tyreFr + " RL " + v.tyreRl + " RR " + v.tyreRr), x, y, green);
y += line * 1.4f;
boolean cursor = (SystemClock.uptimeMillis() / 500) % 2 == 0;
print(c, "[" + (v.clock.isEmpty() ? "--:--" : v.clock) + "] nils@mg4:~$ " + (cursor ? "█" : ""), x, y, green);
// Scan lines over everything.
for (float sy = 0; sy < h; sy += 3f * dp) c.drawLine(0, sy, w, sy, scan);
}
private void print(Canvas c, String s, float x, float y, int colour) {
text.setColor(colour);
c.drawText(s, x, y, text);
}
private static String row(String label, String value) {
return String.format(Locale.US, "%-9s%-16s", label, value);
}
/** [#####.......] from min to max; empty when unread. */
private static String bar(Float value, float min, float max) {
int n = value == null ? 0 : Math.round(BAR * (Math.max(min, Math.min(max, value)) - min) / (max - min));
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < BAR; i++) sb.append(i < n ? '#' : '.');
return sb.append(']').toString();
}
/** −50…150 kW with a | at zero: < for regeneration to its left, # for drawing to its right. */
private String powerBar() {
int zero = BAR / 4;
int to = v.powerKw == null ? zero : zero + Math.round(BAR * Math.max(-50f, Math.min(150f, v.powerKw)) / 200f);
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < BAR; i++) {
if (i == zero) sb.append('|');
else if (v.powerKw != null && i < zero && i >= to) sb.append('<');
else if (v.powerKw != null && i > zero && i <= to) sb.append('#');
else sb.append('.');
}
return sb.append(']').toString();
}
private String gearRow() {
String[] letters = { "P", "R", "N", "D" };
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 4; i++) {
boolean on = v.gear != null && v.gear == i + 1;
sb.append(on ? "[" + letters[i] + "]" : " " + letters[i] + " ");
}
return sb.toString();
}
}