App 2.35.1: remove Aviation; Sci-fi colours the app in its frame's lilac

Aviation is removed (a stored one opens Tiles). The app's Sci-fi palette
now takes the lilac of the console frame's lower elbow instead of the
orange of the upper one.

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-25 20:42:45 +01:00
co-authored by Claude Opus 5.5
parent 921bab7170
commit ee66e2f37f
11 changed files with 21 additions and 262 deletions
@@ -1,233 +0,0 @@
package com.evsuite.abrp;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Typeface;
/**
* The Aviation Overview style: a pilot's primary flight display.
*
* The speed on a scrolling tape at the left and the battery on one at the right (where the
* altitude would be), each with a readout box. In the middle, a horizon that "pitches" with the
* power: drawing power raises the nose (the horizon drops), regenerating lowers it; a ladder of
* kW marks and the yellow aircraft symbol over it. Green annunciators at the top (gear, power,
* clock), boxes of range, trip, usage, climate and odometer along the bottom. Unread values
* show "—" and leave the horizon level.
*
* The tapes and the horizon ease to new values and redraw only until settled, only on screen.
*/
final class AviationView extends ClusterCanvasView {
private static final long FRAME_MS = 33;
private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint line = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint text = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path path = new Path();
private final RectF rect = new RectF();
private final int sky = 0xFF1E4C80, ground = 0xFF5A3A1E, white = 0xFFF2F4F7, green = 0xFF3CE07A,
cyan = 0xFF4FD8F0, magenta = 0xFFE05CE0, yellow = 0xFFFFD21F, tape = 0xCC1A1F26, amber = 0xFFFFB000;
private float speedShown = Float.NaN, socShown = Float.NaN, powerShown = Float.NaN;
AviationView(Context context) {
super(context);
line.setStyle(Paint.Style.STROKE);
line.setStrokeCap(Paint.Cap.ROUND);
text.setTypeface(Typeface.create("sans-serif-condensed", Typeface.BOLD));
text.setFontFeatureSettings("tnum");
}
@Override
protected void onDraw(Canvas c) {
super.onDraw(c);
float w = getWidth(), h = getHeight();
if (w <= 0 || h <= 0) return;
speedShown = ease(speedShown, v.speedKmh, 180f);
socShown = ease(socShown, v.socPercent, 100f);
powerShown = ease(powerShown, v.powerKw, 200f);
boolean moving = (v.speedKmh != null && speedShown != v.speedKmh)
|| (v.socPercent != null && socShown != v.socPercent)
|| (v.powerKw != null && powerShown != v.powerKw);
c.drawColor(0xFF05070A);
float top = h * 0.10f, bottom = h * 0.84f;
float tapeW = w * 0.10f;
float leftTape = w * 0.04f, rightTape = w - w * 0.04f - tapeW;
drawHorizon(c, leftTape + tapeW + 16f * dp, top, rightTape - 16f * dp, bottom);
drawTape(c, leftTape, top, tapeW, bottom, v.speedKmh == null ? null : speedShown, 0f, 250f, 10f, 20f,
fmt(v.speedKmh, "%.0f", "—"), "KM/H", true);
drawTape(c, rightTape, top, tapeW, bottom, v.socPercent == null ? null : socShown, 0f, 100f, 5f, 10f,
fmt(v.socPercent, "%.0f", "—"), "BATT %", false);
drawAnnunciators(c, w, top);
drawBoxes(c, w, h, bottom);
keepAnimating(moving, FRAME_MS);
}
/** Sky and ground split by a horizon that moves with the power, a kW ladder, the aircraft. */
private void drawHorizon(Canvas c, float l, float t, float r, float b) {
float cx = (l + r) / 2f, cy = (t + b) / 2f, half = (b - t) / 2f;
// 150 kW lifts the nose by 0.8 of the half height; regeneration lowers it, 50 kW as far.
float pitch = v.powerKw == null ? 0f
: powerShown >= 0f ? Math.min(150f, powerShown) / 150f : Math.max(-50f, powerShown) / 50f * 0.6f;
float horizon = cy + pitch * half * 0.8f;
float perKw = half * 0.8f / 150f;
c.save();
rect.set(l, t, r, b);
c.clipRect(rect);
fill.setColor(sky);
c.drawRect(l, t, r, horizon, fill);
fill.setColor(ground);
c.drawRect(l, horizon, r, b, fill);
line.setColor(white);
line.setStrokeWidth(2.5f * dp);
c.drawLine(l, horizon, r, horizon, line);
// The ladder: a mark every 25 kW above the horizon (drawing), below it (regenerating).
text.setTextSize(13f * dp);
for (int kw = -50; kw <= 150; kw += 25) {
if (kw == 0) continue;
float y = horizon - kw * (kw > 0 ? perKw : perKw * 150f / 50f * 0.6f);
float half2 = kw % 50 == 0 ? (r - l) * 0.10f : (r - l) * 0.05f;
line.setStrokeWidth(1.5f * dp);
c.drawLine(cx - half2, y, cx + half2, y, line);
if (kw % 50 == 0) {
text.setColor(white);
text.setTextAlign(Paint.Align.LEFT);
c.drawText(String.valueOf(kw), cx + half2 + 6f * dp, y + 5f * dp, text);
text.setTextAlign(Paint.Align.RIGHT);
c.drawText(String.valueOf(kw), cx - half2 - 6f * dp, y + 5f * dp, text);
}
}
c.restore();
// The aircraft symbol, fixed: two wings and a dot, yellow outlined in black.
float wing = (r - l) * 0.14f;
for (int pass = 0; pass < 2; pass++) {
line.setColor(pass == 0 ? 0xFF000000 : yellow);
line.setStrokeWidth(pass == 0 ? 9f * dp : 5f * dp);
c.drawLine(cx - wing * 2f, cy, cx - wing, cy, line);
c.drawLine(cx - wing, cy, cx - wing, cy + wing * 0.25f, line);
c.drawLine(cx + wing * 2f, cy, cx + wing, cy, line);
c.drawLine(cx + wing, cy, cx + wing, cy + wing * 0.25f, line);
}
fill.setColor(yellow);
c.drawCircle(cx, cy, 5f * dp, fill);
// The power readout in magenta under the symbol.
text.setTextAlign(Paint.Align.CENTER);
text.setTextSize(22f * dp);
text.setColor(v.powerKw != null && v.powerKw < 0f ? cyan : magenta);
c.drawText(fmt(v.powerKw, "%.1f kW", "— kW"), cx, b - 14f * dp, text);
line.setColor(0xFF2A3038);
line.setStrokeWidth(2f * dp);
c.drawRect(l, t, r, b, line);
}
/**
* A vertical tape scrolling with its value: ticks every {@code minor}, numbers every
* {@code major}, and a readout box at the middle pointing in (right for the left tape).
*/
private void drawTape(Canvas c, float l, float t, float tw, float b, Float value, float min, float max,
float minor, float major, String readout, String title, boolean left) {
float cy = (t + b) / 2f, perUnit = (b - t) / (max - min) * (max > 150f ? 3.2f : 2.2f);
fill.setColor(tape);
c.drawRect(l, t, l + tw, b, fill);
c.save();
c.clipRect(l, t, l + tw, b);
float centre = value == null ? min : value;
text.setTextSize(16f * dp);
text.setColor(white);
line.setColor(white);
// No value, no scale: a tape centred on zero would read as 0.
for (float u = min; value != null && u <= max; u += minor) {
float y = cy - (u - centre) * perUnit;
if (y < t - 20f * dp || y > b + 20f * dp) continue;
boolean isMajor = Math.round(u) % Math.round(major) == 0;
float len = isMajor ? tw * 0.22f : tw * 0.12f;
line.setStrokeWidth(2f * dp);
float edge = left ? l + tw : l;
c.drawLine(edge, y, edge + (left ? -len : len), y, line);
if (isMajor) {
text.setTextAlign(left ? Paint.Align.RIGHT : Paint.Align.LEFT);
c.drawText(String.valueOf(Math.round(u)), left ? edge - len - 6f * dp : edge + len + 6f * dp, y + 6f * dp, text);
}
}
c.restore();
// The readout box with its pointer.
boolean low = !left && v.socPercent != null && v.socPercent < 20f;
float bh = 44f * dp;
path.reset();
if (left) {
path.moveTo(l + 2f * dp, cy - bh / 2f);
path.lineTo(l + tw - 12f * dp, cy - bh / 2f);
path.lineTo(l + tw + 2f * dp, cy);
path.lineTo(l + tw - 12f * dp, cy + bh / 2f);
path.lineTo(l + 2f * dp, cy + bh / 2f);
} else {
path.moveTo(l + tw - 2f * dp, cy - bh / 2f);
path.lineTo(l + 12f * dp, cy - bh / 2f);
path.lineTo(l - 2f * dp, cy);
path.lineTo(l + 12f * dp, cy + bh / 2f);
path.lineTo(l + tw - 2f * dp, cy + bh / 2f);
}
path.close();
fill.setColor(0xFF000000);
c.drawPath(path, fill);
line.setColor(low ? amber : white);
line.setStrokeWidth(2f * dp);
c.drawPath(path, line);
text.setTextAlign(Paint.Align.CENTER);
text.setTextSize(30f * dp);
text.setColor(low ? amber : green);
c.drawText(readout, l + tw / 2f + (left ? -4f * dp : 4f * dp), cy + 11f * dp, text);
text.setTextSize(14f * dp);
text.setColor(cyan);
c.drawText(title, l + tw / 2f, t - 8f * dp, text);
}
/** The annunciators: gear, drive state and the clock, green on black in boxes. */
private void drawAnnunciators(Canvas c, float w, float top) {
String gear = Gear.label(v.gear);
String state = v.powerKw == null ? "—" : v.powerKw < -0.3f ? "REGEN" : v.powerKw > 0.3f ? "DRIVE PWR" : "IDLE";
String[] items = { "GEAR " + (gear.isEmpty() ? "—" : gear), state, v.clock.isEmpty() ? "—" : v.clock };
float bw = w * 0.16f, gap = w * 0.02f, x = (w - 3 * bw - 2 * gap) / 2f, bt = 4f * dp, bb = top - 26f * dp;
if (bb - bt < 18f * dp) bb = bt + 22f * dp;
text.setTextSize((bb - bt) * 0.6f);
text.setTextAlign(Paint.Align.CENTER);
for (String item : items) {
line.setColor(0xFF2A3038);
line.setStrokeWidth(1.5f * dp);
c.drawRect(x, bt, x + bw, bb, line);
text.setColor(green);
c.drawText(item, x + bw / 2f, bb - (bb - bt) * 0.25f, text);
x += bw + gap;
}
}
/** Data boxes along the bottom, cyan labels over white values. */
private void drawBoxes(Canvas c, float w, float h, float top) {
String[][] boxes = {
{ "RANGE", fmt(v.rangeKm, "%.0f km", "—") },
{ "TRIP", fmt(v.tripKm, "%.1f km", "—") },
{ "USAGE 5 MIN", fmt(v.usage5Min, "%.1f", "—") },
{ "SINCE START", fmt(v.usageTrip, "%.1f", "—") },
{ "CLIMATE", fmt(v.climateCelsius, "%.0f °C", "—") },
{ "ODOMETER", fmt(v.odometerKm, "%,.0f", "—") },
};
float gap = 8f * dp, bt = top + 14f * dp, bb = h - 4f * dp;
float bw = (w - gap * (boxes.length - 1)) / boxes.length;
for (int i = 0; i < boxes.length; i++) {
float x = i * (bw + gap);
line.setColor(0xFF2A3038);
line.setStrokeWidth(1.5f * dp);
c.drawRect(x, bt, x + bw, bb, line);
text.setTextAlign(Paint.Align.CENTER);
text.setTextSize(12f * dp);
text.setColor(cyan);
c.drawText(boxes[i][0], x + bw / 2f, bt + 17f * dp, text);
text.setTextSize(Math.min(24f * dp, (bb - bt) * 0.42f));
text.setColor(white);
c.drawText(boxes[i][1], x + bw / 2f, bb - 10f * dp, text);
}
}
}
@@ -343,12 +343,12 @@ public class MainActivity extends AppCompatActivity {
mgPane = drawnPane(octagonView);
terminalView = new TerminalView(this);
java.util.Collections.addAll(canvasStyles, new MinimalView(this), terminalView,
new AviationView(this), new NixieView(this), new ChronographView(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,
futuristicPane, sciFiPane, cartoonPane, mgPane,
canvasPanes[0], canvasPanes[1], canvasPanes[2], canvasPanes[3], canvasPanes[4] };
canvasPanes[0], canvasPanes[1], canvasPanes[2], canvasPanes[3] };
// The Overview page holds every style; Settings → Overview style picks the one shown.
android.widget.FrameLayout overviewPage = new android.widget.FrameLayout(this);
overviewPage.setLayoutParams(new android.view.ViewGroup.LayoutParams(
@@ -1745,7 +1745,7 @@ public class MainActivity extends AppCompatActivity {
getString(R.string.overview_style_scifi), getString(R.string.overview_style_cartoon),
getString(R.string.overview_style_mg),
getString(R.string.overview_style_minimal), getString(R.string.overview_style_terminal),
getString(R.string.overview_style_aviation), getString(R.string.overview_style_nixie),
getString(R.string.overview_style_nixie),
getString(R.string.overview_style_chronograph) };
}
@@ -24,7 +24,6 @@ enum OverviewStyle {
MG(true, false),
MINIMAL(true, false),
TERMINAL(true, true),
AVIATION(false, false),
NIXIE(false, false),
CHRONOGRAPH(true, false);
@@ -56,7 +55,7 @@ enum OverviewStyle {
* 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, including "BLUEPRINT" and
* "MASCOT", the styles removed in 2.35.0.
* "MASCOT", the styles removed in 2.35.0, and "AVIATION" (2.35.1).
*/
static OverviewStyle fromPref(String stored) {
if ("DASHBOARD".equals(stored)) return GAUGES;
@@ -17,7 +17,6 @@ final class StylePalette {
case TERMINAL: return R.style.ThemeOverlay_EV_Style_Terminal;
case SCIFI: return R.style.ThemeOverlay_EV_Style_SciFi;
case CARTOON: return R.style.ThemeOverlay_EV_Style_Cartoon;
case AVIATION: return R.style.ThemeOverlay_EV_Style_Aviation;
case NIXIE: return R.style.ThemeOverlay_EV_Style_Nixie;
default: return 0;
}
-1
View File
@@ -135,7 +135,6 @@
<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>
<string name="cockpit_battery">BATTERY</string>
+8 -18
View File
@@ -24,15 +24,15 @@
<item name="evAccentStroke">#80296B3A</item>
<item name="evAccentTrack">#1C8A3A</item>
</style>
<!-- Sci-fi: the starship console's orange, peach for the light tone (scifi_orange, scifi_peach). -->
<!-- Sci-fi: the lilac of the console frame's lower elbow (scifi_lilac). -->
<style name="ThemeOverlay.EV.Style.SciFi" parent="">
<item name="evAccent">#FF9966</item>
<item name="evOnAccent">#1F0E04</item>
<item name="evAccentLight">#FFCC99</item>
<item name="evAccentDeep">#3D2214</item>
<item name="evAccentSurface">#2A1B13</item>
<item name="evAccentStroke">#80775040</item>
<item name="evAccentTrack">#6A4A36</item>
<item name="evAccent">#CC99CC</item>
<item name="evOnAccent">#1C0F1C</item>
<item name="evAccentLight">#E8CCE8</item>
<item name="evAccentDeep">#33203A</item>
<item name="evAccentSurface">#22182A</item>
<item name="evAccentStroke">#80705070</item>
<item name="evAccentTrack">#5E4460</item>
</style>
<!-- Cartoon: its sunny orange. -->
<style name="ThemeOverlay.EV.Style.Cartoon" parent="">
@@ -44,16 +44,6 @@
<item name="evAccentStroke">#80775320</item>
<item name="evAccentTrack">#6A4A1C</item>
</style>
<!-- Aviation: the flight display's cyan for selected values. -->
<style name="ThemeOverlay.EV.Style.Aviation" parent="">
<item name="evAccent">#4FD8F0</item>
<item name="evOnAccent">#021418</item>
<item name="evAccentLight">#A8EEFA</item>
<item name="evAccentDeep">#0C2E36</item>
<item name="evAccentSurface">#0F1F24</item>
<item name="evAccentStroke">#80306A76</item>
<item name="evAccentTrack">#2C6470</item>
</style>
<!-- Nixie tubes: the neon's orange glow. -->
<style name="ThemeOverlay.EV.Style.Nixie" parent="">
<item name="evAccent">#FF8A1F</item>