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
This commit is contained in:
2026-10-24 21:38:01 +02:00
co-authored by Claude Opus 5.5
parent ef2ba89fba
commit 1c85fd2d9f
10 changed files with 137 additions and 51 deletions
+2 -2
View File
@@ -69,11 +69,11 @@ Repository: `ssh://git@git.stinnesbeck.com:2222/nils/mg4.git` (remote `origin`),
| Tab | Contents |
|---|---|
| Overview | value tiles (battery, range, range/usage/energy since start, 5-min averages of range, usage, power, speed), vehicle info (odometer, lease allowance and the odometer against it), tyre pressures; read every 0.2 s on a background thread. Styles (Settings → Overview style): Tiles, Gauges (`GaugeView` needles), Cockpit (`RingView` rings, `TraceView` power trace), Retro (`RetroView`, seven-segment VFD, classic or in the accent colour), Futuristic (`FlowView`, animated energy flow), Sci-fi (`SciFiView`, starship console), Cartoon (`CartoonView`), Blueprint (`BlueprintView`, top-view drawing with tyre pressures), MG (`OctagonView`, octagonal dials after the classic MG dashboards), and on `ClusterCanvasView`: Minimal, Terminal, Aviation, Nixie tubes, Chronograph; the drawn styles share `ClusterValues`. A button in the top-right corner hides the app's navigation for a full-width Overview (`PREF_OVERVIEW_FULLSCREEN`) |
| Overview | value tiles (battery, range, range/usage/energy since start, 5-min averages of range, usage, power, speed), vehicle info (odometer, lease allowance and the odometer against it), tyre pressures; read every 0.2 s on a background thread. Styles (Settings → Overview style): Tiles, Gauges (`GaugeView` needles), Cockpit (`RingView` rings, `TraceView` power trace), Retro (`RetroView`, seven-segment VFD), Futuristic (`FlowView`, animated energy flow), Sci-fi (`SciFiView`, starship console), Cartoon (`CartoonView`), Blueprint (`BlueprintView`, top-view drawing with tyre pressures), MG (`OctagonView`, octagonal dials after the classic MG dashboards), and on `ClusterCanvasView`: Minimal, Terminal, Aviation, Nixie tubes, Chronograph; the drawn styles share `ClusterValues`. A button in the top-right corner hides the app's navigation for a full-width Overview (`PREF_OVERVIEW_FULLSCREEN`) |
| Consumption / Charging | period totals, daily log, charging sessions and curve |
| Live data | on/off, ABRP API key and user token, upload frequency |
| Server | URL, pairing by QR, ingest token, installation ID (read-only), config import |
| Settings | full screen, voice key, Overview style with the accent colour beside it (offered only for styles drawn in it, `OverviewStyle.usesAccent`), tyre unit, battery, charging price, version, update check, install from USB, About |
| Settings | full screen, voice key, Overview style with the accent colour beside it (offered only for styles drawn in it, `OverviewStyle.usesAccent`; Retro and Terminal add "Original" for their own palette, `hasOriginal`), tyre unit, battery, charging price, version, update check, install from USB, About |
| Logs | update log (every install step, with the platform's words), upload log |
Key classes (`app/src/main/java/com/evsuite/abrp/`): `AbrpUploadService` (foreground service,
+1 -1
View File
@@ -79,7 +79,7 @@ with the server using a token of its own, obtained by pairing with a phone (belo
| Tab | What it shows |
|---|---|
| **Overview** | Battery, range, power and speed; range, usage and energy since start; 5-minute averages of range, usage, power and speed; vehicle info (odometer, climate setpoint) and tyre pressures. Read five times a second. Looks to choose from in Settings → Overview style: Tiles, Gauges, Cockpit, Retro (classic turquoise or in your accent colour), Futuristic, Sci-fi, Cartoon, Blueprint, MG, Minimal, Terminal, Aviation, Nixie tubes and Chronograph. The small button in the top-right corner hides the app's navigation for a full-width Overview; tap it again to bring it back |
| **Overview** | Battery, range, power and speed; range, usage and energy since start; 5-minute averages of range, usage, power and speed; vehicle info (odometer, climate setpoint) and tyre pressures. Read five times a second. Looks to choose from in Settings → Overview style: Tiles, Gauges, Cockpit, Retro, Futuristic, Sci-fi, Cartoon, Blueprint, MG, Minimal, Terminal, Aviation, Nixie tubes and Chronograph. The small button in the top-right corner hides the app's navigation for a full-width Overview; tap it again to bring it back |
| **Consumption** | Lifetime, last 7 and 30 days, Trip A and B (distance, energy, average consumption and speed, SOC change), and a daily log of the last four months |
| **Charging** | The current session (SOC, voltage, current, power, energy into the battery and from the grid, time, cost) and its SOC/power curve |
| **Live data** | Live data on/off, ABRP credentials for sending directly from the car, upload frequency |
+11
View File
@@ -131,6 +131,17 @@ tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does *
confirmed car facts, app structure, UI conventions, build/test/ship, documents. It replaces
upstream's EVABRPUploader text. The test emulator is now in the flake: `nix run .#car-emulator`
(Android 9 AOSP at 1920×720, 160 dpi; the car build installs as the system user — checked).
- 2026-10-24 — App 2.27.2 (branch `styles-retro-future`): Terminal can be drawn in the accent
colour too (text in its light shade, the command line in the accent, plain black; the low
battery stays amber). Instead of separate "(accent colour)" entries, Retro and Terminal are one
entry each and the accent colour drop-down beside the style offers "Original" first (their own
palette, the default) and then the accent colours; the choice is kept per style
(`original_colours_<STYLE>`). Picking "Original" leaves the app's accent as it is. The
"Retro (accent colour)" entry is gone: a stored `RETRO_ACCENT` becomes Retro in the accent
colour (`OverviewStyle.hasOriginal`, `wasAccentEntry`; unit-tested). Fourteen entries in the
style list. Checked on the emulator: Terminal original → Dynamic Red, Retro still original,
and a stored `RETRO_ACCENT` migrated to Retro in Dynamic Red
(`screenshots/nilsmg4-2.27.2-terminal-accent.png`); no crash. Not tried on the car.
- 2026-10-24 — App 2.27.1 (branch `styles-retro-future`): Settings → Display rearranged:
Overview style first, the accent colour beside it, tyre pressure on its own row below. The
accent colour is only offered for styles drawn in it (Tiles, Gauges, Cockpit, Retro (accent
+2 -2
View File
@@ -34,8 +34,8 @@ android {
applicationId "com.stinnesbeck.nilsmg4"
minSdk 27
targetSdk 36
versionCode 122
versionName "2.27.1"
versionCode 123
versionName "2.27.2"
// Nothing about the server ships in the APK: its URL and ingest token are entered in
// the app's Server tab (ServerConfig), and the database is only reachable by the server.
@@ -68,6 +68,7 @@ public class MainActivity extends AppCompatActivity {
private OctagonView octagonView;
/** The styles built on {@link ClusterCanvasView}, fed together. */
private final java.util.List<ClusterCanvasView> canvasStyles = new java.util.ArrayList<>();
private TerminalView terminalView;
/** The pane of each Overview style, by its {@link OverviewStyle} ordinal. */
private View[] stylePanes;
private View consumptionPane;
@@ -282,11 +283,12 @@ public class MainActivity extends AppCompatActivity {
octagonView = new OctagonView(this, null);
View sciFiPane = drawnPane(sciFiView), cartoonPane = drawnPane(cartoonView),
blueprintPane = drawnPane(blueprintView), mgPane = drawnPane(octagonView);
java.util.Collections.addAll(canvasStyles, new MinimalView(this), new TerminalView(this),
terminalView = new TerminalView(this);
java.util.Collections.addAll(canvasStyles, new MinimalView(this), terminalView,
new AviationView(this), new NixieView(this), new ChronographView(this));
View[] canvasPanes = new View[canvasStyles.size()];
for (int i = 0; i < canvasPanes.length; i++) canvasPanes[i] = drawnPane(canvasStyles.get(i));
stylePanes = new View[] { vehiclePane, gaugesPane, cockpitPane, retroPane, retroPane,
stylePanes = new View[] { vehiclePane, gaugesPane, cockpitPane, retroPane,
futuristicPane, sciFiPane, cartoonPane, blueprintPane, mgPane,
canvasPanes[0], canvasPanes[1], canvasPanes[2], canvasPanes[3], canvasPanes[4] };
// The Overview page holds every style; Settings → Overview style picks the one shown.
@@ -1216,14 +1218,32 @@ public class MainActivity extends AppCompatActivity {
}
/** The accent colour; a change recreates the activity so every view takes it. */
/**
* The accent colour; a change recreates the activity so every view takes it. For a style
* with an original palette, "Original colours" comes first: it keeps the style in its own
* colours and leaves the app's accent as it is.
*/
private void bindAccentColor() {
OverviewStyle style = overviewStyle();
AccentColor[] colors = AccentColor.values();
String[] labels = new String[colors.length];
for (int i = 0; i < colors.length; i++) labels[i] = colors[i].label;
int offset = style.hasOriginal ? 1 : 0;
String[] labels = new String[colors.length + offset];
if (offset == 1) labels[0] = getString(R.string.accent_original);
for (int i = 0; i < colors.length; i++) labels[i + offset] = colors[i].label;
AccentColor current = AccentColor.fromPref(prefs.getString(AccentColor.PREF_KEY, null));
bindChoices(settingsPane.findViewById(R.id.accent_choices), labels, current.ordinal(), position -> {
if (colors[position] == current) return;
prefs.edit().putString(AccentColor.PREF_KEY, colors[position].name()).apply();
int selected = originalColours(style) ? 0 : current.ordinal() + offset;
bindChoices(settingsPane.findViewById(R.id.accent_choices), labels, selected, position -> {
if (style.hasOriginal) prefs.edit().putBoolean(style.originalKey(), position == 0).apply();
if (position < offset) {
applyOverviewStyle();
return;
}
AccentColor picked = colors[position - offset];
if (picked == current) {
applyOverviewStyle();
return;
}
prefs.edit().putString(AccentColor.PREF_KEY, picked.name()).apply();
recreate();
});
applyAccentAvailability();
@@ -1488,15 +1508,28 @@ public class MainActivity extends AppCompatActivity {
private Double dashTripKm;
private OverviewStyle overviewStyle() {
return OverviewStyle.fromPref(prefs.getString(PREF_OVERVIEW_STYLE, null));
String stored = prefs.getString(PREF_OVERVIEW_STYLE, null);
OverviewStyle style = OverviewStyle.fromPref(stored);
if (OverviewStyle.wasAccentEntry(stored)) {
// "Retro (accent colour)" before 2.27.2: now Retro, in the accent colour.
prefs.edit().putString(PREF_OVERVIEW_STYLE, style.name()).putBoolean(style.originalKey(), false).apply();
}
return style;
}
/** Whether a style with an original palette shows it (the default) or the accent colour. */
private boolean originalColours(OverviewStyle style) {
return style.hasOriginal && prefs.getBoolean(style.originalKey(), true);
}
private void applyOverviewStyle() {
OverviewStyle style = overviewStyle();
for (View pane : stylePanes) pane.setVisibility(pane == stylePanes[style.ordinal()] ? View.VISIBLE : View.GONE);
// Retro in either palette: one pane, the view told which.
retroView.setAccentPalette(style == OverviewStyle.RETRO_ACCENT);
applyAccentAvailability();
// Retro and Terminal: their original palette or the accent colour, as chosen for each.
retroView.setAccentPalette(!originalColours(OverviewStyle.RETRO));
terminalView.setAccentPalette(!originalColours(OverviewStyle.TERMINAL));
// The accent choices depend on the style ("Original colours" for Retro and Terminal).
if (settingsPane != null) bindAccentColor();
}
/**
@@ -1520,7 +1553,7 @@ public class MainActivity extends AppCompatActivity {
private String[] styleLabels() {
return new String[] { getString(R.string.overview_style_tiles), getString(R.string.overview_style_gauges),
getString(R.string.overview_style_cockpit), getString(R.string.overview_style_retro),
getString(R.string.overview_style_retro_accent), getString(R.string.overview_style_futuristic),
getString(R.string.overview_style_futuristic),
getString(R.string.overview_style_scifi), getString(R.string.overview_style_cartoon),
getString(R.string.overview_style_blueprint), getString(R.string.overview_style_mg),
getString(R.string.overview_style_minimal), getString(R.string.overview_style_terminal),
@@ -1533,8 +1566,8 @@ public class MainActivity extends AppCompatActivity {
OverviewStyle[] styles = OverviewStyle.values();
com.google.android.material.textfield.MaterialAutoCompleteTextView field =
settingsPane.findViewById(R.id.overview_style_choices);
// Wider than its field, which shares a row with the accent colour: "Retro (accent colour)".
field.setDropDownWidth(Math.round(360 * getResources().getDisplayMetrics().density));
// Wider than its field, which shares a row with the accent colour.
field.setDropDownWidth(Math.round(320 * getResources().getDisplayMetrics().density));
bindChoices(field, styleLabels(), overviewStyle().ordinal(),
position -> {
prefs.edit().putString(PREF_OVERVIEW_STYLE, styles[position].name()).apply();
@@ -2,43 +2,65 @@ package com.evsuite.abrp;
/**
* The Overview styles in the order of the Settings drop-down, the value each is stored as, and
* whether it is drawn in the chosen accent colour. Styles with their own fixed palette (Retro's
* display, the starship console, the cartoon, the blueprint, the terminal, the flight display,
* the nixie tubes) ignore it, so Settings only offers the accent colour for the others. Pure
* Java, unit-tested.
* how it takes colour:
*
* - {@link #usesAccent}: drawn in the chosen accent colour, so Settings offers the accent
* colour for it. Styles with only their own fixed palette (the starship console, the cartoon,
* the blueprint, the flight display, the nixie tubes) ignore it and Settings greys it out.
* - {@link #hasOriginal}: has an original palette of its own as well (Retro's turquoise
* display, the terminal's green phosphor); Settings then offers "Original colours" above the
* accent colours, remembered per style under {@link #originalKey()}.
*
* Pure Java, unit-tested.
*/
enum OverviewStyle {
TILES(true),
GAUGES(true),
COCKPIT(true),
RETRO(false),
RETRO_ACCENT(true),
FUTURISTIC(true),
SCIFI(false),
CARTOON(false),
BLUEPRINT(false),
MG(true),
MINIMAL(true),
TERMINAL(false),
AVIATION(false),
NIXIE(false),
CHRONOGRAPH(true);
TILES(true, false),
GAUGES(true, false),
COCKPIT(true, false),
RETRO(true, true),
FUTURISTIC(true, false),
SCIFI(false, false),
CARTOON(false, false),
BLUEPRINT(false, false),
MG(true, false),
MINIMAL(true, false),
TERMINAL(true, true),
AVIATION(false, false),
NIXIE(false, false),
CHRONOGRAPH(true, false);
static final String PREF_KEY = "overview_style";
/** Drawn in the accent colour, so the accent choice changes how it looks. */
final boolean usesAccent;
final boolean hasOriginal;
OverviewStyle(boolean usesAccent) {
OverviewStyle(boolean usesAccent, boolean hasOriginal) {
this.usesAccent = usesAccent;
this.hasOriginal = hasOriginal;
}
/** The stored style; "DASHBOARD" (Gauges before 2.22.3) and anything unknown handled. */
/** Where the choice between the original palette and the accent colour is kept. */
String originalKey() {
return "original_colours_" + name();
}
/**
* The stored style. Older values: "DASHBOARD" (Gauges before 2.22.3), and "RETRO_ACCENT" /
* "TERMINAL_ACCENT" (separate entries before 2.27.2, now the style in the accent colour —
* see {@link #wasAccentEntry}). Anything unknown is Tiles.
*/
static OverviewStyle fromPref(String stored) {
if ("DASHBOARD".equals(stored)) return GAUGES;
if ("RETRO_ACCENT".equals(stored)) return RETRO;
if ("TERMINAL_ACCENT".equals(stored)) return TERMINAL;
for (OverviewStyle style : values()) {
if (style.name().equals(stored)) return style;
}
return TILES;
}
/** A stored value from when the accent-coloured variant was its own entry. */
static boolean wasAccentEntry(String stored) {
return "RETRO_ACCENT".equals(stored) || "TERMINAL_ACCENT".equals(stored);
}
}
@@ -14,6 +14,10 @@ import java.util.Locale;
* 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 {
@@ -21,23 +25,33 @@ final class TerminalView extends ClusterCanvasView {
private final Paint text = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint scan = new Paint();
private final int green, dim, amber;
private final int accentText, accentDim;
private final int amber = 0xFFFFB000;
private int green, dim, background;
TerminalView(Context context) {
super(context);
green = 0xFF33FF66;
dim = 0xFF1C8A3A;
amber = 0xFFFFB000;
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(0xFF020A04);
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.
+1 -1
View File
@@ -126,7 +126,6 @@
<string name="overview_style_cockpit">Cockpit</string>
<string name="overview_style_retro">Retro</string>
<string name="overview_style_futuristic">Futuristic</string>
<string name="overview_style_retro_accent">Retro (accent colour)</string>
<string name="overview_style_scifi">Sci-fi</string>
<string name="overview_style_cartoon">Cartoon</string>
<string name="overview_style_blueprint">Blueprint</string>
@@ -136,6 +135,7 @@
<string name="overview_fullscreen_exit">Show the navigation again</string>
<string name="overview_style_minimal">Minimal</string>
<string name="overview_style_terminal">Terminal</string>
<string name="accent_original">Original</string>
<string name="overview_style_aviation">Aviation</string>
<string name="overview_style_nixie">Nixie tubes</string>
<string name="overview_style_chronograph">Chronograph</string>
@@ -19,17 +19,23 @@ public class OverviewStyleTest {
@Test
public void oldAndUnknownValues() {
assertEquals(OverviewStyle.GAUGES, OverviewStyle.fromPref("DASHBOARD"));
assertEquals(OverviewStyle.RETRO, OverviewStyle.fromPref("RETRO_ACCENT"));
assertEquals(OverviewStyle.TERMINAL, OverviewStyle.fromPref("TERMINAL_ACCENT"));
assertTrue(OverviewStyle.wasAccentEntry("RETRO_ACCENT"));
assertFalse(OverviewStyle.wasAccentEntry("RETRO"));
assertEquals(OverviewStyle.TILES, OverviewStyle.fromPref(null));
assertEquals(OverviewStyle.TILES, OverviewStyle.fromPref("NOPE"));
}
@Test
public void onlyStylesDrawnInTheAccentOfferIt() {
public void colourOptions() {
assertTrue(OverviewStyle.TILES.usesAccent);
assertTrue(OverviewStyle.RETRO_ACCENT.usesAccent);
assertTrue(OverviewStyle.MG.usesAccent);
assertFalse(OverviewStyle.RETRO.usesAccent);
assertFalse(OverviewStyle.TILES.hasOriginal);
assertTrue(OverviewStyle.RETRO.usesAccent);
assertTrue(OverviewStyle.RETRO.hasOriginal);
assertTrue(OverviewStyle.TERMINAL.hasOriginal);
assertFalse(OverviewStyle.CARTOON.usesAccent);
assertFalse(OverviewStyle.NIXIE.usesAccent);
assertEquals("original_colours_RETRO", OverviewStyle.RETRO.originalKey());
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB