Author SHA1 Message Date
nilsandClaude Opus 5.5 ee7cf85255 App 2.43.2: Halloween's teeth are triangles in the right colour
They hang from the grin's upper lip, each in the colour of the rib behind
it: the right one was the darker orange on the lighter rib. Not tried on
the car.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-31 13:06:35 +01:00
nilsandClaude Opus 5.5 1686b791bc SPECS: app 2.43.1 published
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-31 12:49:17 +01:00
nilsandClaude Opus 5.5 12752c41e2 App 2.43.1: Halloween's bats can be seen
They were near-black and small on the darkest part of the sky. Now a
moonlit purple-grey with a light edge, a third larger. Not tried on the car.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-31 12:43:10 +01:00
nilsandClaude Opus 5.5 293464e297 SPECS: app 2.43.0 published
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-31 09:27:42 +01:00
5 changed files with 35 additions and 14 deletions
+4
View File
@@ -140,6 +140,10 @@ 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 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` 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). (Android 9 AOSP at 1920×720, 160 dpi; the car build installs as the system user — checked).
- 2026-10-31 — App 2.43.2: Halloween's teeth are triangles hanging from the grin's upper lip, each in the colour of the rib behind it (the right one was the darker orange on the lighter rib; owner: "the color on the right side is off"). Two screenshots redone. Tested: unit tests, lint, emulator with sample values (drawing in panel and full screen, regenerating in full screen) and with no data. Not tried on the car.
- 2026-10-31 — App 2.43.1 published (`car-2.43.1`).
- 2026-10-31 — App 2.43.1: Halloween's bats can be seen. They were near-black and small on the darkest part of the sky, and the owner did not spot them; now a moonlit purple-grey with a light edge, a third larger. Two screenshots redone. Tested: unit tests, lint, emulator with sample values (panel and full screen) and with no data (no bats). Not tried on the car.
- 2026-10-31 — App 2.43.0 published (`car-2.43.0`).
- 2026-10-31 — App 2.43.0: Overview styles **Día de Muertos** (`MuertosView`) and **Halloween** - 2026-10-31 — App 2.43.0: Overview styles **Día de Muertos** (`MuertosView`) and **Halloween**
(`HalloweenView`; owner: "let's design a 'dia de muertos' and a 'halloween' style"), each with (`HalloweenView`; owner: "let's design a 'dia de muertos' and a 'halloween' style"), each with
its own palette (the papel picado's pink, the pumpkin's orange). Día de Muertos, an ofrenda and its own palette (the papel picado's pink, the pumpkin's orange). Día de Muertos, an ofrenda and
+2 -2
View File
@@ -34,8 +34,8 @@ android {
applicationId "com.stinnesbeck.nilsmg4" applicationId "com.stinnesbeck.nilsmg4"
minSdk 27 minSdk 27
targetSdk 36 targetSdk 36
versionCode 153 versionCode 155
versionName "2.43.0" versionName "2.43.2"
// Nothing about the server ships in the APK: its URL and ingest token are entered in // 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. // the app's Server tab (ServerConfig), and the database is only reachable by the server.
@@ -39,6 +39,7 @@ final class HalloweenView extends ClusterCanvasView {
private static final int STONE_DARK = 0xFF3E3C4A, MOON = 0xFFF4E9B8, MOON_SHADE = 0xFFD8C98C; private static final int STONE_DARK = 0xFF3E3C4A, MOON = 0xFFF4E9B8, MOON_SHADE = 0xFFD8C98C;
private static final int POTION = 0xFF7BF03A, GLASS = 0xFFB9D8D0, BONE = 0xFFF2EAD8, ORANGE = 0xFFFF8C1A; private static final int POTION = 0xFF7BF03A, GLASS = 0xFFB9D8D0, BONE = 0xFFF2EAD8, ORANGE = 0xFFFF8C1A;
private static final int STEM = 0xFF4E7A2B, WOOD = 0xFF2E1B10, INK = 0xFF140A08; private static final int STEM = 0xFF4E7A2B, WOOD = 0xFF2E1B10, INK = 0xFF140A08;
private static final int BAT = 0xFF6F6390, BAT_EDGE = 0xFFD6CCEA;
private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint line = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint line = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -180,11 +181,11 @@ final class HalloweenView extends ClusterCanvasView {
float x = ((i * 0.13f * span + time * speed) % span) + w * 0.04f; float x = ((i * 0.13f * span + time * speed) % span) + w * 0.04f;
float y = lane + (float) Math.sin(time * 2f + i) * 8f * u; float y = lane + (float) Math.sin(time * 2f + i) * 8f * u;
if (Math.abs(x - cx) < 60f * u) y -= 10f * u; if (Math.abs(x - cx) < 60f * u) y -= 10f * u;
bat(c, x, y, (i % 2 == 0 ? 15f : 12f) * u, (float) Math.sin(time * 14f + i * 2f)); bat(c, x, y, (i % 2 == 0 ? 20f : 16f) * u, (float) Math.sin(time * 14f + i * 2f), u);
} }
} }
private void bat(Canvas c, float x, float y, float s, float flap) { private void bat(Canvas c, float x, float y, float s, float flap, float u) {
float tip = -s * 0.5f * flap; float tip = -s * 0.5f * flap;
path.reset(); path.reset();
path.moveTo(x, y - s * 0.3f); path.moveTo(x, y - s * 0.3f);
@@ -197,8 +198,12 @@ final class HalloweenView extends ClusterCanvasView {
path.quadTo(x - s * 1.1f, y + s * 0.05f, x - s * 1.5f, y - s * 0.3f + tip); path.quadTo(x - s * 1.1f, y + s * 0.05f, x - s * 1.5f, y - s * 0.3f + tip);
path.quadTo(x - s * 0.6f, y - s * 0.7f + tip, x, y - s * 0.3f); path.quadTo(x - s * 0.6f, y - s * 0.7f + tip, x, y - s * 0.3f);
path.close(); path.close();
fill.setColor(0xFF06030C); // Lit by the moon: black would vanish in the night sky.
fill.setColor(BAT);
c.drawPath(path, fill); c.drawPath(path, fill);
line.setColor(BAT_EDGE);
line.setStrokeWidth(1.5f * u);
c.drawPath(path, line);
} }
// ---------- Ghost: only where there is room ---------- // ---------- Ghost: only where there is room ----------
@@ -334,10 +339,9 @@ final class HalloweenView extends ClusterCanvasView {
fill.setColor(burn); fill.setColor(burn);
c.drawPath(path, fill); c.drawPath(path, fill);
c.restore(); c.restore();
// Two teeth at the grin's corners, clear of the digits. // Two teeth at the grin's corners, clear of the digits, each the colour of the rib it hangs from.
fill.setColor(PUMPKIN); tooth(c, cx, -120f * u, gw, gt, 26f * u, 17f * u, gt + 40f * u, PUMPKIN, u);
tooth(c, cx - 120f * u, gt + 12f * u, 15f * u, 28f * u); tooth(c, cx, 120f * u, gw, gt, 26f * u, 17f * u, gt + 40f * u, PUMPKIN_LIT, u);
tooth(c, cx + 120f * u, gt + 12f * u, 15f * u, 28f * u);
label(c, fmt(v.speedKmh, "%.0f", "—"), cx, gb - 18f * u, 92f * u, INK, serif, Paint.Align.CENTER); label(c, fmt(v.speedKmh, "%.0f", "—"), cx, gb - 18f * u, 92f * u, INK, serif, Paint.Align.CENTER);
label(c, "KM/H", cx + 80f * u, cy + 64f * u, 16f * u, INK, face, Paint.Align.LEFT); label(c, "KM/H", cx + 80f * u, cy + 64f * u, 16f * u, INK, face, Paint.Align.LEFT);
// The power under the pumpkin, above the ridge. // The power under the pumpkin, above the ridge.
@@ -359,14 +363,27 @@ final class HalloweenView extends ClusterCanvasView {
c.drawPath(path, line); c.drawPath(path, line);
} }
private void tooth(Canvas c, float x, float y, float halfW, float tall) { /** A triangle hanging from the grin's upper lip (a curve sagging by {@code sag}) to {@code tipY}. */
private void tooth(Canvas c, float cx, float dx, float gw, float gt, float sag, float halfW, float tipY, int colour, float u) {
float left = lip(dx - halfW, gw, gt, sag), right = lip(dx + halfW, gw, gt, sag);
path.reset(); path.reset();
path.moveTo(x - halfW, y - tall * 0.3f); // A little above the lip, so none of its dark edge shows across the tooth's root.
path.lineTo(x + halfW, y - tall * 0.3f); path.moveTo(cx + dx - halfW, left - 1.5f * u);
path.lineTo(x + halfW, y + tall * 0.7f); path.lineTo(cx + dx + halfW, right - 1.5f * u);
path.lineTo(x - halfW, y + tall * 0.7f); path.lineTo(cx + dx, tipY);
path.close(); path.close();
fill.setColor(colour);
c.drawPath(path, fill); c.drawPath(path, fill);
line.setColor(0xFF4A1F04);
line.setStrokeWidth(3f * u);
c.drawLine(cx + dx - halfW, left + 2f * u, cx + dx, tipY, line);
c.drawLine(cx + dx + halfW, right + 2f * u, cx + dx, tipY, line);
}
/** The height of the grin's upper lip {@code dx} from the middle. */
private static float lip(float dx, float gw, float gt, float sag) {
float t = (gw - Math.abs(dx)) / (2f * gw);
return gt + 2f * t * (1f - t) * sag;
} }
// ---------- Left: the sign under the cobweb ---------- // ---------- Left: the sign under the cobweb ----------
Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 KiB

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

After

Width:  |  Height:  |  Size: 304 KiB