App 2.6.0: floating button to open the app
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
This commit is contained in:
@@ -120,7 +120,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
private TextView connectionStatusText;
|
||||
|
||||
/** Upload page: live telemetry switch and cadence. */
|
||||
private SwitchMaterial liveTelemetrySwitch, boostSwitch;
|
||||
private SwitchMaterial liveTelemetrySwitch, boostSwitch, floatingButtonSwitch;
|
||||
private View overlayPermissionNote;
|
||||
private android.widget.Spinner intervalSpinner;
|
||||
private TextInputLayout lowSocLayout;
|
||||
private TextInputEditText lowSocInput;
|
||||
@@ -303,6 +304,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
super.onResume();
|
||||
// Poll state and log only while the screen is up; onPause cancels it.
|
||||
uiHandler.post(uiRefresh);
|
||||
FloatingButton.setAppInForeground(true);
|
||||
refreshOverlayPermissionNote();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -310,6 +313,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
super.onPause();
|
||||
// Stop polling off-screen: this activity has no reason to spend cycles then.
|
||||
uiHandler.removeCallbacks(uiRefresh);
|
||||
FloatingButton.setAppInForeground(false);
|
||||
}
|
||||
|
||||
// ---------- Update check ----------
|
||||
@@ -791,6 +795,22 @@ public class MainActivity extends AppCompatActivity {
|
||||
// ---------- Upload page ----------
|
||||
|
||||
private void bindServicePane() {
|
||||
floatingButtonSwitch = servicePane.findViewById(R.id.floating_button_switch);
|
||||
overlayPermissionNote = servicePane.findViewById(R.id.overlay_permission_note);
|
||||
floatingButtonSwitch.setChecked(UploadSettings.floatingButtonEnabled(prefs));
|
||||
floatingButtonSwitch.setOnCheckedChangeListener((btn, checked) -> {
|
||||
prefs.edit().putBoolean(UploadSettings.KEY_FLOATING_BUTTON, checked).apply();
|
||||
FloatingButton.refresh();
|
||||
refreshOverlayPermissionNote();
|
||||
});
|
||||
servicePane.findViewById(R.id.overlay_permission_button).setOnClickListener(v -> {
|
||||
try {
|
||||
startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + getPackageName())));
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.overlay_settings_missing, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
liveTelemetrySwitch = servicePane.findViewById(R.id.abrp_upload_switch);
|
||||
intervalSpinner = servicePane.findViewById(R.id.interval_spinner);
|
||||
boostSwitch = servicePane.findViewById(R.id.boost_switch);
|
||||
@@ -847,6 +867,16 @@ public class MainActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The note under the floating-button switch: only when the button is wanted but this build
|
||||
* may not draw over other apps (the car build may, through the system UID).
|
||||
*/
|
||||
private void refreshOverlayPermissionNote() {
|
||||
if (overlayPermissionNote == null) return;
|
||||
boolean missing = UploadSettings.floatingButtonEnabled(prefs) && !FloatingButton.canDraw(this);
|
||||
overlayPermissionNote.setVisibility(missing ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
/** Clamped to 1-99: 0 would never trigger and 100 would boost permanently. */
|
||||
private void saveLowSocThreshold() {
|
||||
CharSequence raw = lowSocInput.getText();
|
||||
|
||||
Reference in New Issue
Block a user