From 3f1e1968170cd362ad41cc48784148fddee7efe5 Mon Sep 17 00:00:00 2001 From: Nils Stinnesbeck Date: Sat, 17 Oct 2026 23:52:33 +0200 Subject: [PATCH] App 2.8.0: full screen Immersive full screen while the app is in front: the system's top and bottom bars are hidden, and a swipe from the edge shows them briefly. Re-applied whenever the window regains focus. A "Full screen" switch in the Upload tab (default on) turns it off. On the Android 9 emulator the bars are hidden (system UI flags 0x1706), stay hidden after leaving and returning, and the switch toggles them through the UI. Whether SAIC's own bars on the MG4 obey the flags is only known on the car; if they do not, nothing changes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki --- SPECS.md | 3 ++ app/build.gradle | 4 +- .../java/com/evsuite/abrp/MainActivity.java | 37 ++++++++++++++++++- .../java/com/evsuite/abrp/UploadSettings.java | 7 ++++ .../main/res/layout-w1000dp/pane_service.xml | 2 + app/src/main/res/layout/pane_service.xml | 2 + app/src/main/res/values/strings.xml | 2 + 7 files changed, 54 insertions(+), 3 deletions(-) diff --git a/SPECS.md b/SPECS.md index 0ec7454..d5fa8e9 100644 --- a/SPECS.md +++ b/SPECS.md @@ -114,6 +114,9 @@ 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 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. +- 2026-10-17 — App 2.8.0: immersive full screen (switch in the Upload tab, default on). Hides the + system bars on the emulator; whether SAIC's own bars on the MG4 can be hidden is only known once + tried on the car. - 2026-10-17 — App 2.7.1: the voice key works. The car's key report, read off the on-screen log: `android.intent.extra.hardkey.keycode` (Integer), `.down` (Boolean, a report per press and per release), `.longpress` (Boolean). 2.7.0 looked only for short names and never found the code. diff --git a/app/build.gradle b/app/build.gradle index f38b18d..a050f40 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,8 +34,8 @@ android { applicationId "com.stinnesbeck.nilsmg4" minSdk 27 targetSdk 36 - versionCode 65 - versionName "2.7.1" + versionCode 66 + versionName "2.8.0" // 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. diff --git a/app/src/main/java/com/evsuite/abrp/MainActivity.java b/app/src/main/java/com/evsuite/abrp/MainActivity.java index fae8d78..44fa0b0 100644 --- a/app/src/main/java/com/evsuite/abrp/MainActivity.java +++ b/app/src/main/java/com/evsuite/abrp/MainActivity.java @@ -120,7 +120,7 @@ public class MainActivity extends AppCompatActivity { private TextView connectionStatusText; /** Upload page: live telemetry switch and cadence. */ - private SwitchMaterial liveTelemetrySwitch, boostSwitch, voiceKeySwitch; + private SwitchMaterial liveTelemetrySwitch, boostSwitch, voiceKeySwitch, fullScreenSwitch; private TextView steeringKeysText; private android.widget.Spinner intervalSpinner; private TextInputLayout lowSocLayout; @@ -308,6 +308,35 @@ public class MainActivity extends AppCompatActivity { uiHandler.post(uiRefresh); } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + // The system shows its bars again when focus moves (a dialog, the bars swiped in); + // re-applying on regaining focus keeps the app full screen. + if (hasFocus) applyFullScreen(); + } + + /** + * Immersive full screen: the head unit's top and bottom bars are hidden while the app is + * in front, and a swipe from the edge shows them briefly. On the MG4 those bars are SAIC's + * own (climate strip, hazard and home shortcuts); if the platform keeps them, these flags + * simply have no effect. + */ + @SuppressWarnings("deprecation") // the WindowInsetsController API starts at 30; this is 28 + private void applyFullScreen() { + View decor = getWindow().getDecorView(); + if (UploadSettings.fullScreen(prefs)) { + decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN); + } else { + decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + @Override protected void onPause() { super.onPause(); @@ -798,6 +827,12 @@ public class MainActivity extends AppCompatActivity { voiceKeySwitch.setChecked(UploadSettings.voiceKeyOpensApp(prefs)); voiceKeySwitch.setOnCheckedChangeListener((btn, checked) -> prefs.edit().putBoolean(UploadSettings.KEY_VOICE_KEY_OPENS, checked).apply()); + fullScreenSwitch = servicePane.findViewById(R.id.full_screen_switch); + fullScreenSwitch.setChecked(UploadSettings.fullScreen(prefs)); + fullScreenSwitch.setOnCheckedChangeListener((btn, checked) -> { + prefs.edit().putBoolean(UploadSettings.KEY_FULL_SCREEN, checked).apply(); + applyFullScreen(); + }); liveTelemetrySwitch = servicePane.findViewById(R.id.abrp_upload_switch); intervalSpinner = servicePane.findViewById(R.id.interval_spinner); boostSwitch = servicePane.findViewById(R.id.boost_switch); diff --git a/app/src/main/java/com/evsuite/abrp/UploadSettings.java b/app/src/main/java/com/evsuite/abrp/UploadSettings.java index fd3d717..7a867a3 100644 --- a/app/src/main/java/com/evsuite/abrp/UploadSettings.java +++ b/app/src/main/java/com/evsuite/abrp/UploadSettings.java @@ -16,6 +16,13 @@ final class UploadSettings { static final String KEY_LOW_SOC_PERCENT = "low_soc_percent"; static final String KEY_AUTOSTART = "autostart"; static final String KEY_ABRP_UPLOAD_ENABLED = "abrp_upload_enabled"; + /** Hide the head unit's system bars while the app is open. */ + static final String KEY_FULL_SCREEN = "full_screen"; + + static boolean fullScreen(SharedPreferences prefs) { + return prefs.getBoolean(KEY_FULL_SCREEN, true); + } + /** A double press of the steering wheel's voice key opens the app. */ static final String KEY_VOICE_KEY_OPENS = "voice_key_opens_app"; diff --git a/app/src/main/res/layout-w1000dp/pane_service.xml b/app/src/main/res/layout-w1000dp/pane_service.xml index ea2863f..2ed63fd 100644 --- a/app/src/main/res/layout-w1000dp/pane_service.xml +++ b/app/src/main/res/layout-w1000dp/pane_service.xml @@ -6,6 +6,8 @@ + + diff --git a/app/src/main/res/layout/pane_service.xml b/app/src/main/res/layout/pane_service.xml index b14540c..fbe693c 100644 --- a/app/src/main/res/layout/pane_service.xml +++ b/app/src/main/res/layout/pane_service.xml @@ -6,6 +6,8 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9f7431d..6d4d5ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -107,6 +107,8 @@ Connection timed out Server error (HTTP %1$d) + Full screen + Hides the car\'s top and bottom bars while this app is open. Swipe in from the top or bottom edge to show them briefly, e.g. for the climate controls. Double-press the voice key to open this app Press the voice key on the steering wheel twice quickly. The voice assistant may still react to the presses. Steering-wheel keys