App 2.29.0: a chibi Mascot, chosen in Settings beside the tyre unit
ChibiFigure draws a small, full-figure co-pilot in a hoodie whose whole body acts the mood (waving, arms up and hopping, fist, hands on cheeks, nose bubble, pointing back, leaning into the wind). Settings → Mascot (Classic / Chibi, MascotCharacter) shows only for the Mascot style; the chibi has lines of her own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
@@ -24,14 +24,20 @@ import android.os.SystemClock;
|
||||
* power, 5-minute usage and the trip; the clock top right, left of the full-screen button.
|
||||
* Unread values show "—".
|
||||
*
|
||||
* Two characters ({@link MascotCharacter}): this classic co-pilot, or the chibi drawn by
|
||||
* {@link ChibiFigure}, each with her own lines.
|
||||
*
|
||||
* Drawn on a 100 × 100 grid scaled to the pane. Nothing animates on its own: blinking, the Zs,
|
||||
* sparkles and wind move because readings redraw the view every 0.2 s.
|
||||
*/
|
||||
final class MascotView extends ClusterCanvasView {
|
||||
|
||||
private static final int[] LINES = {
|
||||
R.array.mascot_waiting, R.array.mascot_reverse, R.array.mascot_worried, R.array.mascot_sleeping,
|
||||
R.array.mascot_cheering, R.array.mascot_determined, R.array.mascot_fast, R.array.mascot_content };
|
||||
/** Her lines per {@link MascotCharacter}, then per {@link MascotMood}, in their orders. */
|
||||
private static final int[][] LINES = {
|
||||
{ R.array.mascot_waiting, R.array.mascot_reverse, R.array.mascot_worried, R.array.mascot_sleeping,
|
||||
R.array.mascot_cheering, R.array.mascot_determined, R.array.mascot_fast, R.array.mascot_content },
|
||||
{ R.array.chibi_waiting, R.array.chibi_reverse, R.array.chibi_worried, R.array.chibi_sleeping,
|
||||
R.array.chibi_cheering, R.array.chibi_determined, R.array.chibi_fast, R.array.chibi_content } };
|
||||
|
||||
private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint stroke = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
@@ -39,7 +45,9 @@ final class MascotView extends ClusterCanvasView {
|
||||
private final Path p = new Path();
|
||||
private final RectF r = new RectF();
|
||||
private final MascotMood.Holder holder = new MascotMood.Holder();
|
||||
private final String[][] lines = new String[LINES.length][];
|
||||
private final String[][][] lines = new String[LINES.length][][];
|
||||
private final ChibiFigure chibi;
|
||||
private MascotCharacter character = MascotCharacter.CLASSIC;
|
||||
private final int accent, accentDark, accentLight, regen, warn, white, grey, track;
|
||||
private final int hair = 0xFF2E2440, hairLight = 0xFF5A4880, skin = 0xFFFBE2D0, skinShade = 0xFFE9BFA9,
|
||||
iris = 0xFF2F8F9D, pupil = 0xFF123238, lash = 0xFF221A2E, lips = 0xFFC2606A, mouth = 0xFF8E3A48,
|
||||
@@ -56,7 +64,11 @@ final class MascotView extends ClusterCanvasView {
|
||||
white = context.getColor(R.color.ev_text_primary);
|
||||
grey = context.getColor(R.color.ev_text_secondary);
|
||||
track = context.getColor(R.color.ev_outline);
|
||||
for (int i = 0; i < LINES.length; i++) lines[i] = context.getResources().getStringArray(LINES[i]);
|
||||
for (int i = 0; i < LINES.length; i++) {
|
||||
lines[i] = new String[LINES[i].length][];
|
||||
for (int j = 0; j < LINES[i].length; j++) lines[i][j] = context.getResources().getStringArray(LINES[i][j]);
|
||||
}
|
||||
chibi = new ChibiFigure(accent, accentLight, regen);
|
||||
stroke.setStyle(Paint.Style.STROKE);
|
||||
stroke.setStrokeCap(Paint.Cap.ROUND);
|
||||
stroke.setStrokeJoin(Paint.Join.ROUND);
|
||||
@@ -64,6 +76,12 @@ final class MascotView extends ClusterCanvasView {
|
||||
text.setFontFeatureSettings("tnum");
|
||||
}
|
||||
|
||||
/** Who sits beside the values (Settings → Mascot, shown for this style only). */
|
||||
void setCharacter(MascotCharacter character) {
|
||||
this.character = character == null ? MascotCharacter.CLASSIC : character;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas c) {
|
||||
super.onDraw(c);
|
||||
@@ -80,11 +98,14 @@ final class MascotView extends ClusterCanvasView {
|
||||
c.save();
|
||||
c.translate(ox, oy);
|
||||
c.scale(k, k);
|
||||
drawCharacter(c, mood, t);
|
||||
boolean chibiShown = character == MascotCharacter.CHIBI;
|
||||
if (chibiShown) chibi.draw(c, mood, t, t % 4200 < 260);
|
||||
else drawCharacter(c, mood, t);
|
||||
c.restore();
|
||||
|
||||
float x0 = ox + 78f * k;
|
||||
drawBubble(c, mood, t, x0, h, ox + 70f * k, oy + 46f * k, w);
|
||||
float tipX = chibiShown ? ChibiFigure.TIP_X : 70f, tipY = chibiShown ? ChibiFigure.TIP_Y : 46f;
|
||||
drawBubble(c, mood, t, x0, h, ox + tipX * k, oy + tipY * k, w);
|
||||
drawValues(c, x0, ox + 93f * k, w, h);
|
||||
}
|
||||
|
||||
@@ -420,7 +441,7 @@ final class MascotView extends ClusterCanvasView {
|
||||
// ---------- Speech bubble and values, in pixels ----------
|
||||
|
||||
private void drawBubble(Canvas c, MascotMood mood, long t, float x0, float h, float tipX, float tipY, float w) {
|
||||
String[] options = lines[mood.ordinal()];
|
||||
String[] options = lines[character.ordinal()][mood.ordinal()];
|
||||
String line = options.length == 0 ? "" : options[(int) ((t / 30000 + mood.ordinal()) % options.length)];
|
||||
text.setTextAlign(Paint.Align.LEFT);
|
||||
float pad = h * 0.045f, room = w - 140f * dp - x0 - 2f * pad;
|
||||
|
||||
Reference in New Issue
Block a user