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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
2026-10-17 23:52:33 +02:00
co-authored by Claude Opus 5.5
parent 55710fb722
commit 3f1e196817
7 changed files with 54 additions and 3 deletions
+3
View File
@@ -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.
+2 -2
View File
@@ -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.
@@ -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);
@@ -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";
@@ -6,6 +6,8 @@
<TextView android:id="@+id/status_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_input_panel" android:padding="16dp" android:layout_marginTop="20dp" android:layout_marginBottom="12dp" android:text="@string/state_stopped" android:textAppearance="@style/Text.EV.Body" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/abrp_upload_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_abrp_upload_enabled" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_abrp_upload_enabled" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/full_screen_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_full_screen" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_full_screen" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/voice_key_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_voice_key" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_voice_key" android:textAppearance="@style/Text.EV.Secondary" />
</LinearLayout>
+2
View File
@@ -6,6 +6,8 @@
<TextView android:id="@+id/status_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_input_panel" android:padding="16dp" android:layout_marginTop="20dp" android:layout_marginBottom="12dp" android:text="@string/state_stopped" android:textAppearance="@style/Text.EV.Body" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/abrp_upload_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_abrp_upload_enabled" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_abrp_upload_enabled" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/full_screen_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_full_screen" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_full_screen" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/voice_key_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_voice_key" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_voice_key" android:textAppearance="@style/Text.EV.Secondary" />
</LinearLayout>
+2
View File
@@ -107,6 +107,8 @@
<string name="conn_err_timeout">Connection timed out</string>
<string name="conn_err_http">Server error (HTTP %1$d)</string>
<string name="label_full_screen">Full screen</string>
<string name="hint_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.</string>
<string name="label_voice_key">Double-press the voice key to open this app</string>
<string name="hint_voice_key">Press the voice key on the steering wheel twice quickly. The voice assistant may still react to the presses.</string>
<string name="label_steering_keys">Steering-wheel keys</string>