Allow disabling ABRP uploads independently

This commit is contained in:
TCFDONMEZ
2026-10-05 00:28:28 +03:00
parent dbbfb9f803
commit 90982580af
10 changed files with 88 additions and 11 deletions
@@ -202,7 +202,7 @@ public class AbrpUploadService extends Service {
// and something else has to. Idempotent, asynchronous, and absent on a car that does
// not have the map stack — in which case ext_temp simply stays unreported.
requestLocationUpdates();
if (settings.abrpUploadEnabled) requestLocationUpdates();
// Fire the first upload after a short warm-up, then every UPLOAD_INTERVAL_SEC.
// scheduleWithFixedDelay ensures we always get at least UPLOAD_INTERVAL_SEC
@@ -293,12 +293,13 @@ public class AbrpUploadService extends Service {
mainHandler.post(this::requestLocationUpdates);
Log.i(TAG, "Settings reloaded: interval=" + settings.intervalSec + "s"
+ " boostLowSoc=" + settings.boostLowSoc
+ " lowSoc=" + settings.lowSocPercent + "%");
+ " lowSoc=" + settings.lowSocPercent + "%"
+ " abrpUpload=" + settings.abrpUploadEnabled);
}
// Same watchdog for GPS: requestLocationUpdates used to run once in onCreate,
// so a provider that was off at start-up — or dropped later — meant
// position-less telemetry for the rest of the session.
if (!locationUpdatesActive
if (settings.abrpUploadEnabled && !locationUpdatesActive
&& System.currentTimeMillis() - lastLocationRequestMs
> LOCATION_RETRY_INTERVAL_SEC * 1000) {
Log.w(TAG, "Location updates inactive, re-arming");
@@ -306,7 +307,8 @@ public class AbrpUploadService extends Service {
}
// Before the upload, not after: an upload that fails for want of a network is
// the thing this is here to prevent. Cheap and rate-limited when already on.
if (wifi != null && prefs.getBoolean(MainActivity.WIFI_AUTO_KEY, true)) {
if (settings.abrpUploadEnabled && wifi != null
&& prefs.getBoolean(MainActivity.WIFI_AUTO_KEY, true)) {
wifi.ensureConnected(false);
}
doUpload();
@@ -323,6 +325,12 @@ public class AbrpUploadService extends Service {
EnergySnapshot vehicle = energyReader.read(sampleMs);
consumptionTracker.sample(vehicle);
// Keep local consumption history alive while another app owns the ABRP feed.
if (!settings.abrpUploadEnabled) {
updateNotification(getString(R.string.notif_tracking_locally));
return;
}
String token = securePrefs.getString(SecurePrefs.KEY_TOKEN, "").trim();
String apiKey = securePrefs.getString(SecurePrefs.KEY_API_KEY, "").trim();
@@ -663,6 +671,12 @@ public class AbrpUploadService extends Service {
try { locationManager.removeUpdates(locationListener); } catch (Exception ignored) {}
locationUpdatesActive = false;
if (!settings.abrpUploadEnabled) {
lastLocation = null;
Log.i(TAG, "GPS updates stopped while ABRP upload is disabled");
return;
}
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
// Match the upload cadence: a 10 s GPS subscription burned power for
// fixes that were thrown away between uploads.
@@ -50,8 +50,10 @@ public class BootReceiver extends BroadcastReceiver {
SharedPreferences secure = SecurePrefs.get(context);
boolean haveCreds = !secure.getString(SecurePrefs.KEY_TOKEN, "").trim().isEmpty()
&& !secure.getString(SecurePrefs.KEY_API_KEY, "").trim().isEmpty();
boolean uploadEnabled = prefs.getBoolean(UploadSettings.KEY_ABRP_UPLOAD_ENABLED,
UploadSettings.DEFAULT_ABRP_UPLOAD_ENABLED);
if (!enabled || !autostart || !haveCreds) {
if (!enabled || !autostart || (uploadEnabled && !haveCreds)) {
Log.i(TAG, action + " ignored (enabled=" + enabled
+ " autostart=" + autostart + " credentials=" + haveCreds + ")");
return;
@@ -70,6 +70,7 @@ public class MainActivity extends AppCompatActivity {
private TextInputLayout tokenLayout;
private TextInputEditText tokenInput;
private SwitchMaterial serviceSwitch;
private SwitchMaterial abrpUploadSwitch;
private SwitchMaterial autostartSwitch;
private TextView statusText;
private Button testButton;
@@ -250,6 +251,7 @@ public class MainActivity extends AppCompatActivity {
tokenLayout = abrpPane.findViewById(R.id.token_layout);
tokenInput = abrpPane.findViewById(R.id.token_input);
serviceSwitch = servicePane.findViewById(R.id.service_switch);
abrpUploadSwitch = servicePane.findViewById(R.id.abrp_upload_switch);
autostartSwitch = servicePane.findViewById(R.id.autostart_switch);
statusText = servicePane.findViewById(R.id.status_text);
testButton = abrpPane.findViewById(R.id.test_button);
@@ -275,6 +277,9 @@ public class MainActivity extends AppCompatActivity {
apiKeyInput.setText(savedApiKeyOrDefault());
tokenInput.setText(securePrefs.getString(SecurePrefs.KEY_TOKEN, ""));
serviceSwitch.setChecked(prefs.getBoolean("service_enabled", false));
abrpUploadSwitch.setChecked(prefs.getBoolean(
UploadSettings.KEY_ABRP_UPLOAD_ENABLED,
UploadSettings.DEFAULT_ABRP_UPLOAD_ENABLED));
autostartSwitch.setChecked(
prefs.getBoolean(UploadSettings.KEY_AUTOSTART, UploadSettings.DEFAULT_AUTOSTART));
@@ -290,7 +295,9 @@ public class MainActivity extends AppCompatActivity {
boolean enabled = prefs.getBoolean("service_enabled", false);
boolean haveCreds = !securePrefs.getString(SecurePrefs.KEY_TOKEN, "").trim().isEmpty()
&& !securePrefs.getString(SecurePrefs.KEY_API_KEY, "").trim().isEmpty();
if (enabled && haveCreds) {
boolean uploadEnabled = prefs.getBoolean(UploadSettings.KEY_ABRP_UPLOAD_ENABLED,
UploadSettings.DEFAULT_ABRP_UPLOAD_ENABLED);
if (enabled && (!uploadEnabled || haveCreds)) {
// Asked for first, and deliberately: the service declares the `location`
// foreground type, and since API 34 starting it without the permission behind
// that type is a SecurityException in its own onCreate. Started this way round,
@@ -308,7 +315,7 @@ public class MainActivity extends AppCompatActivity {
if (checked) {
String apiKey = textOf(apiKeyInput);
String token = textOf(tokenInput);
if (apiKey.isEmpty() || token.isEmpty()) {
if (abrpUploadSwitch.isChecked() && (apiKey.isEmpty() || token.isEmpty())) {
serviceSwitch.setChecked(false);
if (apiKey.isEmpty()) apiKeyLayout.setError(getString(R.string.api_key_required));
if (token.isEmpty()) tokenLayout.setError(getString(R.string.token_required));
@@ -325,6 +332,22 @@ public class MainActivity extends AppCompatActivity {
}
refreshStatus();
});
abrpUploadSwitch.setOnCheckedChangeListener((btn, checked) -> {
if (checked) {
String apiKey = textOf(apiKeyInput);
String token = textOf(tokenInput);
if (apiKey.isEmpty() || token.isEmpty()) {
abrpUploadSwitch.setChecked(false);
if (apiKey.isEmpty()) apiKeyLayout.setError(getString(R.string.api_key_required));
if (token.isEmpty()) tokenLayout.setError(getString(R.string.token_required));
return;
}
}
prefs.edit().putBoolean(UploadSettings.KEY_ABRP_UPLOAD_ENABLED, checked).apply();
AbrpUploadService.reloadSettings();
refreshStatus();
});
}
private void showAbout() {
@@ -362,6 +385,9 @@ public class MainActivity extends AppCompatActivity {
apiKeyInput.setText(savedApiKeyOrDefault());
tokenInput.setText(securePrefs.getString(SecurePrefs.KEY_TOKEN, ""));
serviceSwitch.setChecked(prefs.getBoolean("service_enabled", false));
abrpUploadSwitch.setChecked(prefs.getBoolean(
UploadSettings.KEY_ABRP_UPLOAD_ENABLED,
UploadSettings.DEFAULT_ABRP_UPLOAD_ENABLED));
autostartSwitch.setChecked(
prefs.getBoolean(UploadSettings.KEY_AUTOSTART, UploadSettings.DEFAULT_AUTOSTART));
// Poll state and log only while the screen is up; onPause cancels it.
@@ -1070,6 +1096,14 @@ public class MainActivity extends AppCompatActivity {
// force-kill because onDestroy never ran.
UploadLog.State state = AbrpUploadService.state();
if (AbrpUploadService.isRunning() && !prefs.getBoolean(
UploadSettings.KEY_ABRP_UPLOAD_ENABLED,
UploadSettings.DEFAULT_ABRP_UPLOAD_ENABLED)) {
statusText.setText(R.string.state_tracking_locally);
statusText.setTextColor(COLOR_OK);
return;
}
switch (state) {
case ERROR:
// Derived from the log, not from the last status alone: a single failed
@@ -15,6 +15,7 @@ final class UploadSettings {
static final String KEY_BOOST_LOW_SOC = "boost_low_soc";
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";
/**
* On by default: an ABRP plan is only useful if it is fed from the moment the drive
@@ -22,6 +23,7 @@ final class UploadSettings {
* service that starts itself must also be switchable off.
*/
static final boolean DEFAULT_AUTOSTART = true;
static final boolean DEFAULT_ABRP_UPLOAD_ENABLED = true;
/**
* Default cadence while driving. 60 s is roughly 1.5 km between points at motorway
@@ -44,18 +46,26 @@ final class UploadSettings {
final int intervalSec;
final boolean boostLowSoc;
final int lowSocPercent;
final boolean abrpUploadEnabled;
UploadSettings(int intervalSec, boolean boostLowSoc, int lowSocPercent) {
this(intervalSec, boostLowSoc, lowSocPercent, DEFAULT_ABRP_UPLOAD_ENABLED);
}
UploadSettings(int intervalSec, boolean boostLowSoc, int lowSocPercent,
boolean abrpUploadEnabled) {
this.intervalSec = intervalSec;
this.boostLowSoc = boostLowSoc;
this.lowSocPercent = lowSocPercent;
this.abrpUploadEnabled = abrpUploadEnabled;
}
static UploadSettings from(SharedPreferences prefs) {
return new UploadSettings(
prefs.getInt(KEY_INTERVAL_SEC, DEFAULT_INTERVAL_SEC),
prefs.getBoolean(KEY_BOOST_LOW_SOC, DEFAULT_BOOST_LOW_SOC),
prefs.getInt(KEY_LOW_SOC_PERCENT, DEFAULT_LOW_SOC_PERCENT));
prefs.getInt(KEY_LOW_SOC_PERCENT, DEFAULT_LOW_SOC_PERCENT),
prefs.getBoolean(KEY_ABRP_UPLOAD_ENABLED, DEFAULT_ABRP_UPLOAD_ENABLED));
}
static UploadSettings defaults() {