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:
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
applicationId "com.stinnesbeck.nilsmg4"
|
||||
minSdk 27
|
||||
targetSdk 36
|
||||
versionCode 135
|
||||
versionName "2.35.0"
|
||||
versionCode 136
|
||||
versionName "2.35.1"
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -43,6 +43,7 @@ public class OverviewStyleTest {
|
||||
public void removedStylesFallBackToTiles() {
|
||||
assertEquals(OverviewStyle.TILES, OverviewStyle.fromPref("MASCOT"));
|
||||
assertEquals(OverviewStyle.TILES, OverviewStyle.fromPref("BLUEPRINT"));
|
||||
assertEquals(OverviewStyle.TILES, OverviewStyle.fromPref("AVIATION"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user