App 2.36.0: Overview style Grid, after the Tron films
Neon lines on black over a perspective grid floor that moves with the speed; the battery as a light wall, power on a disc whose inner ring turns with it, and the last two minutes of power as a light-cycle trail of right angles (the Cockpit's PowerTrace, now in ClusterValues). Cyan, or the accent colour; in cyan the app turns cyan too. In THEMES.md with two screenshots. Emulator only; not tried on the car. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
@@ -21,4 +21,6 @@ final class ClusterValues {
|
||||
String clock = "";
|
||||
/** Tyre pressures formatted in the chosen unit, "—" when unread; never null. */
|
||||
String tyreFl = "—", tyreFr = "—", tyreRl = "—", tyreRr = "—";
|
||||
/** Battery power over the last two minutes, oldest first, NaN where unread; null before any. */
|
||||
float[] powerTrail;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
package com.evsuite.abrp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* The Grid Overview style, after the neon world of the Tron films: glowing lines on black, a
|
||||
* perspective grid floor that runs towards you with the speed, and shapes with cut corners.
|
||||
*
|
||||
* Centre: the speed, large and thin. Left: the battery as a segmented light wall standing on
|
||||
* the floor, with the charge and range beside it. Right: power on a disc — the ring lights from
|
||||
* the top, clockwise while drawing and anticlockwise while regenerating, and its inner ring
|
||||
* turns with the power. On the floor, the last two minutes of power as a light-cycle trail,
|
||||
* which only ever turns at right angles. P R N D as tiles on top, the clock top right, and a
|
||||
* row of read-outs along the bottom. Unread values show "—", light nothing and leave gaps in
|
||||
* the trail; the floor stands still when the speed is unread.
|
||||
*
|
||||
* Two palettes: the original cyan, or the chosen accent colour ({@link #setAccentPalette}). A
|
||||
* low battery is orange in the original (the films' other side) and white in the accent one,
|
||||
* so it never looks like the accent itself. The floor and the disc animate (about 30 frames a
|
||||
* second) only while the car moves and the view is on screen.
|
||||
*/
|
||||
final class GridView extends ClusterCanvasView {
|
||||
|
||||
/** Battery segments in the light wall, disc segments, metres per grid tile. */
|
||||
private static final int WALL = 20, DISC = 60;
|
||||
private static final float TILE_M = 4f;
|
||||
|
||||
private final Paint line = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint text = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint sky = new Paint();
|
||||
private final Path path = new Path();
|
||||
private android.graphics.DashPathEffect dash;
|
||||
private float dashFor;
|
||||
private final RectF r = new RectF();
|
||||
private final Typeface thin = Typeface.create("sans-serif-thin", Typeface.NORMAL);
|
||||
private final Typeface label = Typeface.create("sans-serif-condensed", Typeface.BOLD);
|
||||
private final int accent, accentLight;
|
||||
|
||||
private int main, light, dim, alert;
|
||||
/** The floor's position within one tile (0..1) and the disc's inner ring angle. */
|
||||
private float floorPhase, discTurn;
|
||||
private long lastFrame;
|
||||
private int skyFor = -1;
|
||||
|
||||
GridView(Context context) {
|
||||
super(context);
|
||||
accent = AccentColor.resolve(context, R.attr.evAccent);
|
||||
accentLight = AccentColor.resolve(context, R.attr.evAccentLight);
|
||||
line.setStyle(Paint.Style.STROKE);
|
||||
line.setStrokeCap(Paint.Cap.SQUARE);
|
||||
line.setStrokeJoin(Paint.Join.MITER);
|
||||
setAccentPalette(false);
|
||||
}
|
||||
|
||||
/** The chosen accent colour instead of the original cyan. */
|
||||
void setAccentPalette(boolean useAccent) {
|
||||
main = useAccent ? accent : 0xFF18E6FF;
|
||||
light = useAccent ? accentLight : 0xFFC8FCFF;
|
||||
dim = AccentColor.withAlpha(main, 0x40);
|
||||
alert = useAccent ? 0xFFFFFFFF : 0xFFFF8A1F;
|
||||
skyFor = -1;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas c) {
|
||||
super.onDraw(c);
|
||||
float w = getWidth(), h = getHeight();
|
||||
if (w <= 0 || h <= 0) return;
|
||||
float u = Math.min(h / 640f, w / 1100f);
|
||||
advance();
|
||||
c.drawColor(0xFF000000);
|
||||
|
||||
float horizon = h * 0.55f;
|
||||
drawSky(c, w, horizon);
|
||||
drawFloor(c, w, h, horizon, u);
|
||||
drawGears(c, w, u);
|
||||
drawClock(c, w, u);
|
||||
drawSpeed(c, w, horizon, u);
|
||||
drawWall(c, horizon, u);
|
||||
drawDisc(c, w, u);
|
||||
drawTrail(c, w, h, u);
|
||||
drawReadouts(c, w, h, u);
|
||||
|
||||
boolean moving = v.speedKmh != null && v.speedKmh > 0.5f;
|
||||
keepAnimating(moving, 33);
|
||||
}
|
||||
|
||||
/** Moves the floor by the distance driven since the last frame, and turns the disc. */
|
||||
private void advance() {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
float dt = lastFrame == 0 ? 0f : Math.min(0.2f, (now - lastFrame) / 1000f);
|
||||
lastFrame = now;
|
||||
if (v.speedKmh != null && v.speedKmh > 0.5f) {
|
||||
floorPhase = (floorPhase + v.speedKmh / 3.6f * dt / TILE_M) % 1f;
|
||||
}
|
||||
if (v.powerKw != null) discTurn = (discTurn + v.powerKw * 3f * dt) % 360f;
|
||||
}
|
||||
|
||||
// ---------- Floor ----------
|
||||
|
||||
private void drawSky(Canvas c, float w, float horizon) {
|
||||
if (skyFor != (int) horizon) {
|
||||
sky.setShader(new LinearGradient(0, horizon - horizon * 0.45f, 0, horizon,
|
||||
0x00000000, AccentColor.withAlpha(main, 0x30), Shader.TileMode.CLAMP));
|
||||
skyFor = (int) horizon;
|
||||
}
|
||||
c.drawRect(0, horizon - horizon * 0.45f, w, horizon, sky);
|
||||
}
|
||||
|
||||
/** Lines converging on the horizon, and cross lines that run towards the viewer. */
|
||||
private void drawFloor(Canvas c, float w, float h, float horizon, float u) {
|
||||
float vx = w / 2f, depth = h - horizon;
|
||||
line.setStrokeWidth(1.2f * u);
|
||||
for (int i = -14; i <= 14; i++) {
|
||||
float bottomX = vx + i * w * 0.09f;
|
||||
line.setColor(AccentColor.withAlpha(main, 0x38));
|
||||
c.drawLine(vx + (bottomX - vx) * 0.02f, horizon, bottomX, h, line);
|
||||
}
|
||||
for (int k = 0; k < 24; k++) {
|
||||
float d = k + 1f - floorPhase;
|
||||
if (d <= 0.05f) continue;
|
||||
float y = horizon + depth / d;
|
||||
if (y > h || y < horizon + 1f) continue;
|
||||
float near = (y - horizon) / depth;
|
||||
line.setColor(AccentColor.withAlpha(main, (int) (0x10 + 0x50 * near)));
|
||||
c.drawLine(0, y, w, y, line);
|
||||
}
|
||||
glowLine(c, 0, horizon, w, horizon, main, 1.5f * u);
|
||||
}
|
||||
|
||||
// ---------- Top ----------
|
||||
|
||||
private void drawGears(Canvas c, float w, float u) {
|
||||
String[] gears = { "P", "R", "N", "D" };
|
||||
String now = Gear.label(v.gear);
|
||||
float tw = 58f * u, th = 40f * u, gap = 12f * u;
|
||||
float x = w / 2f - (gears.length * tw + (gears.length - 1) * gap) / 2f, top = 22f * u;
|
||||
text.setTypeface(label);
|
||||
text.setTextSize(24f * u);
|
||||
text.setTextAlign(Paint.Align.CENTER);
|
||||
for (String g : gears) {
|
||||
boolean on = g.equals(now);
|
||||
chamfer(x, top, x + tw, top + th, 9f * u);
|
||||
if (on) {
|
||||
fill.setColor(main);
|
||||
c.drawPath(path, fill);
|
||||
glow(c, path, main, 1.5f * u);
|
||||
} else {
|
||||
line.setColor(dim);
|
||||
line.setStrokeWidth(1.5f * u);
|
||||
c.drawPath(path, line);
|
||||
}
|
||||
text.setColor(on ? 0xFF000000 : dim);
|
||||
c.drawText(g, x + tw / 2f, top + th * 0.72f, text);
|
||||
x += tw + gap;
|
||||
}
|
||||
}
|
||||
|
||||
private void drawClock(Canvas c, float w, float u) {
|
||||
text.setTypeface(thin);
|
||||
text.setTextSize(44f * u);
|
||||
text.setTextAlign(Paint.Align.RIGHT);
|
||||
glowText(c, v.clock.isEmpty() ? "—" : v.clock, w - 28f * u, 62f * u, light, u);
|
||||
}
|
||||
|
||||
// ---------- Speed ----------
|
||||
|
||||
private void drawSpeed(Canvas c, float w, float horizon, float u) {
|
||||
text.setTypeface(thin);
|
||||
text.setTextSize(210f * u);
|
||||
text.setTextAlign(Paint.Align.CENTER);
|
||||
glowText(c, fmt(v.speedKmh, "%.0f", "—"), w / 2f, horizon - 62f * u, light, u);
|
||||
caption(c, "KM/H", w / 2f, horizon - 22f * u, u, Paint.Align.CENTER);
|
||||
}
|
||||
|
||||
// ---------- Battery wall ----------
|
||||
|
||||
private void drawWall(Canvas c, float horizon, float u) {
|
||||
boolean low = v.socPercent != null && v.socPercent < 20f;
|
||||
int colour = low ? alert : main;
|
||||
float left = 34f * u, right = left + 52f * u, top = 96f * u, bottom = horizon + 70f * u;
|
||||
float seg = (bottom - top) / WALL;
|
||||
int lit = v.socPercent == null ? 0 : Math.round(v.socPercent / 100f * WALL);
|
||||
for (int i = 0; i < WALL; i++) {
|
||||
float b = bottom - i * seg, t = b - seg + 3f * u;
|
||||
r.set(left, t, right, b);
|
||||
if (i < lit) {
|
||||
fill.setColor(i == lit - 1 ? light : colour);
|
||||
c.drawRect(r, fill);
|
||||
} else {
|
||||
line.setColor(dim);
|
||||
line.setStrokeWidth(1f * u);
|
||||
c.drawRect(r, line);
|
||||
}
|
||||
}
|
||||
chamfer(left - 8f * u, top - 8f * u, right + 8f * u, bottom + 8f * u, 10f * u);
|
||||
glow(c, path, colour, 1.5f * u);
|
||||
|
||||
float x = right + 30f * u;
|
||||
caption(c, low ? "LOW ENERGY" : "ENERGY", x, top + 18f * u, u, Paint.Align.LEFT);
|
||||
text.setTypeface(thin);
|
||||
text.setTextSize(96f * u);
|
||||
text.setTextAlign(Paint.Align.LEFT);
|
||||
String soc = fmt(v.socPercent, "%.0f", "—");
|
||||
glowText(c, soc, x, top + 116f * u, low ? alert : light, u);
|
||||
float socWidth = text.measureText(soc);
|
||||
text.setTextSize(36f * u);
|
||||
glowText(c, "%", x + socWidth + 8f * u, top + 116f * u, colour, u);
|
||||
caption(c, "RANGE", x, top + 168f * u, u, Paint.Align.LEFT);
|
||||
text.setTypeface(thin);
|
||||
text.setTextSize(52f * u);
|
||||
glowText(c, fmt(v.rangeKm, "%.0f km", "—"), x, top + 222f * u, light, u);
|
||||
}
|
||||
|
||||
// ---------- Power disc ----------
|
||||
|
||||
/** Zero at the top, −50 kW a quarter turn anticlockwise, 150 kW three quarters clockwise. */
|
||||
private void drawDisc(Canvas c, float w, float u) {
|
||||
float cx = w - 170f * u, cy = 238f * u, outer = 132f * u, ring = 22f * u;
|
||||
line.setColor(dim);
|
||||
line.setStrokeWidth(1.5f * u);
|
||||
c.drawCircle(cx, cy, outer + 10f * u, line);
|
||||
float sweepPerKw = 270f / 200f;
|
||||
float power = v.powerKw == null ? 0f : Math.max(-50f, Math.min(150f, v.powerKw));
|
||||
float segDeg = 270f / DISC;
|
||||
for (int i = 0; i < DISC; i++) {
|
||||
// Segment i covers −50 + i·(200/DISC) … one step more, drawn from −135° (top-left).
|
||||
float kwFrom = -50f + i * 200f / DISC, kwTo = kwFrom + 200f / DISC;
|
||||
boolean on = v.powerKw != null
|
||||
&& (power >= 0 ? kwFrom >= 0f && kwFrom < power : kwTo <= 0f && kwTo > power);
|
||||
float start = -90f + kwFrom * sweepPerKw;
|
||||
r.set(cx - outer + ring / 2f, cy - outer + ring / 2f, cx + outer - ring / 2f, cy + outer - ring / 2f);
|
||||
line.setStrokeWidth(ring);
|
||||
line.setStrokeCap(Paint.Cap.BUTT);
|
||||
line.setColor(on ? (power < 0 ? light : main) : AccentColor.withAlpha(main, 0x22));
|
||||
c.drawArc(r, start + 0.6f, segDeg - 1.2f, false, line);
|
||||
}
|
||||
line.setStrokeCap(Paint.Cap.SQUARE);
|
||||
// The zero mark, and an inner ring of dashes that turns with the power.
|
||||
glowLine(c, cx, cy - outer - 16f * u, cx, cy - outer + ring + 6f * u, light, 2f * u);
|
||||
float inner = outer - ring - 22f * u;
|
||||
line.setStrokeWidth(3f * u);
|
||||
line.setColor(main);
|
||||
r.set(cx - inner, cy - inner, cx + inner, cy + inner);
|
||||
for (int k = 0; k < 8; k++) c.drawArc(r, discTurn + k * 45f, 26f, false, line);
|
||||
line.setColor(dim);
|
||||
line.setStrokeWidth(1f * u);
|
||||
c.drawCircle(cx, cy, inner - 12f * u, line);
|
||||
|
||||
text.setTypeface(thin);
|
||||
text.setTextSize(64f * u);
|
||||
text.setTextAlign(Paint.Align.CENTER);
|
||||
glowText(c, fmt(v.powerKw, "%.1f", "—"), cx, cy + 18f * u, light, u);
|
||||
caption(c, v.powerKw != null && v.powerKw < 0f ? "KW · REGEN" : "KW", cx, cy + 50f * u, u,
|
||||
Paint.Align.CENTER);
|
||||
caption(c, "POWER", cx, cy - 46f * u, u, Paint.Align.CENTER);
|
||||
}
|
||||
|
||||
// ---------- Light trail ----------
|
||||
|
||||
/** Power over the last two minutes as a trail of right angles; NaN breaks it. */
|
||||
private void drawTrail(Canvas c, float w, float h, float u) {
|
||||
float[] trail = v.powerTrail;
|
||||
float left = 300f * u, right = w - 360f * u, top = h * 0.66f, bottom = h * 0.84f;
|
||||
if (right - left < 200f * u) return;
|
||||
float max = 10f, min = -10f;
|
||||
if (trail != null) {
|
||||
for (float p : trail) {
|
||||
if (Float.isNaN(p)) continue;
|
||||
max = Math.max(max, (float) Math.ceil(p / 10f) * 10f);
|
||||
min = Math.min(min, (float) Math.floor(p / 10f) * 10f);
|
||||
}
|
||||
}
|
||||
float zero = bottom - (0f - min) / (max - min) * (bottom - top);
|
||||
line.setColor(dim);
|
||||
line.setStrokeWidth(1f * u);
|
||||
if (dash == null || dashFor != u) {
|
||||
dash = new android.graphics.DashPathEffect(new float[] { 6f * u, 8f * u }, 0);
|
||||
dashFor = u;
|
||||
}
|
||||
line.setPathEffect(dash);
|
||||
c.drawLine(left, zero, right, zero, line);
|
||||
line.setPathEffect(null);
|
||||
caption(c, "POWER · LAST 2 MIN", left, top - 10f * u, u, Paint.Align.LEFT);
|
||||
if (trail == null || trail.length < 2) return;
|
||||
|
||||
float step = (right - left) / (trail.length - 1);
|
||||
path.reset();
|
||||
boolean open = false;
|
||||
float lastX = 0, lastY = 0;
|
||||
for (int i = 0; i < trail.length; i++) {
|
||||
float p = trail[i];
|
||||
if (Float.isNaN(p)) {
|
||||
open = false;
|
||||
continue;
|
||||
}
|
||||
float x = left + i * step, y = bottom - (p - min) / (max - min) * (bottom - top);
|
||||
if (!open) {
|
||||
path.moveTo(x, y);
|
||||
open = true;
|
||||
} else {
|
||||
path.lineTo(x, lastY); // along, then a right angle
|
||||
path.lineTo(x, y);
|
||||
}
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
glow(c, path, main, 2.5f * u);
|
||||
if (open) {
|
||||
fill.setColor(light);
|
||||
c.drawCircle(lastX, lastY, 5f * u, fill);
|
||||
fill.setColor(AccentColor.withAlpha(main, 0x50));
|
||||
c.drawCircle(lastX, lastY, 12f * u, fill);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Read-outs ----------
|
||||
|
||||
private void drawReadouts(Canvas c, float w, float h, float u) {
|
||||
String[][] cells = {
|
||||
{ "USAGE · 5 MIN", fmt(v.usage5Min, "%.1f", "—") },
|
||||
{ "SINCE START", fmt(v.usageTrip, "%.1f", "—") },
|
||||
{ "TRIP", fmt(v.tripKm, "%.1f km", "—") },
|
||||
{ "CLIMATE", fmt(v.climateCelsius, "%.0f °C", "—") },
|
||||
{ "ODOMETER", fmt(v.odometerKm, "%,.0f km", "—") },
|
||||
};
|
||||
float margin = 24f * u, gap = 14f * u, top = h - 70f * u, bottom = h - 14f * u;
|
||||
float cw = (w - 2 * margin - (cells.length - 1) * gap) / cells.length;
|
||||
for (int i = 0; i < cells.length; i++) {
|
||||
float l = margin + i * (cw + gap);
|
||||
chamfer(l, top, l + cw, bottom, 10f * u);
|
||||
fill.setColor(0xE6000000);
|
||||
c.drawPath(path, fill);
|
||||
line.setColor(dim);
|
||||
line.setStrokeWidth(1.5f * u);
|
||||
c.drawPath(path, line);
|
||||
caption(c, cells[i][0], l + 16f * u, top + 20f * u, u, Paint.Align.LEFT);
|
||||
text.setTypeface(thin);
|
||||
text.setTextSize(30f * u);
|
||||
text.setTextAlign(Paint.Align.RIGHT);
|
||||
glowText(c, cells[i][1], l + cw - 16f * u, bottom - 10f * u, light, u);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Light ----------
|
||||
|
||||
/** A rectangle with its top-left and bottom-right corners cut, into [path]. */
|
||||
private void chamfer(float l, float t, float rr, float b, float cut) {
|
||||
path.reset();
|
||||
path.moveTo(l + cut, t);
|
||||
path.lineTo(rr, t);
|
||||
path.lineTo(rr, b - cut);
|
||||
path.lineTo(rr - cut, b);
|
||||
path.lineTo(l, b);
|
||||
path.lineTo(l, t + cut);
|
||||
path.close();
|
||||
}
|
||||
|
||||
/** A line that glows: two wide faint strokes under a sharp one. */
|
||||
private void glow(Canvas c, Path p, int colour, float width) {
|
||||
line.setColor(AccentColor.withAlpha(colour, 0x18));
|
||||
line.setStrokeWidth(width * 6f);
|
||||
c.drawPath(p, line);
|
||||
line.setColor(AccentColor.withAlpha(colour, 0x40));
|
||||
line.setStrokeWidth(width * 3f);
|
||||
c.drawPath(p, line);
|
||||
line.setColor(colour);
|
||||
line.setStrokeWidth(width);
|
||||
c.drawPath(p, line);
|
||||
}
|
||||
|
||||
private void glowLine(Canvas c, float x1, float y1, float x2, float y2, int colour, float width) {
|
||||
path.reset();
|
||||
path.moveTo(x1, y1);
|
||||
path.lineTo(x2, y2);
|
||||
glow(c, path, colour, width);
|
||||
}
|
||||
|
||||
/** Text with a halo: a wide faint outline under the letters. */
|
||||
private void glowText(Canvas c, String s, float x, float y, int colour, float u) {
|
||||
text.setStyle(Paint.Style.STROKE);
|
||||
text.setStrokeWidth(7f * u);
|
||||
text.setColor(AccentColor.withAlpha(colour, 0x30));
|
||||
c.drawText(s, x, y, text);
|
||||
text.setStyle(Paint.Style.FILL);
|
||||
text.setColor(colour);
|
||||
c.drawText(s, x, y, text);
|
||||
}
|
||||
|
||||
/** A small label in spaced capitals. */
|
||||
private void caption(Canvas c, String s, float x, float y, float u, Paint.Align align) {
|
||||
text.setTypeface(label);
|
||||
text.setTextSize(15f * u);
|
||||
text.setLetterSpacing(0.2f);
|
||||
text.setTextAlign(align);
|
||||
text.setStyle(Paint.Style.FILL);
|
||||
text.setColor(main);
|
||||
c.drawText(s, x, y, text);
|
||||
text.setLetterSpacing(0f);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
/** The styles built on {@link ClusterCanvasView}, fed together. */
|
||||
private final java.util.List<ClusterCanvasView> canvasStyles = new java.util.ArrayList<>();
|
||||
private TerminalView terminalView;
|
||||
private GridView gridView;
|
||||
/** The pane of each Overview style, by its {@link OverviewStyle} ordinal. */
|
||||
private View[] stylePanes;
|
||||
private View consumptionPane;
|
||||
@@ -342,13 +343,14 @@ public class MainActivity extends AppCompatActivity {
|
||||
View sciFiPane = drawnPane(sciFiView), cartoonPane = drawnPane(cartoonView),
|
||||
mgPane = drawnPane(octagonView);
|
||||
terminalView = new TerminalView(this);
|
||||
gridView = new GridView(this);
|
||||
java.util.Collections.addAll(canvasStyles, new MinimalView(this), terminalView,
|
||||
new NixieView(this), new ChronographView(this));
|
||||
new NixieView(this), new ChronographView(this), gridView);
|
||||
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[0], canvasPanes[1], canvasPanes[2], canvasPanes[3], canvasPanes[4] };
|
||||
// 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(
|
||||
@@ -1713,9 +1715,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
private void applyOverviewStyle() {
|
||||
OverviewStyle style = overviewStyle();
|
||||
for (View pane : stylePanes) pane.setVisibility(pane == stylePanes[style.ordinal()] ? View.VISIBLE : View.GONE);
|
||||
// Retro and Terminal: their original palette or the accent colour, as chosen for each.
|
||||
// Retro, Terminal and Grid: their original palette or the accent colour, as chosen for each.
|
||||
retroView.setAccentPalette(!originalColours(OverviewStyle.RETRO));
|
||||
terminalView.setAccentPalette(!originalColours(OverviewStyle.TERMINAL));
|
||||
gridView.setAccentPalette(!originalColours(OverviewStyle.GRID));
|
||||
// The accent choices depend on the style ("Original colours" for Retro and Terminal).
|
||||
if (settingsPane != null) bindAccentColor();
|
||||
}
|
||||
@@ -1746,7 +1749,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
getString(R.string.overview_style_mg),
|
||||
getString(R.string.overview_style_minimal), getString(R.string.overview_style_terminal),
|
||||
getString(R.string.overview_style_nixie),
|
||||
getString(R.string.overview_style_chronograph) };
|
||||
getString(R.string.overview_style_chronograph), getString(R.string.overview_style_grid) };
|
||||
}
|
||||
|
||||
/** The style drop-down; every style is fed on every reading, only the chosen one is shown. */
|
||||
@@ -1812,6 +1815,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
v.tyreFr = unit.format(s.getTirePressures().getFrontRightKpa());
|
||||
v.tyreRl = unit.format(s.getTirePressures().getRearLeftKpa());
|
||||
v.tyreRr = unit.format(s.getTirePressures().getRearRightKpa());
|
||||
v.powerTrail = cockpitTrace.snapshot();
|
||||
retroView.setValues(v);
|
||||
flowView.setValues(v);
|
||||
sciFiView.setValues(v);
|
||||
|
||||
@@ -8,7 +8,7 @@ package com.evsuite.abrp;
|
||||
* colour for it. Styles with only their own fixed palette (the starship console, the cartoon,
|
||||
* 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
|
||||
* display, the terminal's green phosphor, the Grid's neon cyan); Settings then offers "Original colours" above the
|
||||
* accent colours, remembered per style under {@link #originalKey()}.
|
||||
*
|
||||
* Pure Java, unit-tested.
|
||||
@@ -25,7 +25,8 @@ enum OverviewStyle {
|
||||
MINIMAL(true, false),
|
||||
TERMINAL(true, true),
|
||||
NIXIE(false, false),
|
||||
CHRONOGRAPH(true, false);
|
||||
CHRONOGRAPH(true, false),
|
||||
GRID(true, true);
|
||||
|
||||
static final String PREF_KEY = "overview_style";
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ final class StylePalette {
|
||||
case SCIFI: return R.style.ThemeOverlay_EV_Style_SciFi;
|
||||
case CARTOON: return R.style.ThemeOverlay_EV_Style_Cartoon;
|
||||
case NIXIE: return R.style.ThemeOverlay_EV_Style_Nixie;
|
||||
case GRID: return R.style.ThemeOverlay_EV_Style_Grid;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@
|
||||
<string name="overview_style_minimal">Minimal</string>
|
||||
<string name="overview_style_terminal">Terminal</string>
|
||||
<string name="accent_original">Original</string>
|
||||
<string name="overview_style_grid">Grid</string>
|
||||
<string name="overview_style_nixie">Nixie tubes</string>
|
||||
<string name="overview_style_chronograph">Chronograph</string>
|
||||
<string name="cockpit_battery">BATTERY</string>
|
||||
|
||||
@@ -54,4 +54,14 @@
|
||||
<item name="evAccentStroke">#80774A26</item>
|
||||
<item name="evAccentTrack">#6A4224</item>
|
||||
</style>
|
||||
<!-- Grid: the neon cyan of its lines. -->
|
||||
<style name="ThemeOverlay.EV.Style.Grid" parent="">
|
||||
<item name="evAccent">#18E6FF</item>
|
||||
<item name="evOnAccent">#00161A</item>
|
||||
<item name="evAccentLight">#C8FCFF</item>
|
||||
<item name="evAccentDeep">#06343C</item>
|
||||
<item name="evAccentSurface">#0A1C20</item>
|
||||
<item name="evAccentStroke">#80267A86</item>
|
||||
<item name="evAccentTrack">#1E6672</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user