App 2.28.0: Overview style Mascot, an anime co-pilot who reacts to the drive

MascotView draws her head and shoulders beside speed, gear, battery,
power, usage and trip. MascotMood picks her expression from the reading
(waiting, reverse, worried, asleep, cheering, determined, fast, content)
and holds a new mood for 1.5 s so values at a threshold do not make her
flicker. A speech bubble carries a line per mood.

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-24 23:07:50 +02:00
co-authored by Claude Opus 5.5
parent 965a20cfb2
commit 57cabfa0e1
11 changed files with 704 additions and 8 deletions
@@ -0,0 +1,63 @@
package com.evsuite.abrp;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/** The Mascot style's moods — pure JVM. */
public class MascotMoodTest {
private static ClusterValues values(Integer gear, Float speed, Float soc, Float power) {
ClusterValues v = new ClusterValues();
v.gear = gear;
v.speedKmh = speed;
v.socPercent = soc;
v.powerKw = power;
return v;
}
@Test
public void nothingReadIsWaiting() {
assertEquals(MascotMood.WAITING, MascotMood.of(null));
assertEquals(MascotMood.WAITING, MascotMood.of(new ClusterValues()));
}
@Test
public void moodsByPriority() {
assertEquals(MascotMood.REVERSE, MascotMood.of(values(2, 3f, 10f, 5f)));
assertEquals(MascotMood.WORRIED, MascotMood.of(values(4, 50f, 15f, 10f)));
assertEquals(MascotMood.WORRIED, MascotMood.of(values(1, 0f, 15f, 0f)));
assertEquals(MascotMood.SLEEPING, MascotMood.of(values(1, 0f, 80f, 0f)));
assertEquals(MascotMood.CHEERING, MascotMood.of(values(4, 120f, 80f, -20f)));
assertEquals(MascotMood.DETERMINED, MascotMood.of(values(4, 60f, 80f, 90f)));
assertEquals(MascotMood.FAST, MascotMood.of(values(4, 110f, 80f, 25f)));
assertEquals(MascotMood.CONTENT, MascotMood.of(values(4, 50f, 80f, 10f)));
assertEquals(MascotMood.CONTENT, MascotMood.of(values(3, 0f, 80f, 0f)));
}
@Test
public void unreadValuesAreNoReason() {
// Speed unread, battery unread: in D she is content, not worried or fast.
assertEquals(MascotMood.CONTENT, MascotMood.of(values(4, null, null, 10f)));
assertEquals(MascotMood.CONTENT, MascotMood.of(values(null, 50f, null, null)));
}
@Test
public void holderWaitsOutShortChanges() {
MascotMood.Holder holder = new MascotMood.Holder();
assertEquals(MascotMood.CONTENT, holder.update(MascotMood.CONTENT, 0));
assertEquals(MascotMood.CONTENT, holder.update(MascotMood.CHEERING, 100));
assertEquals(MascotMood.CONTENT, holder.update(MascotMood.CONTENT, 300));
assertEquals(MascotMood.CONTENT, holder.update(MascotMood.CHEERING, 400));
assertEquals(MascotMood.CONTENT, holder.update(MascotMood.CHEERING, 1800));
assertEquals(MascotMood.CHEERING, holder.update(MascotMood.CHEERING, 1900));
}
@Test
public void reverseAndWaitingShowAtOnce() {
MascotMood.Holder holder = new MascotMood.Holder();
holder.update(MascotMood.SLEEPING, 0);
assertEquals(MascotMood.REVERSE, holder.update(MascotMood.REVERSE, 10));
assertEquals(MascotMood.WAITING, holder.update(MascotMood.WAITING, 20));
}
}
@@ -36,6 +36,7 @@ public class OverviewStyleTest {
assertTrue(OverviewStyle.TERMINAL.hasOriginal);
assertFalse(OverviewStyle.CARTOON.usesAccent);
assertFalse(OverviewStyle.NIXIE.usesAccent);
assertTrue(OverviewStyle.MASCOT.usesAccent);
assertEquals("original_colours_RETRO", OverviewStyle.RETRO.originalKey());
}
}