App 2.14.0: accent colour after the MG4 paints; car colour probe

The accent is selectable in Settings → Display: Dynamic Red (default),
Volcano Orange, Holborn Blue, Racing Green, Camden Grey, Monument
Silver, Arctic White and Black Pearl. White, silver and black cannot be
an accent on the black screen as they are, so they become light greys
and a pearl blue-grey.

The accent palette is now theme attributes (attrs_accent.xml) with one
overlay per paint (accents.xml), applied before the activity inflates;
layouts, colour lists, drawables and the views that draw in code use
them, including the red tints that were hard-coded. A change recreates
the activity on the same page. Android 9 crashes on gradients with
theme colours, so the battery card is built from layers.

Logs → "Look for the car colour" lists the head unit's system
properties that mention colour, paint or car model, to find out whether
the paint colour can be read from the car.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
2026-10-18 13:52:57 +02:00
co-authored by Claude Opus 5.5
parent 40906ec4a7
commit 9b5fcc0734
41 changed files with 425 additions and 74 deletions
@@ -227,11 +227,13 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
super.onCreate(savedInstanceState);
prefs = getSharedPreferences("abrp_prefs", MODE_PRIVATE);
// Before anything is inflated: every ?attr/evAccent… resolves against this overlay.
getTheme().applyStyle(AccentColor.fromPref(prefs.getString(AccentColor.PREF_KEY, null)).overlay, true);
setContentView(R.layout.activity_main);
((TextView) findViewById(R.id.version_badge)).setText("v" + BuildConfig.VERSION_NAME);
prefs = getSharedPreferences("abrp_prefs", MODE_PRIVATE);
// The pages are inflated here, once, and handed to the pager as fixed,
// non-recycled items. That is what lets every widget below be looked up now and
@@ -259,9 +261,20 @@ public class MainActivity extends AppCompatActivity {
statusText = servicePane.findViewById(R.id.status_text);
callLogText = logPane.findViewById(R.id.call_log_text);
steeringKeysText = logPane.findViewById(R.id.steering_keys_text);
TextView carColorText = logPane.findViewById(R.id.car_color_text);
logPane.findViewById(R.id.car_color_button).setOnClickListener(v -> new Thread(() -> {
java.util.List<String> found = CarColorProbe.run(60);
String shown = found.isEmpty() ? getString(R.string.car_color_none)
: android.text.TextUtils.join("\n", found);
runOnUiThread(() -> carColorText.setText(shown));
}, "car-color-probe").start());
logPane.findViewById(R.id.flush_now_button).setOnClickListener(v -> manualFlush());
setUpPager();
// Back on the page that was open when the activity was recreated (accent colour change).
if (savedInstanceState != null) {
((ViewPager2) findViewById(R.id.content)).setCurrentItem(savedInstanceState.getInt(STATE_PAGE, 0), false);
}
findViewById(R.id.about_button).setOnClickListener(v -> showAbout());
findViewById(R.id.update_button).setOnClickListener(v -> checkForUpdate(v));
@@ -275,6 +288,14 @@ public class MainActivity extends AppCompatActivity {
startForegroundService(new Intent(this, AbrpUploadService.class));
}
private static final String STATE_PAGE = "page";
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_PAGE, ((ViewPager2) findViewById(R.id.content)).getCurrentItem());
}
private void showAbout() {
String version;
try {
@@ -710,7 +731,7 @@ public class MainActivity extends AppCompatActivity {
code.setTypeface(android.graphics.Typeface.MONOSPACE, android.graphics.Typeface.BOLD);
code.setTextSize(36);
code.setLetterSpacing(0.15f);
code.setTextColor(getColor(R.color.ev_cyan));
code.setTextColor(AccentColor.resolve(MainActivity.this, R.attr.evAccentLight));
status = new TextView(MainActivity.this);
status.setTextAppearance(R.style.Text_EV_Body);
status.setText(startingText);
@@ -978,6 +999,7 @@ public class MainActivity extends AppCompatActivity {
voiceKeySwitch.setOnCheckedChangeListener((btn, checked) ->
prefs.edit().putBoolean(UploadSettings.KEY_VOICE_KEY_OPENS, checked).apply());
bindTireUnit();
bindAccentColor();
fullScreenSwitch = settingsPane.findViewById(R.id.full_screen_switch);
fullScreenSwitch.setChecked(UploadSettings.fullScreen(prefs));
fullScreenSwitch.setOnCheckedChangeListener((btn, checked) -> {
@@ -1058,6 +1080,29 @@ public class MainActivity extends AppCompatActivity {
}, "abrp-test").start();
}
/** The accent colour; a change recreates the activity so every view takes it. */
private void bindAccentColor() {
android.widget.Spinner spinner = settingsPane.findViewById(R.id.accent_spinner);
AccentColor[] colors = AccentColor.values();
String[] labels = new String[colors.length];
for (int i = 0; i < colors.length; i++) labels[i] = colors[i].label;
android.widget.ArrayAdapter<String> adapter = new android.widget.ArrayAdapter<>(
this, R.layout.spinner_value, labels);
adapter.setDropDownViewResource(R.layout.spinner_dropdown);
spinner.setAdapter(adapter);
AccentColor current = AccentColor.fromPref(prefs.getString(AccentColor.PREF_KEY, null));
spinner.setSelection(current.ordinal());
spinner.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
if (colors[position] == current) return;
prefs.edit().putString(AccentColor.PREF_KEY, colors[position].name()).apply();
recreate();
}
@Override public void onNothingSelected(android.widget.AdapterView<?> parent) { }
});
}
/** Tyre pressure unit: display only, read on every refresh of the Overview. */
private void bindTireUnit() {
android.widget.Spinner spinner = settingsPane.findViewById(R.id.tire_unit_spinner);
@@ -1388,9 +1433,10 @@ public class MainActivity extends AppCompatActivity {
MaterialButton button = consumptionPane.findViewById(id);
boolean selected = id == selectedId;
button.setSelected(selected);
button.setTextColor(selected ? 0xFFE0424C : COLOR_PENDING);
int accent = AccentColor.resolve(this, R.attr.evAccent);
button.setTextColor(selected ? accent : COLOR_PENDING);
button.setStrokeColor(android.content.res.ColorStateList.valueOf(
selected ? 0xFFE0424C : 0xFF3A4143));
selected ? accent : 0xFF3A4143));
}
}