Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9813c2afd | ||
|
|
3a532cd498 |
@@ -140,6 +140,8 @@ 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-11-02 — App 2.45.2: Halloween's song banner **waves** (owner: "Can it also be a bit wavy?"): waves run from the bat to the tail, still where the bat holds it and up to 7 units at the tail, and the text follows them (`drawTextOnPath`). Tested: emulator with the throwaway fake session, unit tests, lint. Not tried on the car.
|
||||
- 2026-11-02 — App 2.45.1 published (`car-2.45.1`).
|
||||
- 2026-11-02 — App 2.45.1: **Halloween's song flies through the sky** (owner: "Instead of taking up screen space on the bottom, can we somehow include the title in the theme itself? ... have text fly through the air"). A bat tows a parchment banner with "♪ title — artist" from left to right across the strip of sky above the sign, behind the moon, one pass about every half minute, again and again while something plays (the title is cut first so the artist stays); the line at the foot of the hill is gone. While a banner flies the view draws about 30 frames a second, as it does while driving. Tested: emulator with the throwaway fake session (banner clear of the sign along the whole pass), unit tests, lint. Not tried on the car.
|
||||
- 2026-11-02 — App 2.45.0 published (`car-2.45.0`).
|
||||
- 2026-11-02 — App 2.45.0: **now playing on the Overview** (owner: "can we also implement music titles etc in our app?"; display only, Tiles and Halloween first). `NowPlayingWatcher` follows the active media sessions as the music-sources check reads them (`MediaSessionManager`, MEDIA_CONTENT_CONTROL as the system user), with a change listener and a callback per session, so reading costs no call into another app; it never connects to a media browser service (2.42.0) and sends no command. It runs only while the Overview is on screen in Tiles or Halloween with Settings → "Show what's playing" on (**on by default**, `PREF_NOW_PLAYING`). `NowPlaying` (pure, unit-tested) picks the session that plays, the one updated last of several, else the one paused last, passing over sessions without text (Bluetooth's empty one); CarPlay's YouTube titles lose their `[videoID]`; the radio shows its text (the artist field, e.g. "BLIND MELON - NO RAIN") with the station below. Tiles: the empty slot under the battery shows source ("· paused" when paused), title, artist, a cover (the session's picture, or Bluetooth's `ALBUM_ART_URI` file, read small off the main thread) and how far the song is; invisible when nothing plays; the narrow layout has no room and shows none. Halloween: "♪ title — artist" along the foot of the hill, the title cut first. Titles stay on screen: not logged, not sent. Tested: unit tests, lint, emulator with a throwaway build publishing a fake session (CarPlay-like title with ID removed, cover, bar; Halloween line; the switch hides and brings back both). With Halloween chosen the Settings card has two switches more and scrolls further. Not tried on the car: whether the car's sessions, Bluetooth's cover file and the radio text show as in the music-sources checks.
|
||||
|
||||
@@ -267,7 +267,7 @@ you drive the bats fly, the potion bubbles and the ghost floats (where there is
|
||||
this screen). With "Pumpkin light follows the music" on (Settings, shown with this style; off by
|
||||
default) and Bluetooth or CarPlay music playing, the pumpkin's glow flares and its carving
|
||||
brightens on each kick drum; the radio cannot be heard, so it burns as usual then. What plays
|
||||
(Bluetooth, CarPlay or the radio) flies across the top of the sky on a parchment banner that a
|
||||
(Bluetooth, CarPlay or the radio) flies across the top of the sky on a waving parchment banner that a
|
||||
bat tows, behind the moon, again and again while it plays. The app turns the
|
||||
pumpkin's orange with it.
|
||||
|
||||
|
||||
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
applicationId "com.stinnesbeck.nilsmg4"
|
||||
minSdk 27
|
||||
targetSdk 36
|
||||
versionCode 161
|
||||
versionName "2.45.1"
|
||||
versionCode 162
|
||||
versionName "2.45.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.
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PathMeasure;
|
||||
import android.graphics.RadialGradient;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
@@ -50,6 +51,8 @@ final class HalloweenView extends ClusterCanvasView {
|
||||
private final Paint image = new Paint(Paint.FILTER_BITMAP_FLAG);
|
||||
private final Paint glow = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Path path = new Path();
|
||||
private final Path baseline = new Path();
|
||||
private final PathMeasure measure = new PathMeasure();
|
||||
private final RectF r = new RectF();
|
||||
private final Typeface serif = Typeface.create("serif", Typeface.BOLD);
|
||||
private final Typeface face = Typeface.create("sans-serif-condensed", Typeface.BOLD);
|
||||
@@ -280,7 +283,7 @@ final class HalloweenView extends ClusterCanvasView {
|
||||
// ---------- What plays: a bat tows it across the sky on a banner ----------
|
||||
|
||||
/**
|
||||
* A bat pulls a parchment banner with the song from left to right, high in the sky, behind the
|
||||
* A bat pulls a waving parchment banner with the song from left to right, high in the sky, behind the
|
||||
* sign and the moon, again and again while something plays. Returns whether it is drawn, so
|
||||
* the view keeps animating for it.
|
||||
*/
|
||||
@@ -300,20 +303,33 @@ final class HalloweenView extends ClusterCanvasView {
|
||||
// In the strip of sky above the sign, clear of it.
|
||||
float yc = 34f * u + (float) Math.sin(t * 1.3f) * 4f * u;
|
||||
float top = yc - bh / 2f, bottom = yc + bh / 2f, right = bx + bw, notch = 12f * u;
|
||||
// The ribbon, its trailing end cut as a swallowtail.
|
||||
// The ribbon, waving in the wind: still where the bat holds it, rippling more towards its
|
||||
// tail, which is cut as a swallowtail. The text follows the waves.
|
||||
int steps = Math.max(8, Math.round(bw / (12f * u)));
|
||||
path.reset();
|
||||
path.moveTo(bx, top);
|
||||
path.lineTo(right, top);
|
||||
path.lineTo(right, bottom);
|
||||
path.lineTo(bx, bottom);
|
||||
path.lineTo(bx + notch, yc);
|
||||
for (int i = 0; i <= steps; i++) {
|
||||
float x = right - bw * i / steps;
|
||||
if (i == 0) path.moveTo(x, top + wave(x, right, bw, t, u));
|
||||
else path.lineTo(x, top + wave(x, right, bw, t, u));
|
||||
}
|
||||
path.lineTo(bx + notch, yc + wave(bx + notch, right, bw, t, u));
|
||||
for (int i = steps; i >= 0; i--) path.lineTo(right - bw * i / steps, bottom + wave(right - bw * i / steps, right, bw, t, u));
|
||||
path.close();
|
||||
fill.setColor(BONE);
|
||||
c.drawPath(path, fill);
|
||||
line.setColor(0xFF8A6A3A);
|
||||
line.setStrokeWidth(2f * u);
|
||||
c.drawPath(path, line);
|
||||
label(c, s, bx + notch + (bw - notch) / 2f, yc + 7f * u, 19f * u, INK, face, Paint.Align.CENTER);
|
||||
baseline.reset();
|
||||
for (int i = 0; i <= steps; i++) {
|
||||
float x = bx + notch + (bw - notch) * i / steps;
|
||||
if (i == 0) baseline.moveTo(x, yc + wave(x, right, bw, t, u));
|
||||
else baseline.lineTo(x, yc + wave(x, right, bw, t, u));
|
||||
}
|
||||
measure.setPath(baseline, false);
|
||||
text.setColor(INK);
|
||||
text.setTextAlign(Paint.Align.LEFT);
|
||||
c.drawTextOnPath(s, baseline, Math.max(0f, (measure.getLength() - text.measureText(s)) / 2f), 7f * u, text);
|
||||
// The tow lines, and the bat at their end.
|
||||
float batX = right + tow, batY = yc - 10f * u;
|
||||
line.setColor(BAT_EDGE);
|
||||
@@ -324,6 +340,15 @@ final class HalloweenView extends ClusterCanvasView {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* How far the banner bends at {@code x}: nothing at the bat's end ({@code right}), up to 7 units
|
||||
* at the tail, in waves that run from the bat to the tail.
|
||||
*/
|
||||
private static float wave(float x, float right, float bw, float t, float u) {
|
||||
float along = right - x;
|
||||
return 7f * u * (along / bw) * (float) Math.sin(along / (170f * u) * 2f * Math.PI - t * 5f);
|
||||
}
|
||||
|
||||
/** {@code s} cut with "…" to fit {@code room} in the text paint as set. */
|
||||
private String cut(String s, float room) {
|
||||
if (text.measureText(s) <= room) return s;
|
||||
|
||||
Reference in New Issue
Block a user