diff --git a/AGENTS.md b/AGENTS.md
index b1742ec..fb9fe76 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -69,7 +69,7 @@ Repository: `ssh://git@git.stinnesbeck.com:2222/nils/mg4.git` (remote `origin`),
| Tab | Contents |
|---|---|
-| Overview | value tiles (battery, range, range/usage/energy since start, 5-min averages of range, usage, power, speed), vehicle info (odometer, lease allowance and the odometer against it), tyre pressures; read every 0.2 s on a background thread. Styles (Settings → Overview style): Tiles, Gauges (`GaugeView` needles), Cockpit (`RingView` rings, `TraceView` power trace), Retro (`RetroView`, seven-segment VFD, classic or in the accent colour), Futuristic (`FlowView`, animated energy flow), Sci-fi (`SciFiView`, starship console), Cartoon (`CartoonView`), Blueprint (`BlueprintView`, top-view drawing with tyre pressures); the drawn styles share `ClusterValues` |
+| Overview | value tiles (battery, range, range/usage/energy since start, 5-min averages of range, usage, power, speed), vehicle info (odometer, lease allowance and the odometer against it), tyre pressures; read every 0.2 s on a background thread. Styles (Settings → Overview style): Tiles, Gauges (`GaugeView` needles), Cockpit (`RingView` rings, `TraceView` power trace), Retro (`RetroView`, seven-segment VFD, classic or in the accent colour), Futuristic (`FlowView`, animated energy flow), Sci-fi (`SciFiView`, starship console), Cartoon (`CartoonView`), Blueprint (`BlueprintView`, top-view drawing with tyre pressures), MG (`OctagonView`, octagonal dials after the classic MG dashboards); the drawn styles share `ClusterValues` |
| Consumption / Charging | period totals, daily log, charging sessions and curve |
| Live data | on/off, ABRP API key and user token, upload frequency |
| Server | URL, pairing by QR, ingest token, installation ID (read-only), config import |
diff --git a/README.md b/README.md
index ca6b1c2..5c5f52a 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ with the server using a token of its own, obtained by pairing with a phone (belo
| Tab | What it shows |
|---|---|
-| **Overview** | Battery, range, power and speed; range, usage and energy since start; 5-minute averages of range, usage, power and speed; vehicle info (odometer, climate setpoint) and tyre pressures. Read five times a second. Looks to choose from in Settings → Overview style: Tiles, Gauges, Cockpit, Retro (classic turquoise or in your accent colour), Futuristic, Sci-fi, Cartoon and Blueprint |
+| **Overview** | Battery, range, power and speed; range, usage and energy since start; 5-minute averages of range, usage, power and speed; vehicle info (odometer, climate setpoint) and tyre pressures. Read five times a second. Looks to choose from in Settings → Overview style: Tiles, Gauges, Cockpit, Retro (classic turquoise or in your accent colour), Futuristic, Sci-fi, Cartoon, Blueprint and MG |
| **Consumption** | Lifetime, last 7 and 30 days, Trip A and B (distance, energy, average consumption and speed, SOC change), and a daily log of the last four months |
| **Charging** | The current session (SOC, voltage, current, power, energy into the battery and from the grid, time, cost) and its SOC/power curve |
| **Live data** | Live data on/off, ABRP credentials for sending directly from the car, upload frequency |
diff --git a/SPECS.md b/SPECS.md
index 0d3a72f..9f7d907 100644
--- a/SPECS.md
+++ b/SPECS.md
@@ -131,6 +131,16 @@ 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-10-24 — App 2.25.0 (branch `styles-retro-future`): Overview style "MG" (`OctagonView`),
+ after the classic MG dashboards whose dials sat in octagonal bezels — not a copy of the MG4's
+ own driver display, and without MG's octagon logo. Three octagonal dials with chrome bezels,
+ cream scales and needles in the accent colour: speed in the middle (digits and P R N D under
+ the hub), battery E…F on the left (red zone below 20 %, range), power −50…150 kW on the right
+ (green charge zone below zero). A cream line below: odometer, trip, 5-minute usage, climate,
+ clock, separated by small accent octagons. Needles ease and redraw only until settled, only
+ on screen; unread values draw no needle and "—". Palette `mg_*`. Checked on the emulator with
+ sample values (`screenshots/nilsmg4-2.25.0-mg.png`) and without data; no crash. Not tried on
+ the car.
- 2026-10-24 — App 2.24.1 (branch `styles-retro-future`): the line of text above every page
("Your drive at a glance.", "Settings. Display, battery…" and so on) is gone, at the owner's
request; the pages start at the top, level with the sidebar, and gain about 45 dp. Its 14
diff --git a/app/build.gradle b/app/build.gradle
index 4dc7472..2d1bcd3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -34,8 +34,8 @@ android {
applicationId "com.stinnesbeck.nilsmg4"
minSdk 27
targetSdk 36
- versionCode 118
- versionName "2.24.1"
+ versionCode 119
+ versionName "2.25.0"
// 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.
diff --git a/app/src/main/java/com/evsuite/abrp/MainActivity.java b/app/src/main/java/com/evsuite/abrp/MainActivity.java
index 31b53e0..45bbdf2 100644
--- a/app/src/main/java/com/evsuite/abrp/MainActivity.java
+++ b/app/src/main/java/com/evsuite/abrp/MainActivity.java
@@ -65,6 +65,7 @@ public class MainActivity extends AppCompatActivity {
private SciFiView sciFiView;
private CartoonView cartoonView;
private BlueprintView blueprintView;
+ private OctagonView octagonView;
/** The pane of each Overview style, by its index in {@link #OVERVIEW_STYLES}. */
private View[] stylePanes;
private View consumptionPane;
@@ -250,10 +251,11 @@ public class MainActivity extends AppCompatActivity {
sciFiView = new SciFiView(this, null);
cartoonView = new CartoonView(this, null);
blueprintView = new BlueprintView(this, null);
+ octagonView = new OctagonView(this, null);
View sciFiPane = drawnPane(sciFiView), cartoonPane = drawnPane(cartoonView),
- blueprintPane = drawnPane(blueprintView);
+ blueprintPane = drawnPane(blueprintView), mgPane = drawnPane(octagonView);
stylePanes = new View[] { vehiclePane, gaugesPane, cockpitPane, retroPane, retroPane,
- futuristicPane, sciFiPane, cartoonPane, blueprintPane };
+ futuristicPane, sciFiPane, cartoonPane, blueprintPane, mgPane };
// The Overview page holds every style; Settings → Overview style picks the one shown.
android.widget.FrameLayout overviewPage = new android.widget.FrameLayout(this);
overviewPage.setLayoutParams(new android.view.ViewGroup.LayoutParams(
@@ -267,6 +269,7 @@ public class MainActivity extends AppCompatActivity {
overviewPage.addView(sciFiPane);
overviewPage.addView(cartoonPane);
overviewPage.addView(blueprintPane);
+ overviewPage.addView(mgPane);
consumptionPane = inflatePane(R.layout.pane_consumption);
chargingPane = inflatePane(R.layout.pane_charging);
settingsPane = inflatePane(R.layout.pane_settings);
@@ -1451,7 +1454,7 @@ public class MainActivity extends AppCompatActivity {
/** The stored values of the Overview styles, in the order of the drop-down. */
private static final String[] OVERVIEW_STYLES = { "TILES", "GAUGES", "COCKPIT", "RETRO", "RETRO_ACCENT", "FUTURISTIC",
- "SCIFI", "CARTOON", "BLUEPRINT" };
+ "SCIFI", "CARTOON", "BLUEPRINT", "MG" };
/** The chosen style's index in {@link #OVERVIEW_STYLES}; anything unknown is Tiles. */
private int overviewStyle() {
@@ -1474,7 +1477,7 @@ public class MainActivity extends AppCompatActivity {
getString(R.string.overview_style_cockpit), getString(R.string.overview_style_retro),
getString(R.string.overview_style_retro_accent), getString(R.string.overview_style_futuristic),
getString(R.string.overview_style_scifi), getString(R.string.overview_style_cartoon),
- getString(R.string.overview_style_blueprint) };
+ getString(R.string.overview_style_blueprint), getString(R.string.overview_style_mg) };
bindChoices(settingsPane.findViewById(R.id.overview_style_choices), labels, overviewStyle(),
position -> {
prefs.edit().putString(PREF_OVERVIEW_STYLE, OVERVIEW_STYLES[position]).apply();
@@ -1536,6 +1539,7 @@ public class MainActivity extends AppCompatActivity {
sciFiView.setValues(v);
cartoonView.setValues(v);
blueprintView.setValues(v);
+ octagonView.setValues(v);
}
// ---------- Cockpit (Overview style) ----------
diff --git a/app/src/main/java/com/evsuite/abrp/OctagonView.java b/app/src/main/java/com/evsuite/abrp/OctagonView.java
new file mode 100644
index 0000000..ec9444b
--- /dev/null
+++ b/app/src/main/java/com/evsuite/abrp/OctagonView.java
@@ -0,0 +1,280 @@
+package com.evsuite.abrp;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.Locale;
+
+/**
+ * The MG Overview style, after the classic MG dashboards whose dials sat in octagonal bezels.
+ *
+ * Three octagonal dials with chrome bezels, cream scales and needles in the accent colour: the
+ * speedometer in the middle (with the speed as digits and P R N D underneath), the battery on
+ * the left (E to F, red below 20 %, range underneath) and power on the right (−50 to 150 kW,
+ * the charge zone below zero in green). Below, a cream line of odometer, trip, usage, climate
+ * and clock, separated by small octagons. Unread values draw no needle and "—".
+ *
+ * Needles ease to a new value and the view redraws only until they are there, only on screen.
+ * The MG octagon logo itself is not drawn.
+ */
+public final class OctagonView extends View {
+
+ private static final float START_DEG = 135f, SWEEP_DEG = 270f;
+ private static final long FRAME_MS = 33;
+
+ private final Paint face = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint chrome = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint tick = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint needle = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint zone = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint text = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Path path = new Path();
+ private final RectF rect = new RectF();
+ private final int accent, light, cream, chromeLight, chromeDark, faceColour, green, red;
+ private ClusterValues v = new ClusterValues();
+ /** The needles as shown, easing to their values; NaN until the first reading. */
+ private float speedShown = Float.NaN, socShown = Float.NaN, powerShown = Float.NaN;
+
+ public OctagonView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ accent = AccentColor.resolve(context, R.attr.evAccent);
+ light = AccentColor.resolve(context, R.attr.evAccentLight);
+ cream = context.getColor(R.color.mg_cream);
+ chromeLight = context.getColor(R.color.mg_chrome_light);
+ chromeDark = context.getColor(R.color.mg_chrome_dark);
+ faceColour = context.getColor(R.color.mg_face);
+ green = context.getColor(R.color.ev_ok);
+ red = context.getColor(R.color.ev_error);
+ face.setColor(faceColour);
+ chrome.setStyle(Paint.Style.STROKE);
+ chrome.setStrokeJoin(Paint.Join.ROUND);
+ tick.setColor(cream);
+ tick.setStrokeCap(Paint.Cap.ROUND);
+ needle.setStrokeCap(Paint.Cap.ROUND);
+ zone.setStyle(Paint.Style.STROKE);
+ text.setColor(cream);
+ text.setTextAlign(Paint.Align.CENTER);
+ text.setFontFeatureSettings("tnum");
+ text.setTypeface(Typeface.create("serif", Typeface.NORMAL));
+ }
+
+ void setValues(ClusterValues values) {
+ v = values == null ? new ClusterValues() : values;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas c) {
+ super.onDraw(c);
+ float w = getWidth(), h = getHeight();
+ if (w <= 0 || h <= 0) return;
+ float d = getResources().getDisplayMetrics().density;
+
+ speedShown = ease(speedShown, v.speedKmh, 180f);
+ socShown = ease(socShown, v.socPercent, 100f);
+ powerShown = ease(powerShown, v.powerKw, 200f);
+ boolean moving = (v.speedKmh != null && speedShown != v.speedKmh)
+ || (v.socPercent != null && socShown != v.socPercent)
+ || (v.powerKw != null && powerShown != v.powerKw);
+
+ float dialsBottom = h * 0.84f;
+ float big = Math.min(dialsBottom * 0.5f, w * 0.20f);
+ float small = big * 0.68f;
+ float cy = dialsBottom / 2f + 4f * d;
+ float cx = w / 2f;
+ float sideX = Math.min(w * 0.21f, cx - big - small - 20f * d);
+
+ // Battery, left.
+ bezel(c, sideX + small * 0.05f, cy + big - small, small, d);
+ dial(c, sideX + small * 0.05f, cy + big - small, small, 0f, 100f, 25f, 5,
+ v.socPercent == null ? null : socShown, 20f, false, new String[] { "E", "½", "F" }, d);
+ label(c, sideX + small * 0.05f, cy + big - small, small, "BATTERY",
+ v.socPercent == null ? "—" : String.format(Locale.US, "%.0f%%", v.socPercent),
+ v.rangeKm == null ? "— km" : String.format(Locale.US, "%.0f km", v.rangeKm), 0.20f);
+
+ // Power, right.
+ float rx = w - sideX - small * 0.05f;
+ bezel(c, rx, cy + big - small, small, d);
+ dial(c, rx, cy + big - small, small, -50f, 150f, 50f, 5,
+ v.powerKw == null ? null : powerShown, null, true, null, d);
+ label(c, rx, cy + big - small, small, "kW",
+ v.powerKw == null ? "—" : String.format(Locale.US, "%.1f", v.powerKw),
+ v.powerKw != null && v.powerKw < 0f ? "CHARGE" : "POWER", 0.20f);
+
+ // Speed, centre.
+ bezel(c, cx, cy, big, d);
+ dial(c, cx, cy, big, 0f, 180f, 20f, 4, v.speedKmh == null ? null : speedShown, null, false, null, d);
+ label(c, cx, cy, big, "km/h",
+ v.speedKmh == null ? "—" : String.format(Locale.US, "%.0f", v.speedKmh), null, 0.24f);
+ gears(c, cx, cy + big * 0.62f, big, d);
+
+ drawLine(c, w, h, d);
+
+ if (moving && isShown()) postInvalidateDelayed(FRAME_MS);
+ }
+
+ /** One step of a needle towards its value; jumps there when close or on the first value. */
+ private static float ease(float shown, Float target, float span) {
+ if (target == null || !Float.isFinite(target)) return shown;
+ if (Float.isNaN(shown)) return target;
+ float next = shown + (target - shown) * 0.25f;
+ return Math.abs(target - next) < span * 0.002f ? target : next;
+ }
+
+ /** A regular octagon with flat sides at top, bottom and both sides. */
+ private void octagon(float cx, float cy, float r) {
+ path.reset();
+ for (int i = 0; i < 8; i++) {
+ double a = Math.toRadians(22.5 + 45 * i);
+ float x = cx + r * (float) Math.cos(a), y = cy + r * (float) Math.sin(a);
+ if (i == 0) path.moveTo(x, y); else path.lineTo(x, y);
+ }
+ path.close();
+ }
+
+ /** The bezel: a dark face inside a chrome octagon, lit from above (light outer, dark inner line). */
+ private void bezel(Canvas c, float cx, float cy, float r, float d) {
+ octagon(cx, cy, r);
+ c.drawPath(path, face);
+ chrome.setColor(chromeLight);
+ chrome.setStrokeWidth(Math.max(3f * d, r * 0.035f));
+ c.drawPath(path, chrome);
+ octagon(cx, cy, r * 0.93f);
+ chrome.setColor(chromeDark);
+ chrome.setStrokeWidth(Math.max(2f * d, r * 0.02f));
+ c.drawPath(path, chrome);
+ octagon(cx, cy, r * 1.035f);
+ chrome.setColor(AccentColor.withAlpha(chromeLight, 0x40));
+ chrome.setStrokeWidth(d);
+ c.drawPath(path, chrome);
+ }
+
+ /**
+ * A scale inside the octagon, its numbers or end labels, an optional warning zone below
+ * {@code warnBelow} (red) or charge zone below zero (green), and the needle when known.
+ */
+ private void dial(Canvas c, float cx, float cy, float r, float min, float max, float major,
+ int minorPer, Float value, Float warnBelow, boolean chargeZone, String[] labels, float d) {
+ float sr = r * 0.74f;
+ rect.set(cx - sr, cy - sr, cx + sr, cy + sr);
+ zone.setStrokeWidth(r * 0.05f);
+ if (warnBelow != null) {
+ zone.setColor(AccentColor.withAlpha(red, 0xC0));
+ c.drawArc(rect, START_DEG, SWEEP_DEG * (warnBelow - min) / (max - min), false, zone);
+ }
+ if (chargeZone && min < 0f) {
+ zone.setColor(AccentColor.withAlpha(green, 0xC0));
+ c.drawArc(rect, START_DEG, SWEEP_DEG * (0f - min) / (max - min), false, zone);
+ }
+ float minor = major / minorPer;
+ int steps = Math.round((max - min) / minor);
+ text.setTextSize(r * 0.13f);
+ for (int i = 0; i <= steps; i++) {
+ float val = min + i * minor;
+ boolean isMajor = i % minorPer == 0;
+ double a = Math.toRadians(START_DEG + SWEEP_DEG * (val - min) / (max - min));
+ float cos = (float) Math.cos(a), sin = (float) Math.sin(a);
+ float outer = r * 0.82f, inner = isMajor ? r * 0.68f : r * 0.75f;
+ tick.setStrokeWidth(isMajor ? r * 0.025f : r * 0.012f);
+ c.drawLine(cx + outer * cos, cy + outer * sin, cx + inner * cos, cy + inner * sin, tick);
+ if (isMajor && labels == null) {
+ float lr = r * 0.54f;
+ c.drawText(String.format(Locale.US, "%.0f", val), cx + lr * cos,
+ cy + lr * sin + text.getTextSize() * 0.36f, text);
+ }
+ }
+ if (labels != null) {
+ text.setTextSize(r * 0.16f);
+ for (int i = 0; i < labels.length; i++) {
+ double a = Math.toRadians(START_DEG + SWEEP_DEG * i / (labels.length - 1f));
+ float lr = r * 0.52f;
+ c.drawText(labels[i], cx + lr * (float) Math.cos(a), cy + lr * (float) Math.sin(a) + text.getTextSize() * 0.36f, text);
+ }
+ }
+ if (value != null) {
+ double a = Math.toRadians(START_DEG + SWEEP_DEG * (Math.max(min, Math.min(max, value)) - min) / (max - min));
+ float cos = (float) Math.cos(a), sin = (float) Math.sin(a);
+ needle.setColor(AccentColor.withAlpha(accent, 0x50));
+ needle.setStrokeWidth(r * 0.06f);
+ c.drawLine(cx - r * 0.12f * cos, cy - r * 0.12f * sin, cx + r * 0.80f * cos, cy + r * 0.80f * sin, needle);
+ needle.setColor(accent);
+ needle.setStrokeWidth(r * 0.03f);
+ c.drawLine(cx - r * 0.12f * cos, cy - r * 0.12f * sin, cx + r * 0.80f * cos, cy + r * 0.80f * sin, needle);
+ }
+ // The hub: a small chrome octagon.
+ octagon(cx, cy, r * 0.08f);
+ fill.setColor(chromeLight);
+ c.drawPath(path, fill);
+ octagon(cx, cy, r * 0.045f);
+ fill.setColor(faceColour);
+ c.drawPath(path, fill);
+ }
+
+ /** Under the hub: a small caption, the value large, and a line below it. */
+ private void label(Canvas c, float cx, float cy, float r, String caption, String value, String below,
+ float valueSize) {
+ text.setTextSize(r * 0.10f);
+ text.setColor(AccentColor.withAlpha(cream, 0xB0));
+ text.setLetterSpacing(0.15f);
+ c.drawText(caption, cx, cy + r * 0.25f, text);
+ text.setLetterSpacing(0f);
+ text.setColor(cream);
+ text.setTextSize(r * valueSize);
+ c.drawText(value, cx, cy + r * 0.50f, text);
+ if (below != null) {
+ text.setTextSize(r * 0.11f);
+ text.setColor(light);
+ c.drawText(below, cx, cy + r * 0.67f, text);
+ text.setColor(cream);
+ }
+ }
+
+ /** P R N D under the speed; the engaged one in the accent colour. */
+ private void gears(Canvas c, float cx, float y, float r, float d) {
+ String[] letters = { "P", "R", "N", "D" };
+ text.setTextSize(r * 0.10f);
+ float pitch = r * 0.13f;
+ for (int i = 0; i < 4; i++) {
+ boolean on = v.gear != null && v.gear == i + 1;
+ text.setColor(on ? light : AccentColor.withAlpha(cream, 0x55));
+ text.setFakeBoldText(on);
+ c.drawText(letters[i], cx + (i - 1.5f) * pitch, y + r * 0.10f, text);
+ }
+ text.setFakeBoldText(false);
+ text.setColor(cream);
+ }
+
+ /** The cream line along the bottom, items separated by small octagons. */
+ private void drawLine(Canvas c, float w, float h, float d) {
+ String[] items = {
+ v.odometerKm == null ? "ODO —" : String.format(Locale.US, "ODO %,.0f km", v.odometerKm),
+ v.tripKm == null ? "TRIP —" : String.format(Locale.US, "TRIP %.1f km", v.tripKm),
+ v.usage5Min == null ? "— kWh/100 km" : String.format(Locale.US, "%.1f kWh/100 km", v.usage5Min),
+ v.climateCelsius == null ? "— °C" : String.format(Locale.US, "%.0f °C", v.climateCelsius),
+ v.clock.isEmpty() ? "—" : v.clock,
+ };
+ float y = h * 0.95f;
+ text.setTextSize(20f * d);
+ text.setLetterSpacing(0.08f);
+ float pitch = w / items.length;
+ for (int i = 0; i < items.length; i++) {
+ c.drawText(items[i], pitch * (i + 0.5f), y, text);
+ if (i > 0) {
+ octagon(pitch * i, y - 7f * d, 5f * d);
+ fill.setColor(accent);
+ c.drawPath(path, fill);
+ }
+ }
+ text.setLetterSpacing(0f);
+ chrome.setColor(chromeDark);
+ chrome.setStrokeWidth(d);
+ c.drawLine(w * 0.06f, h * 0.885f, w * 0.94f, h * 0.885f, chrome);
+ }
+}
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index d0b5072..c94c2cc 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -45,4 +45,9 @@
#0A2240
#E3EEFF
+
+ #0D0D0F
+ #F2E8D5
+ #D5DADF
+ #5A6066
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a32f9a5..a7c746a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -130,6 +130,7 @@
Sci-fi
Cartoon
Blueprint
+ MG
BATTERY
USAGE · 5 MIN
kWh/100 km
diff --git a/screenshots/nilsmg4-2.25.0-mg.png b/screenshots/nilsmg4-2.25.0-mg.png
new file mode 100644
index 0000000..976fdc2
Binary files /dev/null and b/screenshots/nilsmg4-2.25.0-mg.png differ