1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-04 09:17:29 +01:00

Garmin protocol: show AGPS data status in settings

This commit is contained in:
kuhy 2024-05-03 16:36:16 +02:00 committed by Daniele Gobbetti
parent 91c2408170
commit 880e18fffc
12 changed files with 169 additions and 17 deletions

View File

@ -231,6 +231,7 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_AGPS_EXPIRY_REMINDER_TIME = "pref_agps_expiry_reminder_time";
public static final String PREF_AGPS_UPDATE_TIME = "pref_agps_update_time";
public static final String PREF_AGPS_EXPIRE_TIME = "pref_agps_expire_time";
public static final String PREF_AGPS_STATUS = "pref_agps_status";
public static final String PREF_FIND_PHONE = "prefs_find_phone";
public static final String PREF_FIND_PHONE_DURATION = "prefs_find_phone_duration";

View File

@ -16,7 +16,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.file.GarminAgpsFile;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps.GarminAgpsFile;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;

View File

@ -11,6 +11,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
@ -54,6 +55,9 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
final List<Integer> location = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.LOCATION);
location.add(R.xml.devicesettings_workout_send_gps_to_band);
if (supportsAgpsUpdates()) {
location.add(R.xml.devicesettings_garmin_agps);
}
final List<Integer> connection = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CONNECTION);
connection.add(R.xml.devicesettings_high_mtu);
@ -64,6 +68,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
return deviceSpecificSettings;
}
@Override
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(GBDevice device) {
return new GarminSettingsCustomizer();
}
@Override
public boolean supportsActivityDataFetching() {
return true;

View File

@ -0,0 +1,72 @@
package nodomain.freeyourgadget.gadgetbridge.devices.garmin;
import android.os.Parcel;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps.GarminAgpsStatus;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
@Override
public void onPreferenceChange(Preference preference, DeviceSpecificSettingsHandler handler) {
}
@Override
public void customizeSettings(DeviceSpecificSettingsHandler handler, Prefs prefs) {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
final Preference prefAgpsUpdateTime = handler.findPreference(DeviceSettingsPreferenceConst.PREF_AGPS_UPDATE_TIME);
if (prefAgpsUpdateTime != null) {
final long ts = prefs.getLong(DeviceSettingsPreferenceConst.PREF_AGPS_UPDATE_TIME, 0L);
if (ts > 0) {
prefAgpsUpdateTime.setSummary(sdf.format(new Date(ts)));
} else {
prefAgpsUpdateTime.setSummary(handler.getContext().getString(R.string.unknown));
}
}
final Preference prefAgpsStatus = handler.findPreference(DeviceSettingsPreferenceConst.PREF_AGPS_STATUS);
if (prefAgpsStatus != null) {
final GarminAgpsStatus agpsStatus = GarminAgpsStatus.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_AGPS_STATUS, GarminAgpsStatus.MISSING.name()));
prefAgpsStatus.setSummary(handler.getContext().getString(agpsStatus.getText()));
}
}
@Override
public Set<String> getPreferenceKeysWithSummary() {
return Collections.emptySet();
}
public static final Creator<GarminSettingsCustomizer> CREATOR = new Creator<GarminSettingsCustomizer>() {
@Override
public GarminSettingsCustomizer createFromParcel(final Parcel in) {
return new GarminSettingsCustomizer();
}
@Override
public GarminSettingsCustomizer[] newArray(final int size) {
return new GarminSettingsCustomizer[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
}
}

View File

@ -27,7 +27,9 @@ import java.util.TimerTask;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminAgpsInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminPreferences;
import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.GarminCapability;
@ -48,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiSmartProto;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps.GarminAgpsStatus;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.ICommunicator;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v1.CommunicatorV1;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v2.CommunicatorV2;
@ -629,6 +632,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
final File agpsFile = getAgpsFile();
try (FileOutputStream outputStream = new FileOutputStream(agpsFile)) {
outputStream.write(agpsHandler.getFile().getBytes());
evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(
DeviceSettingsPreferenceConst.PREF_AGPS_STATUS, GarminAgpsStatus.PENDING.name()
));
LOG.info("AGPS file successfully written to the cache directory.");
} catch (final IOException e) {
LOG.error("Failed to write AGPS bytes to temporary directory", e);

View File

@ -1,7 +1,8 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.http;
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Instant;
import java.io.File;
import java.io.FileInputStream;
@ -11,21 +12,22 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.file.GarminAgpsDataType;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GBTarFile;
public class EphemerisHandler {
private static final Logger LOG = LoggerFactory.getLogger(EphemerisHandler.class);
public class AgpsHandler {
private static final Logger LOG = LoggerFactory.getLogger(AgpsHandler.class);
private static final String QUERY_CONSTELLATIONS = "constellations";
private final GarminSupport deviceSupport;
public EphemerisHandler(GarminSupport deviceSupport) {
public AgpsHandler(GarminSupport deviceSupport) {
this.deviceSupport = deviceSupport;
}
public byte[] handleEphemerisRequest(final String path, final Map<String, String> query) {
public byte[] handleAgpsRequest(final String path, final Map<String, String> query) {
try {
if (!query.containsKey(QUERY_CONSTELLATIONS)) {
LOG.debug("Query does not contain information about constellations; skipping request.");
@ -45,10 +47,16 @@ public class EphemerisHandler {
final GarminAgpsDataType garminAgpsDataType = GarminAgpsDataType.valueOf(constellation);
if (!tarFile.containsFile(garminAgpsDataType.getFileName())) {
LOG.error("AGPS archive is missing requested file: {}", garminAgpsDataType.getFileName());
deviceSupport.evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(
DeviceSettingsPreferenceConst.PREF_AGPS_STATUS, GarminAgpsStatus.ERROR.name()
));
return null;
}
} catch (IllegalArgumentException e) {
LOG.error("Device requested unsupported AGPS data type: {}", constellation);
deviceSupport.evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(
DeviceSettingsPreferenceConst.PREF_AGPS_STATUS, GarminAgpsStatus.ERROR.name()
));
return null;
}
}
@ -56,7 +64,10 @@ public class EphemerisHandler {
return rawBytes;
}
} catch (IOException e) {
LOG.error("Unable to obtain ephemeris data.", e);
LOG.error("Unable to obtain AGPS data.", e);
deviceSupport.evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(
DeviceSettingsPreferenceConst.PREF_AGPS_STATUS, GarminAgpsStatus.ERROR.name()
));
return null;
}
}
@ -64,6 +75,12 @@ public class EphemerisHandler {
public Callable<Void> getOnDataSuccessfullySentListener() {
return () -> {
LOG.info("AGPS data successfully sent to the device.");
deviceSupport.evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(
DeviceSettingsPreferenceConst.PREF_AGPS_UPDATE_TIME, Instant.now().toEpochMilli()
));
deviceSupport.evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(
DeviceSettingsPreferenceConst.PREF_AGPS_STATUS, GarminAgpsStatus.CURRENT.name()
));
if (deviceSupport.getAgpsFile().delete()) {
LOG.info("AGPS data was deleted from the cache folder.");
}

View File

@ -1,4 +1,4 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.file;
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps;
public enum GarminAgpsDataType {
GLONASS("CPE_GLO.BIN"), QZSS("CPE_QZSS.BIN"), GPS("CPE_GPS.BIN"),

View File

@ -1,4 +1,4 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.file;
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,23 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps;
import androidx.annotation.StringRes;
import nodomain.freeyourgadget.gadgetbridge.R;
public enum GarminAgpsStatus {
MISSING(R.string.agps_status_missing), // AGPS data file was not yet installed
PENDING(R.string.agps_status_pending), // AGPS data file is waiting for installation
CURRENT(R.string.agps_status_current), // AGPS data was successfully installed
ERROR(R.string.agps_status_error); // Unable to install AGPS data file
private final @StringRes int text;
GarminAgpsStatus(@StringRes int text) {
this.text = text;
}
public @StringRes int getText() {
return text;
}
}

View File

@ -21,6 +21,7 @@ import java.util.zip.GZIPOutputStream;
import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiHttpService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps.AgpsHandler;
import nodomain.freeyourgadget.gadgetbridge.util.HttpUtils;
public class HttpHandler {
@ -30,10 +31,10 @@ public class HttpHandler {
//.serializeNulls()
.create();
private final EphemerisHandler ephemerisHandler;
private final AgpsHandler agpsHandler;
public HttpHandler(GarminSupport deviceSupport) {
ephemerisHandler = new EphemerisHandler(deviceSupport);
agpsHandler = new AgpsHandler(deviceSupport);
}
public GdiHttpService.HttpService handle(final GdiHttpService.HttpService httpService) {
@ -78,13 +79,13 @@ public class HttpHandler {
LOG.debug("Weather response: {}", json);
return createRawResponse(rawRequest, json.getBytes(StandardCharsets.UTF_8), "application/json", null);
} else if (path.startsWith("/ephemeris/")) {
LOG.info("Got ephemeris request for {}", path);
final byte[] ephemerisData = ephemerisHandler.handleEphemerisRequest(path, query);
if (ephemerisData == null) {
LOG.info("Got AGPS request for {}", path);
final byte[] agpsData = agpsHandler.handleAgpsRequest(path, query);
if (agpsData == null) {
return null;
}
LOG.debug("Successfully obtained ephemeris data (length: {})", ephemerisData.length);
return createRawResponse(rawRequest, ephemerisData, "application/x-tar", ephemerisHandler.getOnDataSuccessfullySentListener());
LOG.debug("Successfully obtained AGPS data (length: {})", agpsData.length);
return createRawResponse(rawRequest, agpsData, "application/x-tar", agpsHandler.getOnDataSuccessfullySentListener());
} else {
LOG.warn("Unhandled path {}", urlString);
return null;

View File

@ -535,6 +535,11 @@
<string name="pref_agps_expiry_reminder_time">AGPS Expiry Reminder Time</string>
<string name="pref_agps_update_time">AGPS Update Time</string>
<string name="pref_agps_expire_time">AGPS Expire Time</string>
<string name="pref_agps_status">AGPS Status</string>
<string name="agps_status_missing">Missing</string>
<string name="agps_status_pending">Pending</string>
<string name="agps_status_current">Current</string>
<string name="agps_status_error">Error</string>
<string name="pref_camera_remote_title">Camera Remote</string>
<string name="pref_camera_remote_summary">Allows the watch to trigger the phone\'s camera</string>
<string name="pref_morning_updates_title">Morning Updates</string>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:icon="@drawable/ic_gps_edit"
android:key="pref_header_agps"
android:title="@string/pref_agps_header">
<Preference
android:key="pref_agps_status"
android:title="@string/pref_agps_status" />
<Preference
android:key="pref_agps_update_time"
android:title="@string/pref_agps_update_time" />
<!-- <Preference-->
<!-- android:key="pref_agps_expire_time"-->
<!-- android:title="@string/pref_agps_expire_time" />-->
</PreferenceCategory>
</androidx.preference.PreferenceScreen>