App 2.17.0: speed average, compact top bar, About in Settings

- "Speed, 5-min average" card under Speed: time-weighted mean speed,
  standing included, from the same samples as the other averages.
- The top bar is one compact line (title and subtitle side by side).
  Version, update check and About moved from it to an App card on the
  Settings tab.
- The Overview no longer scrolls in the car's window: checked on the
  emulator at 1778x640, the layout height the car reports, after
  trimming the page's padding and the tyre card's height.
- The capacity hint says "Overview" instead of "Vehicle tab".

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 20:26:02 +02:00
co-authored by Claude Opus 5.5
parent 79bcfa05f6
commit 8d3eb80f66
13 changed files with 93 additions and 29 deletions
+4
View File
@@ -112,6 +112,10 @@ tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does *
INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in
the now gitignored `k8s/secrets.yaml` (since moved to `k8s/secrets/secrets.yaml`, outside the now gitignored `k8s/secrets.yaml` (since moved to `k8s/secrets/secrets.yaml`, outside
`kubectl apply -f k8s/`). Filled-in secrets live only there from now on. `kubectl apply -f k8s/`). Filled-in secrets live only there from now on.
- 2026-10-18 — App 2.17.0: "Speed, 5-min average" card under Speed. The top bar is one compact line
(title and subtitle); version, update check and About moved to an App card on the Settings tab.
The Overview fits the car's window without scrolling: checked on the emulator at 1778×640, the
layout height the car reports (less page padding, a lower tyre card).
- 2026-10-18 — App 2.16.4: drop-downs again, as Material exposed dropdown menus: their list leaves - 2026-10-18 — App 2.16.4: drop-downs again, as Material exposed dropdown menus: their list leaves
the focus with the activity, so full screen stays (a Spinner's list is a focusable window of its the focus with the activity, so full screen stays (a Spinner's list is a focusable window of its
own without the full-screen flags). 2.16.3 had replaced them with buttons. own without the full-screen flags). 2.16.3 had replaced them with buttons.
+2 -2
View File
@@ -34,8 +34,8 @@ android {
applicationId "com.stinnesbeck.nilsmg4" applicationId "com.stinnesbeck.nilsmg4"
minSdk 27 minSdk 27
targetSdk 36 targetSdk 36
versionCode 88 versionCode 89
versionName "2.16.4" versionName "2.17.0"
// Nothing about the server ships in the APK: its URL and ingest token are entered in // 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. // the app's Server tab (ServerConfig), and the database is only reachable by the server.
@@ -94,7 +94,8 @@ public class MainActivity extends AppCompatActivity {
vehTireFl, vehTireFr, vehTireRl, vehTireRr, vehTireFl, vehTireFr, vehTireRl, vehTireRr,
vehRangeTrip, vehRangeTripNote, vehRangeAverage, vehRangeAverageNote, vehRangeTrip, vehRangeTripNote, vehRangeAverage, vehRangeAverageNote,
vehUsageTrip, vehUsageTripNote, vehUsageAverage, vehUsageAverageNote, vehUsageTrip, vehUsageTripNote, vehUsageAverage, vehUsageAverageNote,
vehEnergyTrip, vehEnergyTripNote, vehPowerAverage, vehPowerAverageNote; vehEnergyTrip, vehEnergyTripNote, vehPowerAverage, vehPowerAverageNote,
vehSpeedAverage, vehSpeedAverageNote;
/** Usable pack capacity for the range estimates; the car does not publish it. */ /** Usable pack capacity for the range estimates; the car does not publish it. */
static final String USABLE_KWH_KEY = "usable_kwh"; static final String USABLE_KWH_KEY = "usable_kwh";
@@ -233,7 +234,6 @@ public class MainActivity extends AppCompatActivity {
getTheme().applyStyle(AccentColor.fromPref(prefs.getString(AccentColor.PREF_KEY, null)).overlay, true); getTheme().applyStyle(AccentColor.fromPref(prefs.getString(AccentColor.PREF_KEY, null)).overlay, true);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
((TextView) findViewById(R.id.version_badge)).setText("v" + BuildConfig.VERSION_NAME);
// The pages are inflated here, once, and handed to the pager as fixed, // The pages are inflated here, once, and handed to the pager as fixed,
@@ -277,8 +277,10 @@ public class MainActivity extends AppCompatActivity {
if (savedInstanceState != null) { if (savedInstanceState != null) {
((ViewPager2) findViewById(R.id.content)).setCurrentItem(savedInstanceState.getInt(STATE_PAGE, 0), false); ((ViewPager2) findViewById(R.id.content)).setCurrentItem(savedInstanceState.getInt(STATE_PAGE, 0), false);
} }
findViewById(R.id.about_button).setOnClickListener(v -> showAbout()); ((TextView) settingsPane.findViewById(R.id.settings_version))
findViewById(R.id.update_button).setOnClickListener(v -> checkForUpdate(v)); .setText(getString(R.string.settings_version, BuildConfig.VERSION_NAME));
settingsPane.findViewById(R.id.settings_about_button).setOnClickListener(v -> showAbout());
settingsPane.findViewById(R.id.settings_update_button).setOnClickListener(v -> checkForUpdate(v));
// Unstable builds check for a newer pre-release; the stable flavor's UpdateHook is // Unstable builds check for a newer pre-release; the stable flavor's UpdateHook is
// a no-op and does not even contain the updater. // a no-op and does not even contain the updater.
@@ -1288,6 +1290,8 @@ public class MainActivity extends AppCompatActivity {
vehEnergyTripNote = vehiclePane.findViewById(R.id.vehicle_energy_trip_note); vehEnergyTripNote = vehiclePane.findViewById(R.id.vehicle_energy_trip_note);
vehPowerAverage = vehiclePane.findViewById(R.id.vehicle_power_average); vehPowerAverage = vehiclePane.findViewById(R.id.vehicle_power_average);
vehPowerAverageNote = vehiclePane.findViewById(R.id.vehicle_power_average_note); vehPowerAverageNote = vehiclePane.findViewById(R.id.vehicle_power_average_note);
vehSpeedAverage = vehiclePane.findViewById(R.id.vehicle_speed_average);
vehSpeedAverageNote = vehiclePane.findViewById(R.id.vehicle_speed_average_note);
} }
private double usableKwh() { private double usableKwh() {
@@ -1308,6 +1312,9 @@ public class MainActivity extends AppCompatActivity {
Double power = estimator.averagePowerKw(monotonicMs); Double power = estimator.averagePowerKw(monotonicMs);
vehPowerAverage.setText(power == null ? "—" : getString(R.string.power_kw, power)); vehPowerAverage.setText(power == null ? "—" : getString(R.string.power_kw, power));
vehPowerAverageNote.setText(power == null ? R.string.power_waiting : R.string.usage_last_5_min); vehPowerAverageNote.setText(power == null ? R.string.power_waiting : R.string.usage_last_5_min);
Double speed = estimator.averageSpeedKmh(monotonicMs);
vehSpeedAverage.setText(speed == null ? "—" : getString(R.string.speed_kmh, speed));
vehSpeedAverageNote.setText(speed == null ? R.string.power_waiting : R.string.usage_last_5_min);
Double energy = estimator.energySinceStart(); Double energy = estimator.energySinceStart();
vehEnergyTrip.setText(energy == null ? "—" : getString(R.string.energy_kwh, energy)); vehEnergyTrip.setText(energy == null ? "—" : getString(R.string.energy_kwh, energy));
vehEnergyTripNote.setText(energy == null ? R.string.energy_trip_waiting : R.string.energy_trip_note); vehEnergyTripNote.setText(energy == null ? R.string.energy_trip_waiting : R.string.energy_trip_note);
@@ -177,6 +177,26 @@ final class RangeEstimator {
return energyKwh / (covered / 3_600_000.0); return energyKwh / (covered / 3_600_000.0);
} }
/** Mean speed over the last five minutes, time-weighted, standing included; null below a minute. */
synchronized Double averageSpeedKmh(long nowMs) {
double distanceKm = 0;
long covered = 0;
Sample previous = null;
for (Sample s : samples) {
if (s.timeMs < nowMs - WINDOW_MS) continue;
if (previous != null) {
long dt = s.timeMs - previous.timeMs;
if (dt > 0 && dt <= MAX_GAP_MS) {
distanceKm += (previous.speedKmh + s.speedKmh) / 2 * (dt / 3_600_000.0);
covered += dt;
}
}
previous = s;
}
if (covered < MIN_WINDOW_MS) return null;
return distanceKm / (covered / 3_600_000.0);
}
synchronized void clear() { synchronized void clear() {
samples.clear(); samples.clear();
tripEnergyKwh = 0; tripEnergyKwh = 0;
@@ -16,14 +16,11 @@
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<LinearLayout android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:orientation="vertical">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="16dp" android:paddingBottom="8dp"> <!-- One compact line: the Overview has to fit the car's 1778×720 px window (about 640 dp for
<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> layouts). Version, update check and About are on the Settings tab. -->
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_overview_title" android:textAppearance="@style/Text.EV.Title" android:id="@+id/header_title" android:textSize="32sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:baselineAligned="true" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="10dp" android:paddingBottom="2dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_overview_subtitle" android:textAppearance="@style/Text.EV.Secondary" android:id="@+id/header_subtitle" android:layout_marginTop="6dp" /> <TextView android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/design_overview_title" android:textAppearance="@style/Text.EV.Title" android:textSize="24sp" android:maxLines="1" />
</LinearLayout> <TextView android:id="@+id/header_subtitle" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="@string/design_overview_subtitle" android:textAppearance="@style/Text.EV.Secondary" android:maxLines="1" android:ellipsize="end" />
<TextView android:id="@+id/version_badge" style="@style/Text.EV.VersionBadge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" />
<com.google.android.material.button.MaterialButton android:id="@+id/update_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="72dp" android:layout_height="72dp" android:minWidth="0dp" android:padding="0dp" android:contentDescription="@string/update_check" app:icon="@drawable/ic_refresh" app:iconGravity="textStart" app:iconPadding="0dp" app:iconTint="?attr/evAccent" />
<com.google.android.material.button.MaterialButton android:id="@+id/about_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="72dp" android:layout_height="72dp" android:layout_marginStart="8dp" android:minWidth="0dp" android:padding="0dp" android:contentDescription="@string/about_action" app:icon="@drawable/ic_info" app:iconGravity="textStart" app:iconPadding="0dp" />
</LinearLayout> </LinearLayout>
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
</LinearLayout> </LinearLayout>
@@ -53,6 +53,12 @@
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/wifi_saved_title" android:textAppearance="@style/Text.EV.Eyebrow" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/wifi_saved_title" android:textAppearance="@style/Text.EV.Eyebrow" />
<TextView android:id="@+id/wifi_saved_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textAppearance="@style/Text.EV.Body" android:lineSpacingExtra="16dp" /> <TextView android:id="@+id/wifi_saved_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textAppearance="@style/Text.EV.Body" android:lineSpacingExtra="16dp" />
</LinearLayout> </LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="24dp" android:background="@drawable/bg_glass" android:layout_marginTop="16dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_app_title" android:textAppearance="@style/Text.EV.Eyebrow" />
<TextView android:id="@+id/settings_version" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:textAppearance="@style/Text.EV.Body" />
<com.google.android.material.button.MaterialButton android:id="@+id/settings_update_button" style="@style/Widget.EV.Button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/btn_check_update" app:icon="@drawable/ic_refresh" />
<com.google.android.material.button.MaterialButton android:id="@+id/settings_about_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/btn_about" app:icon="@drawable/ic_info" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</com.evsuite.abrp.StableScrollView> </com.evsuite.abrp.StableScrollView>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.evsuite.abrp.StableScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:clipToPadding="false"> <com.evsuite.abrp.StableScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:clipToPadding="false">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="24dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="12dp" android:paddingBottom="12dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:baselineAligned="false"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:baselineAligned="false">
<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" android:minHeight="112dp" android:padding="16dp" android:background="@drawable/bg_battery_card" android:layout_weight="1" android:layout_marginEnd="10dp"> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" android:minHeight="112dp" android:padding="16dp" android:background="@drawable/bg_battery_card" android:layout_weight="1" android:layout_marginEnd="10dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_battery_short" android:textAppearance="@style/Text.EV.Secondary" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_battery_short" android:textAppearance="@style/Text.EV.Secondary" />
@@ -52,7 +52,11 @@
<TextView android:id="@+id/vehicle_power_average" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:layout_marginTop="6dp" android:textAppearance="@style/Text.EV.Metric.Compact" android:gravity="end" android:fontFeatureSettings="tnum" android:maxLines="1" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="18sp" android:autoSizeMaxTextSize="28sp" android:autoSizeStepGranularity="1sp" /> <TextView android:id="@+id/vehicle_power_average" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:layout_marginTop="6dp" android:textAppearance="@style/Text.EV.Metric.Compact" android:gravity="end" android:fontFeatureSettings="tnum" android:maxLines="1" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="18sp" android:autoSizeMaxTextSize="28sp" android:autoSizeStepGranularity="1sp" />
<TextView android:id="@+id/vehicle_power_average_note" android:gravity="end" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" android:textAppearance="@style/Text.EV.Secondary" android:textColor="?attr/evAccentLight" android:textSize="16sp" android:maxLines="1" android:ellipsize="end" /> <TextView android:id="@+id/vehicle_power_average_note" android:gravity="end" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" android:textAppearance="@style/Text.EV.Secondary" android:textColor="?attr/evAccentLight" android:textSize="16sp" android:maxLines="1" android:ellipsize="end" />
</LinearLayout> </LinearLayout>
<Space android:layout_width="0dp" android:layout_height="1dp" android:layout_weight="1" android:layout_marginEnd="10dp" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginEnd="10dp" android:orientation="vertical" android:minHeight="112dp" android:padding="16dp" android:background="@drawable/bg_glass">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_row_speed_average" android:textAppearance="@style/Text.EV.Secondary" />
<TextView android:id="@+id/vehicle_speed_average" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:layout_marginTop="6dp" android:textAppearance="@style/Text.EV.Metric.Compact" android:gravity="end" android:fontFeatureSettings="tnum" android:maxLines="1" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="18sp" android:autoSizeMaxTextSize="28sp" android:autoSizeStepGranularity="1sp" />
<TextView android:id="@+id/vehicle_speed_average_note" android:gravity="end" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" android:textAppearance="@style/Text.EV.Secondary" android:textColor="?attr/evAccentLight" android:textSize="16sp" android:maxLines="1" android:ellipsize="end" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<Space android:layout_width="1dp" android:layout_height="16dp" /> <Space android:layout_width="1dp" android:layout_height="16dp" />
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:baselineAligned="false"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:baselineAligned="false">
@@ -71,7 +75,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" android:padding="20dp" android:background="@drawable/bg_glass"> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" android:padding="20dp" android:background="@drawable/bg_glass">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_tires" android:textAppearance="@style/Text.EV.Eyebrow" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_tires" android:textAppearance="@style/Text.EV.Eyebrow" />
<LinearLayout android:layout_width="match_parent" android:layout_height="224dp" android:orientation="horizontal" android:gravity="center_vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="196dp" android:orientation="horizontal" android:gravity="center_vertical">
<LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:gravity="center"> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:gravity="center">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_tire_fl" android:textAppearance="@style/Text.EV.Secondary" android:gravity="center" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_tire_fl" android:textAppearance="@style/Text.EV.Secondary" android:gravity="center" />
<TextView android:id="@+id/vehicle_tire_fl" android:fontFeatureSettings="tnum" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:textAppearance="@style/Text.EV.Body" android:gravity="center" android:layout_marginTop="8dp" android:textColor="?attr/evAccentLight" /> <TextView android:id="@+id/vehicle_tire_fl" android:fontFeatureSettings="tnum" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:textAppearance="@style/Text.EV.Body" android:gravity="center" android:layout_marginTop="8dp" android:textColor="?attr/evAccentLight" />
+5 -8
View File
@@ -2,14 +2,11 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/app_root" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:background="@color/ev_background"> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/app_root" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:background="@color/ev_background">
<com.evsuite.abrp.PremiumBackdropView android:layout_width="match_parent" android:layout_height="match_parent"/> <com.evsuite.abrp.PremiumBackdropView android:layout_width="match_parent" android:layout_height="match_parent"/>
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="16dp" android:paddingBottom="8dp"> <!-- One compact line: the Overview has to fit the car's 1778×720 px window (about 640 dp for
<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> layouts). Version, update check and About are on the Settings tab. -->
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_overview_title" android:textAppearance="@style/Text.EV.Title" android:id="@+id/header_title" android:textSize="24sp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:baselineAligned="true" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="10dp" android:paddingBottom="2dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_overview_subtitle" android:textAppearance="@style/Text.EV.Secondary" android:id="@+id/header_subtitle" android:layout_marginTop="6dp"/> <TextView android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/design_overview_title" android:textAppearance="@style/Text.EV.Title" android:textSize="24sp" android:maxLines="1" />
</LinearLayout> <TextView android:id="@+id/header_subtitle" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="@string/design_overview_subtitle" android:textAppearance="@style/Text.EV.Secondary" android:maxLines="1" android:ellipsize="end" />
<TextView android:id="@+id/version_badge" style="@style/Text.EV.VersionBadge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp"/>
<com.google.android.material.button.MaterialButton android:id="@+id/update_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="72dp" android:layout_height="72dp" android:minWidth="0dp" android:padding="0dp" android:contentDescription="@string/update_check" app:icon="@drawable/ic_refresh" app:iconGravity="textStart" app:iconPadding="0dp" app:iconTint="?attr/evAccent"/>
<com.google.android.material.button.MaterialButton android:id="@+id/about_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="72dp" android:layout_height="72dp" android:layout_marginStart="8dp" android:minWidth="0dp" android:padding="0dp" android:contentDescription="@string/about_action" app:icon="@drawable/ic_info" app:iconGravity="textStart" app:iconPadding="0dp"/>
</LinearLayout> </LinearLayout>
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="8dp" android:background="@drawable/bg_sidebar"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="8dp" android:background="@drawable/bg_sidebar">
@@ -50,5 +50,11 @@
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/wifi_saved_title" android:textAppearance="@style/Text.EV.Eyebrow" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/wifi_saved_title" android:textAppearance="@style/Text.EV.Eyebrow" />
<TextView android:id="@+id/wifi_saved_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textAppearance="@style/Text.EV.Body" android:lineSpacingExtra="16dp" /> <TextView android:id="@+id/wifi_saved_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textAppearance="@style/Text.EV.Body" android:lineSpacingExtra="16dp" />
</LinearLayout> </LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="24dp" android:background="@drawable/bg_glass" android:layout_marginTop="16dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_app_title" android:textAppearance="@style/Text.EV.Eyebrow" />
<TextView android:id="@+id/settings_version" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:textAppearance="@style/Text.EV.Body" />
<com.google.android.material.button.MaterialButton android:id="@+id/settings_update_button" style="@style/Widget.EV.Button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/btn_check_update" app:icon="@drawable/ic_refresh" />
<com.google.android.material.button.MaterialButton android:id="@+id/settings_about_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/btn_about" app:icon="@drawable/ic_info" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</com.evsuite.abrp.StableScrollView> </com.evsuite.abrp.StableScrollView>
+7 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.evsuite.abrp.StableScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:clipToPadding="false"> <com.evsuite.abrp.StableScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:clipToPadding="false">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="24dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="12dp" android:paddingBottom="12dp">
<GridLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:columnCount="2"> <GridLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:columnCount="2">
<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" android:minHeight="112dp" android:padding="16dp" android:background="@drawable/bg_battery_card" android:layout_columnWeight="1" android:layout_margin="4dp"> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" android:minHeight="112dp" android:padding="16dp" android:background="@drawable/bg_battery_card" android:layout_columnWeight="1" android:layout_margin="4dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_battery_short" android:textAppearance="@style/Text.EV.Secondary" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_battery_short" android:textAppearance="@style/Text.EV.Secondary" />
@@ -49,6 +49,11 @@
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_row_speed" android:textAppearance="@style/Text.EV.Secondary" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_row_speed" android:textAppearance="@style/Text.EV.Secondary" />
<TextView android:id="@+id/vehicle_speed" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:layout_marginTop="6dp" android:textAppearance="@style/Text.EV.Metric.Compact" android:gravity="end" android:fontFeatureSettings="tnum" android:maxLines="1" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="18sp" android:autoSizeMaxTextSize="28sp" android:autoSizeStepGranularity="1sp" /> <TextView android:id="@+id/vehicle_speed" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:layout_marginTop="6dp" android:textAppearance="@style/Text.EV.Metric.Compact" android:gravity="end" android:fontFeatureSettings="tnum" android:maxLines="1" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="18sp" android:autoSizeMaxTextSize="28sp" android:autoSizeStepGranularity="1sp" />
</LinearLayout> </LinearLayout>
<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:layout_margin="4dp" android:orientation="vertical" android:minHeight="112dp" android:padding="16dp" android:background="@drawable/bg_glass">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_row_speed_average" android:textAppearance="@style/Text.EV.Secondary" />
<TextView android:id="@+id/vehicle_speed_average" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:layout_marginTop="6dp" android:textAppearance="@style/Text.EV.Metric.Compact" android:gravity="end" android:fontFeatureSettings="tnum" android:maxLines="1" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="18sp" android:autoSizeMaxTextSize="28sp" android:autoSizeStepGranularity="1sp" />
<TextView android:id="@+id/vehicle_speed_average_note" android:gravity="end" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" android:textAppearance="@style/Text.EV.Secondary" android:textColor="?attr/evAccentLight" android:textSize="16sp" android:maxLines="1" android:ellipsize="end" />
</LinearLayout>
</GridLayout> </GridLayout>
<Space android:layout_width="1dp" android:layout_height="16dp" /> <Space android:layout_width="1dp" android:layout_height="16dp" />
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp" android:background="@drawable/bg_glass"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp" android:background="@drawable/bg_glass">
@@ -66,7 +71,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp" android:background="@drawable/bg_glass" android:layout_marginTop="16dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp" android:background="@drawable/bg_glass" android:layout_marginTop="16dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_tires" android:textAppearance="@style/Text.EV.Eyebrow" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/design_tires" android:textAppearance="@style/Text.EV.Eyebrow" />
<LinearLayout android:layout_width="match_parent" android:layout_height="224dp" android:orientation="horizontal" android:gravity="center_vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="196dp" android:orientation="horizontal" android:gravity="center_vertical">
<LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:gravity="center"> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:gravity="center">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_tire_fl" android:textAppearance="@style/Text.EV.Secondary" android:gravity="center" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/vehicle_tire_fl" android:textAppearance="@style/Text.EV.Secondary" android:gravity="center" />
<TextView android:id="@+id/vehicle_tire_fl" android:fontFeatureSettings="tnum" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:textAppearance="@style/Text.EV.Body" android:gravity="center" android:layout_marginTop="8dp" android:textColor="?attr/evAccentLight" /> <TextView android:id="@+id/vehicle_tire_fl" android:fontFeatureSettings="tnum" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/no_value" android:textAppearance="@style/Text.EV.Body" android:gravity="center" android:layout_marginTop="8dp" android:textColor="?attr/evAccentLight" />
+6 -1
View File
@@ -59,7 +59,7 @@
<string name="usage_last_5_min">over the last 5 minutes</string> <string name="usage_last_5_min">over the last 5 minutes</string>
<string name="hint_usable_capacity">Usable battery capacity</string> <string name="hint_usable_capacity">Usable battery capacity</string>
<string name="btn_save_capacity">Save capacity</string> <string name="btn_save_capacity">Save capacity</string>
<string name="helper_usable_capacity">Used for the range estimates on the Vehicle tab. The 51 kWh MG4 battery has 50.8 kWh usable, the 64 kWh one 61.7, the 77 kWh one 74.4.</string> <string name="helper_usable_capacity">Used for the range estimates on the Overview. The 51 kWh MG4 battery has 50.8 kWh usable, the 64 kWh one 61.7, the 77 kWh one 74.4.</string>
<string name="usable_capacity_saved">Usable capacity saved</string> <string name="usable_capacity_saved">Usable capacity saved</string>
<string name="usable_capacity_invalid">Enter a value between 10 and 150 kWh</string> <string name="usable_capacity_invalid">Enter a value between 10 and 150 kWh</string>
<string name="vehicle_row_speed">Speed</string> <string name="vehicle_row_speed">Speed</string>
@@ -204,6 +204,11 @@
<string name="update_check">Check for updates</string> <string name="update_check">Check for updates</string>
<string name="update_checking">Checking for a new OTA version…</string> <string name="update_checking">Checking for a new OTA version…</string>
<string name="update_dialog_title">Software update</string> <string name="update_dialog_title">Software update</string>
<string name="btn_check_update">Check for updates</string>
<string name="btn_about">About Nils\' MG4</string>
<string name="settings_version">Version %1$s</string>
<string name="vehicle_row_speed_average">Speed, 5-min average</string>
<string name="speed_kmh">%1$.0f km/h</string>
<string name="update_downloading">Downloading and verifying…</string> <string name="update_downloading">Downloading and verifying…</string>
<string name="update_downloading_percent">Downloading… %1$d%%</string> <string name="update_downloading_percent">Downloading… %1$d%%</string>
<string name="about_action">About</string> <string name="about_action">About</string>
+2 -1
View File
@@ -12,7 +12,7 @@
<string name="design_service_title">Live data.</string> <string name="design_service_title">Live data.</string>
<string name="design_service_subtitle">Where your car\'s live data goes, and how often.</string> <string name="design_service_subtitle">Where your car\'s live data goes, and how often.</string>
<string name="design_settings_title">Settings.</string> <string name="design_settings_title">Settings.</string>
<string name="design_settings_subtitle">Display, battery, charging price and Wi-Fi.</string> <string name="design_settings_subtitle">Display, battery, charging price, Wi-Fi and updates.</string>
<string name="design_log_title">Every upload, right here.</string> <string name="design_log_title">Every upload, right here.</string>
<string name="design_log_subtitle">Status and details of recent uploads.</string> <string name="design_log_subtitle">Status and details of recent uploads.</string>
<string name="design_battery">BATTERY</string> <string name="design_battery">BATTERY</string>
@@ -26,6 +26,7 @@
<string name="design_abrp_title">ABRP, DIRECTLY FROM THE CAR</string> <string name="design_abrp_title">ABRP, DIRECTLY FROM THE CAR</string>
<string name="design_display_title">DISPLAY AND STEERING WHEEL</string> <string name="design_display_title">DISPLAY AND STEERING WHEEL</string>
<string name="design_battery_settings_title">BATTERY</string> <string name="design_battery_settings_title">BATTERY</string>
<string name="design_app_title">APP</string>
<string name="design_price_title">CHARGING PRICE</string> <string name="design_price_title">CHARGING PRICE</string>
<string name="design_service_timing">UPDATE FREQUENCY</string> <string name="design_service_timing">UPDATE FREQUENCY</string>
<string name="design_car_desc">Tire pressure positions on the vehicle</string> <string name="design_car_desc">Tire pressure positions on the vehicle</string>
@@ -140,4 +140,16 @@ public class RangeEstimatorTest {
for (long t = 315_000; t <= 450_000; t += 15_000) r.add(t, 2f, 0f); for (long t = 315_000; t <= 450_000; t += 15_000) r.add(t, 2f, 0f);
assertEquals(11.0, r.averagePowerKw(450_000), 1.0); assertEquals(11.0, r.averagePowerKw(450_000), 1.0);
} }
@Test
public void averageSpeedOverTheLastFiveMinutes() {
RangeEstimator r = new RangeEstimator();
r.add(0, 10f, 60f);
assertNull(r.averageSpeedKmh(0));
for (long t = 15_000; t <= 300_000; t += 15_000) r.add(t, 10f, 60f);
assertEquals(60.0, r.averageSpeedKmh(300_000), 1e-6);
// Half the window standing at lights halves the average.
for (long t = 315_000; t <= 450_000; t += 15_000) r.add(t, 1f, 0f);
assertEquals(30.0, r.averageSpeedKmh(450_000), 4.0);
}
} }