App 2.10.0: range since start, usage in its own cards

"Range since start" replaces the range at current power: remaining
energy over the consumption since the car was switched on, standing
with the climate on included. A trip starts again after 10 minutes
without samples (the car was off) or when charging.

The kWh/100 km for both estimates moved out of the range tiles into
two cards of their own, directly below them; the overview still fits
the full-screen 1920x720 display without scrolling.

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 10:01:11 +02:00
co-authored by Claude Opus 5.5
parent a5db5604d3
commit 2a0c4c9675
8 changed files with 169 additions and 50 deletions
@@ -90,7 +90,8 @@ public class MainActivity extends AppCompatActivity {
private TextView vehSoc, vehRange, vehPower, vehSpeed, vehOdometer,
vehCharging, vehHvac,
vehTireFl, vehTireFr, vehTireRl, vehTireRr,
vehRangePower, vehRangePowerNote, vehRangeAverage, vehRangeAverageNote;
vehRangeTrip, vehRangeTripNote, vehRangeAverage, vehRangeAverageNote,
vehUsageTrip, vehUsageTripNote, vehUsageAverage, vehUsageAverageNote;
/** Usable pack capacity for the range estimates; the car does not publish it. */
static final String USABLE_KWH_KEY = "usable_kwh";
@@ -995,10 +996,14 @@ public class MainActivity extends AppCompatActivity {
vehTireFr = vehiclePane.findViewById(R.id.vehicle_tire_fr);
vehTireRl = vehiclePane.findViewById(R.id.vehicle_tire_rl);
vehTireRr = vehiclePane.findViewById(R.id.vehicle_tire_rr);
vehRangePower = vehiclePane.findViewById(R.id.vehicle_range_power);
vehRangePowerNote = vehiclePane.findViewById(R.id.vehicle_range_power_note);
vehRangeTrip = vehiclePane.findViewById(R.id.vehicle_range_trip);
vehRangeTripNote = vehiclePane.findViewById(R.id.vehicle_range_trip_note);
vehRangeAverage = vehiclePane.findViewById(R.id.vehicle_range_average);
vehRangeAverageNote = vehiclePane.findViewById(R.id.vehicle_range_average_note);
vehUsageTrip = vehiclePane.findViewById(R.id.vehicle_usage_trip);
vehUsageTripNote = vehiclePane.findViewById(R.id.vehicle_usage_trip_note);
vehUsageAverage = vehiclePane.findViewById(R.id.vehicle_usage_average);
vehUsageAverageNote = vehiclePane.findViewById(R.id.vehicle_usage_average_note);
}
private double usableKwh() {
@@ -1006,31 +1011,39 @@ public class MainActivity extends AppCompatActivity {
}
/**
* The two range estimates. Both need the car to be moving on battery power; the average
* also needs a minute or more of driving, which the service has usually collected already.
* The range estimates since start and at the 5-minute average, each with its usage in a
* card below it. Since start needs the car to have moved; the average needs a minute or more
* of driving, which the service has usually collected already.
*/
private void refreshRangeEstimates(EnergySnapshot s) {
RangeEstimator estimator = RangeEstimator.shared();
long monotonicMs = android.os.SystemClock.elapsedRealtime();
estimator.add(monotonicMs, s.getBatteryPowerKw(), s.getSpeedKmh());
Double remaining = RangeEstimator.remainingKwh(s.getSocPercent(), usableKwh());
showEstimate(vehRangePower, vehRangePowerNote,
RangeEstimator.instant(remaining, s.getBatteryPowerKw(), s.getSpeedKmh()),
R.string.range_not_driving);
showEstimate(vehRangeAverage, vehRangeAverageNote,
estimator.average(remaining, monotonicMs), R.string.range_waiting);
RangeEstimator.Estimate trip = estimator.sinceStart(remaining);
showEstimate(vehRangeTrip, vehRangeTripNote, vehUsageTrip, vehUsageTripNote, trip,
trip == null ? "" : getString(R.string.usage_over_distance, trip.distanceKm),
R.string.range_trip_waiting);
showEstimate(vehRangeAverage, vehRangeAverageNote, vehUsageAverage, vehUsageAverageNote,
estimator.average(remaining, monotonicMs), getString(R.string.usage_last_5_min),
R.string.range_waiting);
}
private void showEstimate(TextView value, TextView note, RangeEstimator.Estimate e, int whyNot) {
private void showEstimate(TextView range, TextView rangeNote, TextView usage, TextView usageNote,
RangeEstimator.Estimate e, String basis, int whyNot) {
if (e == null) {
value.setText("—");
note.setText(whyNot);
range.setText("—");
rangeNote.setText(whyNot);
usage.setText("—");
usageNote.setText(whyNot);
return;
}
// Coasting at speed costs almost nothing, which would read as thousands of km.
value.setText(e.rangeKm > 999 ? getString(R.string.range_over, 999)
range.setText(e.rangeKm > 999 ? getString(R.string.range_over, 999)
: String.format(java.util.Locale.US, "%.0f km", e.rangeKm));
note.setText(getString(R.string.range_consumption, e.kwhPer100Km));
rangeNote.setText("");
usage.setText(getString(R.string.range_consumption, e.kwhPer100Km));
usageNote.setText(basis);
}
/**