Upstream kept the head unit on the driver's phone hotspot by re-joining
a saved network every few minutes. This car's head unit has its own
connection and the Wi-Fi is not used, so the feature goes entirely:
WifiAutoConnect, its call on the service tick, the preference key, and
the ACCESS_WIFI_STATE / CHANGE_WIFI_STATE permissions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
The car's key report, from the on-screen key log in 2.7.0:
android.intent.extra.hardkey.keycode = 287 (Integer),
android.intent.extra.hardkey.down (Boolean; one report on press, one on
release) and android.intent.extra.hardkey.longpress (Boolean). 2.7.0
only looked for short names like "keyCode", so it never found the code.
SteeringKeyReport now matches extras by the last part of their name,
case-insensitively, and is Android-free; its tests include the exact
double press logged on the car (releases 279 ms apart). Replayed in the
car's format on the emulator: double press opens the app, single and
long presses do not.
The floating button is removed (class, geometry and test, the overlay
permission, setting, switch and strings): it did not show over the MG4
launcher.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
The MG4's launcher does not list third-party apps, so the app could
only be opened by reinstalling it. Now a small round MG-red button
floats over the car's screens: tap opens the app, drag moves it (the
position is kept and clamped to the screen), and it hides while the
app itself is open. A switch in the Upload tab turns it off.
The always-on service owns the overlay, so the button is there from
boot. It needs the overlay permission (SYSTEM_ALERT_WINDOW), which the
car build has through the system UID; without it, or if the window
manager refuses, the button stays away and nothing crashes, and the
Upload tab offers the system permission screen.
Tested on an Android 9 AOSP emulator at the head unit's 1920x1080 /
160 dpi with the car APK (installs as UID 1000): button appears over
the home screen, tap opens the app, hidden while open, drag moves it
without opening the app, position survives a force-stop, the switch
works through the UI. FloatingButtonGeometry has 3 unit tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
- New Server tab: server URL, ingest token and installation id, with
Save, Test connection (health, then a read-only token check) and the
config-file import that upstream removed (app folder / USB stick first,
document picker as fallback). Settings are kept encrypted and read on
every use, so the service picks up a change without a restart. Each
installation generates its own id; the inherited upstream UUID and
the baked-in CONSUMPTION_API_* build fields are gone.
- Live telemetry restored from before ea8032a (TelemetryPayload,
UploadCadence, UploadSettings, GPS) and sent in batches to
POST /v1/telemetry instead of ABRP; the server forwards to ABRP. Adds
ext_temp, voltage and current. Unsent samples queue in memory (about
8 h) while offline.
- Upload tab: the live-telemetry switch and cadence controls work again;
the dead service and autostart switches are removed.
- Manifest: location permissions and foreground service types.
- Unit tests compile again: 114 (stable) / 136 (unstable) pass. Tests
for the deleted AbrpApi and HeadUnitWeather removed; new tests for
ServerConfig and TelemetryQueue. Lint passes.
Not yet tried on the car.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ports WinClose's SWI69 window control as an opt-in foreground service that is
independent of ABRP: no token, no GPS, no upload cadence. The driver opening a
door while the car is in P at a readable 0 km/h pulses UP at all four areas for
five seconds, then STOP.
Only the parts that serve that one behaviour are taken. WinClose's speed and
time arming, configurable delay, warning beep, per-window modes and position
reads are all absent — the target car is a Comfort trim whose motors carry no
position sensor, so a close can be neither verified nor cancelled.
The pulse, the door edge detector and the eligibility policy are pure classes
with unit tests. STOP is attempted from a finally block on every exit, including
an interrupted sleep, a refused write and a sender that throws, because a pulse
that stops arriving leaves the motors wherever they were.
Guards worth naming, several of them learned from reading WinClose rather than
from the port itself:
- A wake lock spans the pulse. This fires as the driver leaves, which is exactly
when the head unit suspends; without it the glass can stop halfway with no
STOP sent.
- A fresh listener registration replays the door's current state. That replay
seeds the edge detector instead of triggering it, or a binder reconnect while
the door stands open would close the windows on someone halfway out.
- WinClose's 60-second cooldown, so reaching back into the car does not run a
second pulse.
- The service stops itself on a non-SWI69 generation, and the switch is disabled
unless the process holds system uid — only the unstable flavor declares
android.uid.system, so a stable build could otherwise offer a toggle whose
every command would fail.
This is the project's first write to the vehicle. README, SECURITY, AGENTS,
CONTRIBUTING and DISCLAIMER all claimed the app never writes; that claim is
retracted and scoped rather than quietly left standing.
Untested on a car. The feature is off by default.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A fork of malys/EVABRPUploader (itself a fork of Leon Kernan's ABRP_Uploader)
carrying the changes that make it work on this car. History starts here: the
tree is what matters, and the upstream projects are where the earlier work
belongs.
EVHardware is vendored rather than tracked as a submodule, so one clone
builds.
Telemetry. SOC, battery power and range read as unavailable on SWI69 because
the vendor fallbacks were gated to SWI68 alone, while the property ids they
use are the ones this car publishes — the gates now admit SWI69. Tyre
pressure, the odometer and charge status come from the vendor block too,
since the generic properties for all three are declared here and never
publish. Tyre pressure is confirmed on the vehicle; the other two are not
yet.
Vehicle page. Every signal the car answers, in a table. One that it does not
publish shows a dash: an unimplemented VHAL property returns zero, and
"0 °C" would read as a measurement.
Wi-Fi page. Joins a network the unit already has saved, so a phone hotspot
does not need a trip into Settings on every drive. Tried on the upload tick
and from a button, skipped while already associated. This leans on the unit
being API 28 and the build being platform-signed — getConfiguredNetworks and
enableNetwork are closed to ordinary apps from API 29 — and fails quietly
where that does not hold.
The OTA updater points at this repository. An APK from upstream is signed
with a different key, which Android will not install over a platform-signed
build.
Co-Authored-By: Claude <noreply@anthropic.com>