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:
2026-10-24 23:17:21 +02:00
co-authored by Claude Opus 5.5
parent 57cabfa0e1
commit 7df1930edd
12 changed files with 542 additions and 15 deletions
@@ -69,6 +69,7 @@ public class MainActivity extends AppCompatActivity {
/** The styles built on {@link ClusterCanvasView}, fed together. */
private final java.util.List<ClusterCanvasView> canvasStyles = new java.util.ArrayList<>();
private TerminalView terminalView;
private MascotView mascotView;
/** The pane of each Overview style, by its {@link OverviewStyle} ordinal. */
private View[] stylePanes;
private View consumptionPane;
@@ -284,8 +285,10 @@ public class MainActivity extends AppCompatActivity {
View sciFiPane = drawnPane(sciFiView), cartoonPane = drawnPane(cartoonView),
blueprintPane = drawnPane(blueprintView), mgPane = drawnPane(octagonView);
terminalView = new TerminalView(this);
mascotView = new MascotView(this);
mascotView.setCharacter(MascotCharacter.fromPref(prefs.getString(MascotCharacter.PREF_KEY, null)));
java.util.Collections.addAll(canvasStyles, new MinimalView(this), terminalView,
new AviationView(this), new NixieView(this), new ChronographView(this), new MascotView(this));
new AviationView(this), new NixieView(this), new ChronographView(this), mascotView);
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,
@@ -1120,6 +1123,7 @@ public class MainActivity extends AppCompatActivity {
voiceKeySwitch.setOnCheckedChangeListener((btn, checked) ->
prefs.edit().putBoolean(UploadSettings.KEY_VOICE_KEY_OPENS, checked).apply());
bindTireUnit();
bindMascotCharacter();
bindOverviewStyle();
bindAccentColor();
fullScreenSwitch = settingsPane.findViewById(R.id.full_screen_switch);
@@ -1260,6 +1264,17 @@ public class MainActivity extends AppCompatActivity {
prefs.edit().putString(PressureUnit.PREF_KEY, units[position].name()).apply());
}
/** Who sits in the Mascot style; the drop-down shows only while that style is chosen. */
private void bindMascotCharacter() {
MascotCharacter[] characters = MascotCharacter.values();
String[] labels = { getString(R.string.mascot_character_classic), getString(R.string.mascot_character_chibi) };
int current = MascotCharacter.fromPref(prefs.getString(MascotCharacter.PREF_KEY, null)).ordinal();
bindChoices(settingsPane.findViewById(R.id.mascot_choices), labels, current, position -> {
prefs.edit().putString(MascotCharacter.PREF_KEY, characters[position].name()).apply();
mascotView.setCharacter(characters[position]);
});
}
/** The service reads these settings on every tick, so a change applies without a restart. */
private void bindCadenceControls() {
UploadSettings current = UploadSettings.from(prefs);
@@ -1548,6 +1563,10 @@ public class MainActivity extends AppCompatActivity {
TextView note = settingsPane.findViewById(R.id.accent_note);
note.setVisibility(style.usesAccent ? View.GONE : View.VISIBLE);
note.setText(getString(R.string.accent_not_used, styleLabels()[style.ordinal()]));
// The wide layout keeps the Mascot field's place so the page does not jump.
View mascot = settingsPane.findViewById(R.id.mascot_field);
boolean wide = getResources().getConfiguration().screenWidthDp >= 1000;
mascot.setVisibility(style == OverviewStyle.MASCOT ? View.VISIBLE : wide ? View.INVISIBLE : View.GONE);
}
/** The drop-down's labels, by {@link OverviewStyle} ordinal. */