Commit Graph
28 Commits
Author SHA1 Message Date
TCFDONMEZ eb40b3d31d Use vehicle consumption counter for energy 2026-09-30 23:06:27 +03:00
TCFDONMEZ d25c09ec8f Correct SWI69 distance scale 2026-09-30 13:46:51 +03:00
TCFDONMEZ c44722bf12 Retain four months of selectable daily consumption 2026-09-30 12:12:25 +03:00
TCFDONMEZ 1709fdf1ac Track rolling 7 and 30 day consumption 2026-09-30 12:08:54 +03:00
TCFDONMEZ 81483e7f48 Add weekly and monthly consumption periods 2026-09-30 12:04:40 +03:00
TCFDONMEZ 1749815599 Remove unavailable remaining energy card 2026-09-30 11:34:09 +03:00
TCFDONMEZ 48a210db9b Add readable charging curve and SOC power table 2026-09-30 11:30:13 +03:00
TCFDONMEZ 76d1d680d6 Use consumption period buttons 2026-09-30 11:28:24 +03:00
TCFDONMEZ 634a374ea7 Download one OTA APK and open Downloads 2026-09-30 11:27:14 +03:00
TCFDONMEZ 25abf0da5b Add persistent consumption and charging dashboards 2026-09-30 11:17:54 +03:00
TCFDONMEZ 69ef0eeef6 ui: hide unsupported vehicle telemetry 2026-09-30 11:07:29 +03:00
TCFDONMEZ e8c9122fdf fix: use DownloadManager for OTA packages 2026-09-30 11:02:08 +03:00
TCFDONMEZ e091704990 ui: show installed version beside OTA button 2026-09-30 10:55:21 +03:00
TCFDONMEZ dde5842c62 chore: publish public installer OTA test 2026-09-30 10:49:55 +03:00
TCFDONMEZ 1bce1d1ae1 fix: stage OTA fallback in public downloads 2026-09-30 10:48:25 +03:00
TCFDONMEZ 08e894b6f2 chore: publish OTA fallback test build 2026-09-30 10:43:27 +03:00
TCFDONMEZ f2e201d293 fix: open system installer when OTA session fails 2026-09-30 10:42:12 +03:00
TCFDONMEZ cca911c4e4 ux: disable page transition animation 2026-09-30 10:36:09 +03:00
TCFDONMEZ bad747a230 fix: align OTA install with DriveHub Dort 2026-09-30 10:29:57 +03:00
TCFDONMEZ 1330c0b723 chore: release 2.2.9 OTA test 2026-09-30 10:24:41 +03:00
TCFDONMEZ 48a7a0dd72 feat: add OTA 2026-09-29 17:04:59 +03:00
TCFDONMEZ 511c166436 feat: add ota and ui changes 2026-09-29 14:32:25 +03:00
TCFDONMEZandClaude Opus 5 c102625306 chore: release 2.2.4
2.2.3 was published with the broken installer, so it is recorded as released
rather than rewritten, and 2.2.4 is the build that can actually install itself.

2.2.4 must be sideloaded once — the fix cannot deliver itself over the path it
fixes. OTA should carry 2.2.5 onward.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:03:18 +03:00
TCFDONMEZandClaude Opus 5 091d26d6d6 fix: install OTA updates through PackageInstaller
The refresh button found 2.2.3, downloaded it, and reported
"Kurulum reddedildi (imza uyuşmuyor?)" for a build signed with exactly the same
platform certificate as the one already running. The signature was never the
problem.

install() shelled out to `pm install -r` with a path inside the app's private
cache. pm does not read the file — it hands the path to the package manager
service, which opens it from its own process and SELinux context, where that
directory is not readable. The install failed for a reason nobody could see:
install() returned a bare boolean, and UpdateHook mapped every false to the one
message it had. The pm output that would have named the real error was captured
into a StringBuilder and discarded.

A PackageInstaller session takes no path. We open the archive ourselves, as the
app, and stream it into the session, so no second process ever has to reach our
cache. Approach taken from merthankaraman/DriveHub_Dort, which hit the same wall
on the same head unit.

Consequences worth knowing when reading a future failure:

- Committing a session is asynchronous, so the refresh button can only say an
  install started. OtaInstallResultReceiver has the verdict, and surfaces the
  package manager's own INSTALL_FAILED_* text rather than guessing.
- STATUS_PENDING_USER_ACTION is not an error. The receiver launches the
  confirmation activity the platform supplies.
- The PendingIntent must be MUTABLE or it arrives with no status extras.

install() now returns an enum, so a certificate mismatch, an unparseable or
wrong-package archive, and a failed session each say what they are.

Also checks the archive's package name against ours, which was a genuine gap:
the platform key signs every app on this unit, so a certificate match alone
never meant the APK was this app.

The `pm install -r` exit-code test goes with the shell-out it covered. Nothing
in the session path is exercisable off-device.

REQUEST_INSTALL_PACKAGES and the receiver are declared in the unstable manifest
only — the stable channel has no updater at all, and its merged manifest was
checked to confirm it gains neither.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:02:55 +03:00
TCFDONMEZandClaude Opus 5 322ebcc4c0 feat: give window closing its own Pencereler tab
The window switch sat on the Upload Service page, which implied a relationship
that does not exist: closing windows needs no ABRP token, no GPS, no upload
interval, and runs in its own service whether or not uploading is on. It is now
a sixth page of the pager, between Araç and Wi-Fi.

Splitting it in two while it moved, because the new page has room the card did
not: the switch and status stay together, and the event log gets its own card,
monospaced and no longer capped at four lines.

setOffscreenPageLimit switches from a hardcoded 4 to panes.length - 1. It was
already one short of the page count and would have gone stale again here.
refreshWindowClose runs on the two-second tick and dereferences views on a page
that may not be on screen, so the pager must not be free to detach them.

Six tabs share the bar where five did. Widget.EV.NavTab already autosizes down
to a 12sp floor, so the labels shrink rather than truncate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:01:57 +03:00
TCFDONMEZandClaude Opus 5 444732438d feat: close all windows when parked and a door opens
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>
2026-09-25 14:00:10 +03:00
TCFDONMEZandClaude a8938c48a5 feat: turn the OTA updater back on, behind a button in the top bar
OtaUpdater was complete and unreachable: upstream's UpdateHook returned false
and had an empty checkInBackground, so nothing ever called the downloader or
the installer. That seam was suspended for an audit of the published suite;
this fork ships its own builds from its own repository, signed with the
platform key the head unit already trusts, so it is live here.

The release URL and the About link now point at this repository. An APK from
anywhere else is signed with a different key, and OtaUpdater.install checks
the archive's certificate against the running app's before calling
pm install -r — a mismatch is deleted rather than installed.

The refresh button sits outside the tab row, next to About, so it cannot read
as a sixth page. It disables itself for the duration: the check, the download
and the install are one background pass, and a second press would start a
second download of the same APK. The result goes to a toast because the check
is reachable from every page and no one page owns the answer.

The stable flavour gains a matching no-op overload. It still contains no
updater at all — the class is absent from that APK rather than disabled — and
the overload exists only so the shared UI compiles in both flavours.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-22 11:35:43 +03:00
TCFDONMEZandClaude 2fd7b2dde1 Fatih'sMG4 — ABRP telemetry for an MG4 EH32 on SWI69
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>
2026-09-22 10:45:00 +03:00