diff --git a/AGENTS.md b/AGENTS.md index 642d400..029daff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,15 @@ Context for AI agents and new contributors working in this repository. EVABRPUploader is part of **EVSuite**. The workspace `AGENTS.md` and normative workspace `DESIGN.md` apply; this file contains only telemetry-specific additions. +## App-specific visual direction (2.2.6) + +The user explicitly requested a permanent OLED-black theme, charcoal glass-like cards, +muted gold and cyan accents. These app resources intentionally override the shared +DESIGN.md palette, DayNight preference and flat-surface rules. Do not overwrite them +with the shared token generator. Use static gradients rather than continuous effects; +keep large touch targets, readable text and null telemetry shown as a dash. +Wide layouts start at 1000dp; narrower windows use a bottom navigation bar. + ## What this is An Android Automotive app for the **MG4 (SAIC eh32)** that reads vehicle telemetry and @@ -15,11 +24,8 @@ Fork of Leon Kernan's `ABRP_Uploader`. See `LICENSE.md` — the licence status i ## Non-negotiables -1. **Telemetry is read-only.** The explicitly requested SWI69 WinClose integration is - the only vehicle-write exception: an opt-in service closes the four windows on the - WinClose door callback, requiring P and readable 0 km/h before and during the pulse. - It uses WinClose's CarState/VSM interfaces and the shared VehicleWriteGate; no other - vehicle settings may be written. Always send STOP when an active pulse exits. +1. **Telemetry is read-only.** No vehicle settings may be written. Window automation + was removed in 2.2.5 at the user's request, including its service and door listener. 2. **A failed read is not a zero.** Getters return `Integer`/`Float`/`Boolean` and `null` means "could not read". Null fields are omitted from the payload. Sending 0 for SOC tells ABRP the battery is empty and wrecks a live route plan. @@ -43,9 +49,6 @@ UploadLog last 20 attempts + derived service state — pure, unit-test SecurePrefs credentials in EncryptedSharedPreferences, migrates plaintext AbrpApi HTTP; credentials in the POST body, never the query string MainActivity configuration, service state, upload log -WindowCloseService independent, opt-in foreground service; no ABRP/GPS dependency -WinCloseHardware SWI69 adapter derived from the sibling winclose/WindowHardware.kt -WindowClosePulse bounded UP/STOP sequence; pure, unit-tested ``` **Put logic in the pure classes.** Anything Android-free can be unit-tested on the JVM; @@ -57,7 +60,7 @@ anything inside the service effectively cannot be tested without a car. |---|---|---| | `main` | everything shared | all builds | | `stable` | `UpdateHook` no-op | stable channel | -| `unstable` | updater policy code; trigger suspended during the suite audit | unstable channel | +| `unstable` | active GitHub pre-release updater and PackageInstaller result receiver | unstable channel | | `debug` | `VhalProbe` — enumerates the whole VHAL | debug builds only | | `testUnstable` | OTA policy tests | test only | diff --git a/CHANGELOG.md b/CHANGELOG.md index f7943ca..8502a62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,36 @@ All notable changes to this project are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.6] - 2026-09-29 + +### Changed + +- Redesigned all five pages with an OLED-black background, charcoal translucent panels, + muted gold navigation and cyan controls. Static gradients avoid continuous rendering. +- Vehicle overview is now the first page. Energy, temperatures and tyre pressures appear + at the top, with compact battery, range, power, speed and energy cards below. + Unknown data stays a dash. +- Added a wide-screen navigation rail and paired cards, with stacked pages and bottom + navigation for narrower windows. Headers follow the selected page. +- Restyled credentials, transfer controls, frequency picker, Wi-Fi, logs and About; + the interface stays dark regardless of the system theme. + +## [2.2.5] - 2026-09-29 + +### Removed + +- Window closing: its page, service, door listener, pulse logic, resources and dedicated + permissions are removed. Previous opt-in preferences cannot restart the removed service. + +### Fixed + +- OTA checks now report API, network and response errors instead of saying the app is current. +- Release selection considers every asset, choosing the newest eligible version even when + it appears later in the same release. Drafts and stable releases are excluded. +- Concurrent launch/button update checks no longer start duplicate downloads. Installer + session handles are closed after commit; failed sessions are abandoned. +- Wi-Fi permission refusals are handled explicitly, satisfying the Android lint checks. + ## [2.2.4] - 2026-09-25 ### Fixed diff --git a/OTA.md b/OTA.md index cae10fb..56f5adb 100644 --- a/OTA.md +++ b/OTA.md @@ -3,6 +3,25 @@ How to ship an update the car will install by itself. Written for whoever does this next — probably me, having forgotten the details. +## First upgrade from 2.2.3 or older + +Install the platform-signed 2.2.6 APK manually once if the car still runs 2.2.3 or older. +Those builds use the old `pm install` path; publishing a newer APK does not repair the +installer already running on the car. Builds from 2.2.4 use `PackageInstaller` sessions. +Keep the unstable package ID, system shared UID and signing certificate when upgrading. + +The 2.2.5 build removes window automation. It also distinguishes failed release checks +from a successful check with no newer release. On 2026-09-29, GitHub still published only +2.2.3 at the last live check. The local 2.2.6 build includes these fixes and the redesigned +UI; it becomes available to OTA only after publication as a pre-release asset named +`FatihsMG4-unstable-2.2.6.apk`. + +The vehicle artifact uses `com.evsuite.abrp.unstable`, `android.uid.system`, and the +DriveHub_Kamera platform key. Its certificate SHA-256 must be +`c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8`. +The stable build used for a Samsung UI preview is a separate package without OTA or the +vehicle's shared UID; do not use that preview APK for the MG4 upgrade. + ## What the updater actually looks for `OtaUpdater.check()` reads `https://api.github.com/repos/fatihdonmezdev/MG4ABRP/releases` @@ -30,11 +49,11 @@ The version comes from the **asset filename**, not the git tag. The tag can stay 1. **Raise the version.** `app/build.gradle`, `defaultConfig`: ```gradle - versionCode 20 // must increase — Android orders updates by this, not by name - versionName "2.2.3" + versionCode 23 // must increase — Android orders updates by this, not by name + versionName "2.2.6" ``` - The unstable flavour appends its own suffix, so `2.2.3` becomes `2.2.3.0-unstable`. + The unstable flavour appends its own suffix, so `2.2.6` becomes `2.2.6.0-unstable`. Check what the installed build reports before picking a number: the app's About dialog shows it, and `isNewer` compares numerically segment by segment. @@ -57,7 +76,7 @@ The version comes from the **asset filename**, not the git tag. The tag can stay "$BT/apksigner" sign \ --key "$KEYS/platform.pk8" \ --cert "$KEYS/platform.x509.pem" \ - --out FatihsMG4-unstable-2.2.3.apk \ + --out FatihsMG4-unstable-2.2.6.apk \ aligned.apk rm aligned.apk @@ -69,7 +88,7 @@ The version comes from the **asset filename**, not the git tag. The tag can stay 4. **Verify the signer** before uploading. It must say `CN=Android`: ```bash - "$BT/apksigner" verify --print-certs FatihsMG4-unstable-2.2.3.apk | grep DN: + "$BT/apksigner" verify --print-certs FatihsMG4-unstable-2.2.6.apk ``` 5. **Publish.** On github.com/fatihdonmezdev/MG4ABRP → Releases → Draft a new release: diff --git a/README.md b/README.md index 20d3a73..5c38800 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,15 @@ unit and talks to ABRP directly. - [Credits](#credits) ## Screenshots + +Version 2.2.6 introduces an OLED-black interface with charcoal cards, muted gold and +cyan accents, a vehicle overview, and responsive side or bottom navigation. This +app-specific theme intentionally overrides the shared EVSuite visual tokens. +Tablet UI previews at 1280×720; vehicle readings are unavailable on this device. +

- EVABRPUploader screenshot 1 - EVABRPUploader screenshot 2 + OLED vehicle overview with energy and tyre panels above compact metrics + Dark ABRP account configuration

--- @@ -78,9 +84,8 @@ same rule in time as well as in value: a GPS fix older than `max(5 min, 3 x your interval)` is dropped rather than resent, so a stale fix cannot report the car back at the place it set off from. -Telemetry only reads the car. An optional SWI69 WinClose integration can close the four -windows while parked; this is the only vehicle-write feature. See configuration below -and [`SECURITY.md`](SECURITY.md). +Telemetry only reads the car. Version 2.2.5 removes window automation entirely; there +is no window service, door listener or window command path in the app. ## Install The MG4 head unit hides Settings and APK install. The known route in: @@ -136,39 +141,6 @@ put them in a text file and tap **Import file** — see [Config file](#config-fi ## Configuration -On its own **Pencereler** page — not under Upload Service, since it shares nothing with -ABRP — **Kapı açılınca camları kapat** enables the WinClose integration. It is off by -default and restarts with the head unit once enabled; no ABRP token, GPS permission, -speed threshold or driving timer is needed. - -The integration is adapted from the sibling WinClose `WindowHardware.kt`: SWI69 -`CarStateClient` door callback (transaction 6, value 0) triggers the VSM's -`setVehicleWindowStatus` on all four areas. WinClose describes this as the driver-door -signal; passenger-door coverage has not been established. No alternative door-property -source is used. The car must be in P with readable speed exactly 0 km/h. Comfort windows -receive UP (1) every 120 ms for five seconds, then STOP (0). P and speed are checked -during the pulse too. Cancellation, a failed command, or leaving P/stationary state ends -the pulse and attempts STOP on all windows. Duplicate door events and openings during -an active pulse do not extend it, and WinClose's 60-second cooldown means reaching back -into the car does not run a second pulse. A partial wake lock is held across the pulse, -because the close fires exactly as the head unit is heading for suspend. - -The feature is SWI69-only and needs the platform signature: the service stops itself on -any other known generation, and the switch is disabled when the process does not hold -system uid (only the unstable flavor declares `android.uid.system`). - -**No cancellation is possible on a Comfort trim.** Its window motors carry no position -sensor, so nothing can observe you working the switch, and there is no way to abort a -pulse once it starts. Verify the motors' pinch behaviour on a parked car before relying -on this — the windows travel while the driver's door is open. - -No position sensor is assumed: a completed command is not confirmation that the windows -closed. The existing WinClose pulse duration is the starting value, not a manufacturer -guarantee. Vehicle testing of timing, permissions and motor behavior is still required. -The APK keeps its existing application IDs and signing configuration (the unstable flavor -already uses `android.uid.system`). WinClose's source license is unspecified; see its -README before redistribution. - | Setting | Default | Notes | |---|---|---| | Upload frequency | 60 s | 15 / 30 / 60 / 120 / 300 s. Applies while driving | diff --git a/SECURITY.md b/SECURITY.md index 9b4478a..301f476 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -15,9 +15,7 @@ was moving. A proof of concept helps; a working exploit is not required. - Anything that lets another app on the head unit read the stored ABRP credentials. - Anything that gets attacker-controlled code installed through the unstable updater. -- Any vehicle write outside the explicitly enabled SWI69 WinClose feature, or a window - close that bypasses P/readable-zero-speed checks, exceeds its five-second pulse, or - fails to attempt STOP on every window after interruption or failure. +- Any vehicle-setting write. The app is read-only; window automation was removed in 2.2.5. - Telemetry that leaks credentials into logs, URLs or crash reports. ## What is not in scope @@ -36,6 +34,3 @@ was moving. A proof of concept helps; a working exploit is not required. checks fail closed. The stable channel does not contain the updater at all. - The VHAL probe helpers, which enumerate the whole vehicle abstraction layer, exist only in debug builds. -- Window closing uses WinClose's SWI69 CarState callback and VSM commands. Its service is - not exported, has its own opt-in switch, and holds no wake lock or alarm. It does not - assert that a sensorless window has physically closed or that anti-pinch is available. diff --git a/app/build.gradle b/app/build.gradle index d96942f..ca456d4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,8 +34,8 @@ android { applicationId "com.evsuite.abrp" minSdk 27 targetSdk 36 - versionCode 21 - versionName "2.2.4" + versionCode 23 + versionName "2.2.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3d2bce4..545d7f6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,12 +5,6 @@ - - - - @@ -72,15 +66,6 @@ android:exported="false" android:foregroundServiceType="location" /> - - - - - - - - + diff --git a/app/src/main/res/color/nav_tab_foreground.xml b/app/src/main/res/color/nav_tab_foreground.xml index 5b5d26f..64477ea 100644 --- a/app/src/main/res/color/nav_tab_foreground.xml +++ b/app/src/main/res/color/nav_tab_foreground.xml @@ -1,7 +1 @@ - - - - - - + diff --git a/app/src/main/res/color/nav_tab_stroke.xml b/app/src/main/res/color/nav_tab_stroke.xml index 62b03a6..62d6c16 100644 --- a/app/src/main/res/color/nav_tab_stroke.xml +++ b/app/src/main/res/color/nav_tab_stroke.xml @@ -1,7 +1 @@ - - - - - - + diff --git a/app/src/main/res/color/switch_thumb.xml b/app/src/main/res/color/switch_thumb.xml new file mode 100644 index 0000000..deef655 --- /dev/null +++ b/app/src/main/res/color/switch_thumb.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/color/switch_track.xml b/app/src/main/res/color/switch_track.xml new file mode 100644 index 0000000..0154b5b --- /dev/null +++ b/app/src/main/res/color/switch_track.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/drawable/bg_battery_card.xml b/app/src/main/res/drawable/bg_battery_card.xml new file mode 100644 index 0000000..bf08638 --- /dev/null +++ b/app/src/main/res/drawable/bg_battery_card.xml @@ -0,0 +1 @@ + diff --git a/app/src/main/res/drawable/bg_glass.xml b/app/src/main/res/drawable/bg_glass.xml new file mode 100644 index 0000000..ed8c8af --- /dev/null +++ b/app/src/main/res/drawable/bg_glass.xml @@ -0,0 +1 @@ + diff --git a/app/src/main/res/drawable/bg_input_panel.xml b/app/src/main/res/drawable/bg_input_panel.xml new file mode 100644 index 0000000..1b78256 --- /dev/null +++ b/app/src/main/res/drawable/bg_input_panel.xml @@ -0,0 +1 @@ + diff --git a/app/src/main/res/drawable/bg_sidebar.xml b/app/src/main/res/drawable/bg_sidebar.xml new file mode 100644 index 0000000..a176cdc --- /dev/null +++ b/app/src/main/res/drawable/bg_sidebar.xml @@ -0,0 +1 @@ + diff --git a/app/src/main/res/drawable/ic_chevron_down.xml b/app/src/main/res/drawable/ic_chevron_down.xml new file mode 100644 index 0000000..8cd2a5d --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_down.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000..91a111e --- /dev/null +++ b/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1 @@ + diff --git a/app/src/main/res/drawable/ic_tab_windows.xml b/app/src/main/res/drawable/ic_tab_windows.xml deleted file mode 100644 index 4d4babf..0000000 --- a/app/src/main/res/drawable/ic_tab_windows.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_vehicle_outline.xml b/app/src/main/res/drawable/ic_vehicle_outline.xml new file mode 100644 index 0000000..f1e7624 --- /dev/null +++ b/app/src/main/res/drawable/ic_vehicle_outline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout-w1000dp/activity_main.xml b/app/src/main/res/layout-w1000dp/activity_main.xml new file mode 100644 index 0000000..5fd47e6 --- /dev/null +++ b/app/src/main/res/layout-w1000dp/activity_main.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-w1000dp/pane_abrp.xml b/app/src/main/res/layout-w1000dp/pane_abrp.xml new file mode 100644 index 0000000..8bd2c75 --- /dev/null +++ b/app/src/main/res/layout-w1000dp/pane_abrp.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-w1000dp/pane_service.xml b/app/src/main/res/layout-w1000dp/pane_service.xml new file mode 100644 index 0000000..5a6479b --- /dev/null +++ b/app/src/main/res/layout-w1000dp/pane_service.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-w1000dp/pane_vehicle.xml b/app/src/main/res/layout-w1000dp/pane_vehicle.xml new file mode 100644 index 0000000..548c816 --- /dev/null +++ b/app/src/main/res/layout-w1000dp/pane_vehicle.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-w1000dp/pane_wifi.xml b/app/src/main/res/layout-w1000dp/pane_wifi.xml new file mode 100644 index 0000000..a6485be --- /dev/null +++ b/app/src/main/res/layout-w1000dp/pane_wifi.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-w600dp/dialog_about.xml b/app/src/main/res/layout-w600dp/dialog_about.xml new file mode 100644 index 0000000..d0994a6 --- /dev/null +++ b/app/src/main/res/layout-w600dp/dialog_about.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 17ae262..57e26b5 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,135 +1,22 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_about.xml b/app/src/main/res/layout/dialog_about.xml index 84ed020..5b4a7ac 100644 --- a/app/src/main/res/layout/dialog_about.xml +++ b/app/src/main/res/layout/dialog_about.xml @@ -1,142 +1,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/pane_abrp.xml b/app/src/main/res/layout/pane_abrp.xml index 2835442..3f6065f 100644 --- a/app/src/main/res/layout/pane_abrp.xml +++ b/app/src/main/res/layout/pane_abrp.xml @@ -1,153 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -