From 159ebfd8919304039047271dc44291146090a1cf Mon Sep 17 00:00:00 2001 From: "Martin.JM" Date: Sun, 18 Aug 2024 17:32:35 +0200 Subject: [PATCH] Huawei: Workout GPS synchronization --- .../gadgetbridge/daogen/GBDaoGenerator.java | 12 +- .../schema/GadgetbridgeUpdate_79.java | 38 +++ .../devices/huawei/HuaweiBRCoordinator.java | 23 +- .../devices/huawei/HuaweiCoordinator.java | 43 +++ .../devices/huawei/HuaweiGpsParser.java | 102 +++++++ .../devices/huawei/HuaweiLECoordinator.java | 23 +- .../devices/huawei/packets/DeviceConfig.java | 2 + .../huawei/packets/FileDownloadService0A.java | 37 ++- .../huawei/packets/FileDownloadService2C.java | 5 + .../huawei/HuaweiFileDownloadManager.java | 267 ++++++++++++----- .../devices/huawei/HuaweiSupportProvider.java | 281 ++++++++++++++---- .../devices/huawei/HuaweiWorkoutGbParser.java | 9 + .../huawei/requests/GetFileBlockRequest.java | 18 +- .../GetFileDownloadCompleteRequest.java | 6 +- .../requests/GetFileDownloadInitRequest.java | 22 +- .../huawei/requests/GetFileInfoRequest.java | 8 +- .../requests/GetFileParametersRequest.java | 7 +- .../requests/GetSettingRelatedRequest.java | 1 + .../requests/GetWorkoutDataRequest.java | 1 + .../requests/GetWorkoutPaceRequest.java | 1 + .../requests/GetWorkoutTotalsRequest.java | 1 + 21 files changed, 696 insertions(+), 211 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_79.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiGpsParser.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index db389239d..a10a3c983 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -46,7 +46,7 @@ public class GBDaoGenerator { public static void main(String[] args) throws Exception { - final Schema schema = new Schema(78, MAIN_PACKAGE + ".entities"); + final Schema schema = new Schema(79, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -137,8 +137,8 @@ public class GBDaoGenerator { addHuaweiActivitySample(schema, user, device); Entity huaweiWorkoutSummary = addHuaweiWorkoutSummarySample(schema, user, device); - addHuaweiWorkoutDataSample(schema, user, device, huaweiWorkoutSummary); - addHuaweiWorkoutPaceSample(schema, user, device, huaweiWorkoutSummary); + addHuaweiWorkoutDataSample(schema, huaweiWorkoutSummary); + addHuaweiWorkoutPaceSample(schema, huaweiWorkoutSummary); addCalendarSyncState(schema, device); addAlarms(schema, user, device); @@ -1307,10 +1307,12 @@ public class GBDaoGenerator { workoutSummary.addByteArrayProperty("rawData"); + workoutSummary.addStringProperty("gpxFileLocation"); + return workoutSummary; } - private static Entity addHuaweiWorkoutDataSample(Schema schema, Entity user, Entity device, Entity summaryEntity) { + private static Entity addHuaweiWorkoutDataSample(Schema schema, Entity summaryEntity) { Entity workoutDataSample = addEntity(schema, "HuaweiWorkoutDataSample"); workoutDataSample.setJavaDoc("Contains Huawei Workout data samples (multiple per workout)"); @@ -1345,7 +1347,7 @@ public class GBDaoGenerator { return workoutDataSample; } - private static Entity addHuaweiWorkoutPaceSample(Schema schema, Entity user, Entity device, Entity summaryEntity) { + private static Entity addHuaweiWorkoutPaceSample(Schema schema, Entity summaryEntity) { Entity workoutPaceSample = addEntity(schema, "HuaweiWorkoutPaceSample"); workoutPaceSample.setJavaDoc("Contains Huawei Workout pace data samples (one per workout)"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_79.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_79.java new file mode 100644 index 000000000..4663b6172 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_79.java @@ -0,0 +1,38 @@ +/* Copyright (C) 2024 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.database.schema; + +import android.database.sqlite.SQLiteDatabase; + +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao; + +public class GadgetbridgeUpdate_79 implements DBUpdateScript { + @Override + public void upgradeSchema(final SQLiteDatabase db) { + if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.GpxFileLocation.columnName, db)) { + final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \"" + + HuaweiWorkoutSummarySampleDao.Properties.GpxFileLocation.columnName + "\" TEXT"; + db.execSQL(statement); + } + } + + @Override + public void downgradeSchema(final SQLiteDatabase db) { + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBRCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBRCoordinator.java index e4a47808a..0ce47fc3d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBRCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiBRCoordinator.java @@ -26,9 +26,7 @@ import androidx.annotation.NonNull; import java.util.Collection; import java.util.Collections; -import java.util.List; -import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; @@ -38,13 +36,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySampleDao; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSampleDao; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySample; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser; import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample; @@ -87,21 +80,7 @@ public abstract class HuaweiBRCoordinator extends AbstractBLClassicDeviceCoordin @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { - long deviceId = device.getId(); - QueryBuilder qb = session.getHuaweiActivitySampleDao().queryBuilder(); - qb.where(HuaweiActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); - - QueryBuilder qb2 = session.getHuaweiWorkoutSummarySampleDao().queryBuilder(); - List workouts = qb2.where(HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId)).build().list(); - for (HuaweiWorkoutSummarySample sample : workouts) { - session.getHuaweiWorkoutDataSampleDao().queryBuilder().where( - HuaweiWorkoutDataSampleDao.Properties.WorkoutId.eq(sample.getWorkoutId()) - ).buildDelete().executeDeleteWithoutDetachingEntities(); - } - - session.getHuaweiWorkoutSummarySampleDao().queryBuilder().where(HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); - - session.getBaseActivitySummaryDao().queryBuilder().where(BaseActivitySummaryDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); + huaweiCoordinator.deleteDevice(gbDevice, device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java index d799c574c..401a074ce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java @@ -29,7 +29,9 @@ import java.util.TreeMap; import org.slf4j.LoggerFactory; import org.slf4j.Logger; +import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.CameraActivity; import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity; @@ -40,11 +42,21 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications.NotificationConstraintsType; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface; +import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.GB; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*; +import androidx.annotation.NonNull; + public class HuaweiCoordinator { Logger LOG = LoggerFactory.getLogger(HuaweiCoordinator.class); @@ -56,6 +68,7 @@ public class HuaweiCoordinator { ByteBuffer notificationConstraints = null; private boolean supportsTruSleepNewSync = false; + private boolean supportsGpsNewSync = false; private Watchface.WatchfaceDeviceParams watchfaceDeviceParams; @@ -92,6 +105,28 @@ public class HuaweiCoordinator { } } + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + long deviceId = device.getId(); + QueryBuilder qb = session.getHuaweiActivitySampleDao().queryBuilder(); + qb.where(HuaweiActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); + + QueryBuilder qb2 = session.getHuaweiWorkoutSummarySampleDao().queryBuilder(); + List workouts = qb2.where(HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId)).build().list(); + for (HuaweiWorkoutSummarySample sample : workouts) { + session.getHuaweiWorkoutDataSampleDao().queryBuilder().where( + HuaweiWorkoutDataSampleDao.Properties.WorkoutId.eq(sample.getWorkoutId()) + ).buildDelete().executeDeleteWithoutDetachingEntities(); + + session.getHuaweiWorkoutPaceSampleDao().queryBuilder().where( + HuaweiWorkoutPaceSampleDao.Properties.WorkoutId.eq(sample.getWorkoutId()) + ).buildDelete().executeDeleteWithoutDetachingEntities(); + } + + session.getHuaweiWorkoutSummarySampleDao().queryBuilder().where(HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); + + session.getBaseActivitySummaryDao().queryBuilder().where(BaseActivitySummaryDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); + } + private SharedPreferences getCapabilitiesSharedPreferences() { return GBApplication.getContext().getSharedPreferences("huawei_coordinator_capatilities" + parent.getDeviceType().name(), Context.MODE_PRIVATE); } @@ -694,4 +729,12 @@ public class HuaweiCoordinator { public void setSupportsTruSleepNewSync(boolean supportsTruSleepNewSync) { this.supportsTruSleepNewSync = supportsTruSleepNewSync; } + + public boolean isSupportsGpsNewSync() { + return supportsGpsNewSync; + } + + public void setSupportsGpsNewSync(boolean supportsGpsNewSync) { + this.supportsGpsNewSync = supportsGpsNewSync; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiGpsParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiGpsParser.java new file mode 100644 index 000000000..dc4a04d8d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiGpsParser.java @@ -0,0 +1,102 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; + +public class HuaweiGpsParser { + + public static class GpsPoint { + public int timestamp; + public double latitude; + public double longitude; + public boolean altitudeSupported; + public double altitude; + + @NonNull + @Override + public String toString() { + return "GpsPoint{" + + "timestamp=" + timestamp + + ", longitude=" + longitude + + ", latitude=" + latitude + + ", altitudeSupported=" + altitudeSupported + + ", altitude=" + altitude + + '}'; + } + } + + public static GpsPoint[] parseHuaweiGps(byte[] data) { + ByteBuffer buffer = ByteBuffer.wrap(data); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Skip trim + buffer.position(0x20); + + int timestamp; + double lon_start; + double lat_start; + boolean alt_support; + double alt_start; + + byte fileType = buffer.get(); + if ((fileType & 0x03) != 0x03) { + alt_support = false; + timestamp = buffer.getInt(); + lon_start = buffer.getDouble(); + lat_start = buffer.getDouble(); + alt_start = 0; + buffer.position(62); // Skip past unknown fields/padding + } else { + alt_support = true; + timestamp = buffer.getInt(); + lon_start = buffer.getDouble(); + lat_start = buffer.getDouble(); + alt_start = buffer.getDouble(); + buffer.position(70); // Skip past unknown fields/padding + } + + lat_start = lat_start * 0.017453292519943; + lon_start = lon_start * 0.017453292519943; + + // Working values + int time = timestamp; + double lat = lat_start; + double lon = lon_start; + double alt = alt_start; + + int data_size = 15; + if (alt_support) + data_size += 4; + + ArrayList retv = new ArrayList<>(buffer.remaining() / data_size); + while (buffer.remaining() > data_size) { + short time_delta = buffer.getShort(); + buffer.getShort(); // Unknown value + float lon_delta = buffer.getFloat(); + float lat_delta = buffer.getFloat(); + buffer.get(); buffer.get(); buffer.get(); // Unknown values + + time = time + time_delta; + lat = lat + lat_delta; + lon = lon + lon_delta; + + GpsPoint point = new GpsPoint(); + point.timestamp = time; + point.latitude = (lat / 6383807.0d + lat_start) / 0.017453292519943d; + point.longitude = (lon / 6383807.0d / Math.cos(lat_start) + lon_start) / 0.017453292519943d; + point.altitudeSupported = alt_support; + if (alt_support) { + // TODO: not sure about this + float alt_delta = buffer.getFloat(); + alt = alt + alt_delta; + point.altitude = alt; + } + retv.add(point); + } + + return retv.toArray(new GpsPoint[0]); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiLECoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiLECoordinator.java index 16f35e377..7cbc50287 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiLECoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiLECoordinator.java @@ -27,9 +27,7 @@ import androidx.annotation.NonNull; import java.util.Collection; import java.util.Collections; -import java.util.List; -import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; @@ -39,13 +37,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; -import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySampleDao; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSampleDao; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySample; -import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser; import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample; @@ -96,21 +89,7 @@ public abstract class HuaweiLECoordinator extends AbstractBLEDeviceCoordinator i @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { - long deviceId = device.getId(); - QueryBuilder qb = session.getHuaweiActivitySampleDao().queryBuilder(); - qb.where(HuaweiActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); - - QueryBuilder qb2 = session.getHuaweiWorkoutSummarySampleDao().queryBuilder(); - List workouts = qb2.where(HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId)).build().list(); - for (HuaweiWorkoutSummarySample sample : workouts) { - session.getHuaweiWorkoutDataSampleDao().queryBuilder().where( - HuaweiWorkoutDataSampleDao.Properties.WorkoutId.eq(sample.getWorkoutId()) - ).buildDelete().executeDeleteWithoutDetachingEntities(); - } - - session.getHuaweiWorkoutSummarySampleDao().queryBuilder().where(HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); - - session.getBaseActivitySummaryDao().queryBuilder().where(BaseActivitySummaryDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); + huaweiCoordinator.deleteDevice(gbDevice, device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java index def696cf8..6d43a9918 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java @@ -1515,6 +1515,7 @@ public class DeviceConfig { public static class Response extends HuaweiPacket { public boolean truSleepNewSync = false; + public boolean gpsNewSync = false; public Response(ParamsProvider paramsProvider) { super(paramsProvider); @@ -1532,6 +1533,7 @@ public class DeviceConfig { // Tag 2 -> File support byte value = this.tlv.getByte(0x02); truSleepNewSync = (value & 2) != 0; + gpsNewSync = (value & 8) != 0; } // Tag 3 -> SmartWatchVersion diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService0A.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService0A.java index 482b9317d..e06db2695 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService0A.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService0A.java @@ -31,6 +31,7 @@ public class FileDownloadService0A { Type of files that can be downloaded through here: - debug files - sleep files + - gps files - rrisqi file */ @@ -68,6 +69,26 @@ public class FileDownloadService0A { } } + public static class GpsFileRequest extends HuaweiPacket { + public GpsFileRequest(ParamsProvider paramsProvider, short workoutId) { + super(paramsProvider); + + this.serviceId = FileDownloadService0A.id; + this.commandId = id; + + this.tlv = new HuaweiTLV() + .put(0x02, (byte) 0x02) + .put(0x86, new HuaweiTLV() + .put(0x07, (byte) 0x01) // Might be type? + .put(0x88, new HuaweiTLV() + .put(0x09, workoutId) + ) + ); + + this.complete = true; + } + } + public static class Response extends HuaweiPacket { public String[] fileNames; @@ -77,7 +98,11 @@ public class FileDownloadService0A { @Override public void parseTlv() throws ParseException { - String possibleNames = this.tlv.getString(0x01); + String possibleNames; + if (this.tlv.contains(0x01)) + possibleNames = this.tlv.getString(0x01); + else // For GPS, also has workoutId (0x09) and another tag (0x0e), not sure of the meaning + possibleNames = this.tlv.getObject(0x8b).getObject(0x8c).getString(0x0d); fileNames = possibleNames.split(";"); } } @@ -87,13 +112,16 @@ public class FileDownloadService0A { public static final int id = 0x02; public static class Request extends HuaweiPacket { - public Request(ParamsProvider paramsProvider) { + public Request(ParamsProvider paramsProvider, boolean truSleep) { super(paramsProvider); this.serviceId = FileDownloadService0A.id; this.commandId = id; - this.tlv = new HuaweiTLV().put(0x06, (byte) 1); + if (truSleep) + this.tlv = new HuaweiTLV().put(0x06, (byte) 0x01); + else + this.tlv = new HuaweiTLV().put(0x06, (byte) 0x02).put(0x07, (byte) 0x03); this.complete = true; } @@ -151,7 +179,8 @@ public class FileDownloadService0A { @Override public void parseTlv() throws ParseException { - this.fileLength = this.tlv.getInteger(0x02); + if (this.tlv.contains(0x02)) + this.fileLength = this.tlv.getInteger(0x02); if (this.tlv.contains(0x04)) this.transferType = this.tlv.getByte(0x04); if (this.tlv.contains(0x05)) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService2C.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService2C.java index 07f651444..bbbea5332 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService2C.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/FileDownloadService2C.java @@ -33,6 +33,7 @@ public class FileDownloadService2C { public enum FileType { SLEEP_STATE, SLEEP_DATA, + GPS, UNKNOWN; // Never use this as input static byte fileTypeToByte(FileType fileType) { @@ -41,6 +42,8 @@ public class FileDownloadService2C { return (byte) 0x0e; case SLEEP_DATA: return (byte) 0x0f; + case GPS: + return (byte) 0x11; default: throw new RuntimeException(); } @@ -52,6 +55,8 @@ public class FileDownloadService2C { return FileType.SLEEP_STATE; case 0x0f: return FileType.SLEEP_DATA; + case 0x11: + return FileType.GPS; default: return FileType.UNKNOWN; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFileDownloadManager.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFileDownloadManager.java index da8716bb7..bdeb3f0c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFileDownloadManager.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFileDownloadManager.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService0A; @@ -102,48 +103,153 @@ public class HuaweiFileDownloadManager { } } - /** - * Only for internal use - */ public enum FileType { DEBUG, SLEEP_STATE, SLEEP_DATA, + GPS, UNKNOWN // Never for input! } - /** - * Only for internal use, though also used in exception - */ + public static class FileDownloadCallback { + public void downloadComplete(FileRequest fileRequest) { } + + public void downloadException(HuaweiFileDownloadException e) { + if (e.fileRequest != null) + LOG.error("Error downloading file: {}{}", e.fileRequest.getFilename(), e.fileRequest.isNewSync() ? " (newsync)" : "", e); + else + LOG.error("Error in file download", e); + } + } + public static class FileRequest { // Inputs - public String filename; - public FileType fileType; - public boolean newSync; + private final String filename; + private final FileType fileType; + private final boolean newSync; - // Sleep type only - public int startTime; - public int endTime; + FileDownloadCallback fileDownloadCallback = null; + // Sleep type only - for 2C GPS they are set to zero + private int startTime = 0; + private int endTime = 0; + + // GPS type only + private short workoutId; + private Long databaseId; + + private FileRequest(String filename, FileType fileType, boolean newSync, int startTime, int endTime, FileDownloadCallback fileDownloadCallback) { + this.filename = filename; + this.fileType = fileType; + this.newSync = newSync; + this.fileDownloadCallback = fileDownloadCallback; + this.startTime = startTime; + this.endTime = endTime; + } + + public static FileRequest sleepStateFileRequest(boolean supportsTruSleepNewSync, int startTime, int endTime, FileDownloadCallback fileDownloadCallback) { + return new FileRequest("sleep_state.bin", FileType.SLEEP_STATE, supportsTruSleepNewSync, startTime, endTime, fileDownloadCallback); + } + + public static FileRequest sleepDataFileRequest(boolean supportsTruSleepNewSync, int startTime, int endTime, FileDownloadCallback fileDownloadCallback) { + return new FileRequest("sleep_data.bin", FileType.SLEEP_DATA, supportsTruSleepNewSync, startTime, endTime, fileDownloadCallback); + } + + private FileRequest(String filename, FileType fileType, boolean newSync, FileDownloadCallback fileDownloadCallback) { + this.filename = filename; + this.fileType = fileType; + this.newSync = newSync; + this.fileDownloadCallback = fileDownloadCallback; + } + + public static FileRequest debugFileRequest(String filename, FileDownloadCallback fileDownloadCallback) { + return new FileRequest(filename, FileType.DEBUG, false, fileDownloadCallback); + } + + private FileRequest(@Nullable String filename, FileType fileType, boolean newSync, short workoutId, Long databaseId, FileDownloadCallback fileDownloadCallback) { + this.filename = filename; + this.fileType = fileType; + this.newSync = newSync; + this.fileDownloadCallback = fileDownloadCallback; + this.workoutId = workoutId; + this.databaseId = databaseId; + } + + public static FileRequest workoutGpsFileRequest(boolean newSync, short workoutId, Long databaseId, FileDownloadCallback fileDownloadCallback) { + if (newSync) + return new FileRequest(String.format(Locale.getDefault(), "%d_gps.bin", workoutId), FileType.GPS, true, workoutId, databaseId, fileDownloadCallback); + else + return new FileRequest(null, FileType.GPS, false, workoutId, databaseId, fileDownloadCallback); + } // Retrieved - public int fileSize; - public int maxBlockSize; - public int timeout; // TODO: unit? - public ByteBuffer buffer; + private int fileSize; + private int maxBlockSize; + private int timeout; // TODO: unit? + private ByteBuffer buffer; - public int startOfBlockOffset; - public int currentBlockSize; + private int startOfBlockOffset; + private int currentBlockSize; // Old sync only - public String[] filenames; - public byte lastPacketNumber; + private String[] filenames; + private byte lastPacketNumber; // New sync only - public byte fileId; - public boolean noEncrypt; + private byte fileId; + private boolean noEncrypt; + + public byte getFileId() { + return fileId; + } + + public String getFilename() { + return filename; + } + + public FileType getFileType() { + return fileType; + } + + public byte[] getData() { + if (buffer == null) + return new byte[] {}; + return buffer.array(); + } + + public int getCurrentOffset() { + return buffer.position(); + } + + public boolean isNewSync() { + return newSync; + } + + public int getStartTime() { + return startTime; + } + + public int getEndTime() { + return endTime; + } + + public short getWorkoutId() { + return workoutId; + } + + public Long getDatabaseId() { + return databaseId; + } + + public int getCurrentBlockSize() { + return currentBlockSize; + } + + public boolean isNoEncrypt() { + return noEncrypt; + } } /** @@ -219,37 +325,25 @@ public class HuaweiFileDownloadManager { private final ArrayList fileRequests; private FileRequest currentFileRequest; + // If the GB interface needs to be updated at the end of the queue of downloads + private boolean needSync = false; + public HuaweiFileDownloadManager(HuaweiSupportProvider supportProvider) { this.supportProvider = supportProvider; handler = new Handler(Looper.getMainLooper()); isBusy = false; fileRequests = new ArrayList<>(); timeout = () -> { - this.supportProvider.downloadException(new HuaweiFileDownloadTimeoutException(currentFileRequest)); + this.currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadTimeoutException(currentFileRequest)); reset(); }; } - public void downloadDebug(String filename) { - FileRequest request = new FileRequest(); - request.filename = filename; - request.fileType = FileType.DEBUG; - request.newSync = false; + public void addToQueue(FileRequest fileRequest, boolean needSync) { synchronized (supportProvider) { - fileRequests.add(request); - } - startDownload(); - } - - public void downloadSleep(boolean supportsTruSleepNewSync, String filename, int startTime, int endTime) { - FileRequest request = new FileRequest(); - request.filename = filename; - request.fileType = (filename.equals("sleep_state.bin"))?FileType.SLEEP_STATE: FileType.SLEEP_DATA; - request.newSync = supportsTruSleepNewSync; - request.startTime = startTime; - request.endTime = endTime; - synchronized (supportProvider) { - fileRequests.add(request); + fileRequests.add(fileRequest); + if (needSync) + this.needSync = true; } startDownload(); } @@ -275,7 +369,7 @@ public class HuaweiFileDownloadManager { @Override public void handleException(Request.ResponseParseException e) { - supportProvider.downloadException(new HuaweiFileDownloadRequestException(null, this.getClass(), e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadRequestException(null, this.getClass(), e)); } }); } @@ -285,11 +379,15 @@ public class HuaweiFileDownloadManager { initFileDataReceiver(); // Make sure the fileDataReceiver is ready synchronized (this.supportProvider) { - if (this.isBusy) + if (this.isBusy) { + LOG.debug("A new download is started while a previous is in progress."); return; // Already downloading, this file will come eventually + } if (this.fileRequests.isEmpty()) { // No more files to download - supportProvider.downloadQueueEmpty(); + supportProvider.downloadQueueEmpty(this.needSync); + // Don't need sync after this anymore + this.needSync = false; return; } this.isBusy = true; @@ -305,7 +403,7 @@ public class HuaweiFileDownloadManager { GetFileDownloadInitRequest r = (GetFileDownloadInitRequest) request; if (r.newSync) { if (!currentFileRequest.filename.equals(r.filename)) { - supportProvider.downloadException(new HuaweiFileDownloadFileMismatchException( + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadFileMismatchException( currentFileRequest, r.filename )); @@ -313,7 +411,7 @@ public class HuaweiFileDownloadManager { return; } if (currentFileRequest.fileType != r.fileType) { - supportProvider.downloadException(new HuaweiFileDownloadFileMismatchException( + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadFileMismatchException( currentFileRequest, r.fileType )); @@ -329,13 +427,39 @@ public class HuaweiFileDownloadManager { } getFileInfo(); } else { - if (!arrayContains(r.filenames, currentFileRequest.filename)) { - supportProvider.downloadException(new HuaweiFileDownloadFileMismatchException( - currentFileRequest, - r.filenames - )); - reset(); - return; + if (currentFileRequest.fileType == FileType.GPS) { + if (r.filenames.length == 0) { + reset(); + return; + } + for (String filename : r.filenames) { + // We only download the gps file itself right now + if (!filename.contains("_gps.bin")) + continue; + + // Add download request with the filename at the start of the queue + FileRequest fileRequest = new FileRequest( + filename, + FileType.GPS, + currentFileRequest.newSync, + currentFileRequest.workoutId, + currentFileRequest.databaseId, + currentFileRequest.fileDownloadCallback + ); + synchronized (supportProvider) { + fileRequests.add(0, fileRequest); + } + } + currentFileRequest = fileRequests.remove(0); // Replace with the file to download + } else { + if (!arrayContains(r.filenames, currentFileRequest.filename)) { + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadFileMismatchException( + currentFileRequest, + r.filenames + )); + reset(); + return; + } } currentFileRequest.filenames = r.filenames; getDownloadParameters(); @@ -344,13 +468,13 @@ public class HuaweiFileDownloadManager { @Override public void handleException(Request.ResponseParseException e) { - supportProvider.downloadException(new HuaweiFileDownloadRequestException(currentFileRequest, this.getClass(), e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadRequestException(currentFileRequest, this.getClass(), e)); } }); try { getFileDownloadInitRequest.doPerform(); } catch (IOException e) { - supportProvider.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileDownloadInitRequest, e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileDownloadInitRequest, e)); reset(); } } @@ -359,7 +483,10 @@ public class HuaweiFileDownloadManager { // Old sync only, can never be multiple at the same time // Assuming currentRequest is the correct one the entire time // Which may no longer be the case when we implement multi-download for new sync - GetFileParametersRequest getFileParametersRequest = new GetFileParametersRequest(supportProvider); + GetFileParametersRequest getFileParametersRequest = new GetFileParametersRequest(supportProvider, + currentFileRequest.fileType == FileType.SLEEP_STATE || + currentFileRequest.fileType == FileType.SLEEP_DATA + ); getFileParametersRequest.setFinalizeReq(new Request.RequestCallback() { @Override public void call() { @@ -370,14 +497,14 @@ public class HuaweiFileDownloadManager { @Override public void handleException(Request.ResponseParseException e) { - supportProvider.downloadException(new HuaweiFileDownloadRequestException(null, this.getClass(), e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadRequestException(null, this.getClass(), e)); reset(); } }); try { getFileParametersRequest.doPerform(); } catch (IOException e) { - supportProvider.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileParametersRequest, e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileParametersRequest, e)); reset(); } } @@ -390,7 +517,7 @@ public class HuaweiFileDownloadManager { GetFileInfoRequest r = (GetFileInfoRequest) request; if (r.newSync) { if (currentFileRequest.fileId != r.fileId) { - supportProvider.downloadException(new HuaweiFileDownloadFileMismatchException(currentFileRequest, r.fileId, true)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadFileMismatchException(currentFileRequest, r.fileId, true)); reset(); return; } @@ -412,21 +539,19 @@ public class HuaweiFileDownloadManager { @Override public void handleException(Request.ResponseParseException e) { - supportProvider.downloadException(new HuaweiFileDownloadRequestException(null, this.getClass(), e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadRequestException(null, this.getClass(), e)); reset(); } }); try { getFileInfoRequest.doPerform(); } catch (IOException e) { - supportProvider.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileInfoRequest, e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileInfoRequest, e)); reset(); } } private void downloadNextFileBlock() { - handler.removeCallbacks(this.timeout); - if (currentFileRequest.buffer == null) // New file currentFileRequest.buffer = ByteBuffer.allocate(currentFileRequest.fileSize); currentFileRequest.lastPacketNumber = -1; // Counts per block @@ -439,6 +564,9 @@ public class HuaweiFileDownloadManager { // Start listening for file data this.supportProvider.addInProgressRequest(fileDataReceiver); + handler.removeCallbacks(this.timeout); + handler.postDelayed(HuaweiFileDownloadManager.this.timeout, currentFileRequest.timeout * 1000L); + GetFileBlockRequest getFileBlockRequest = new GetFileBlockRequest(supportProvider, currentFileRequest); getFileBlockRequest.setFinalizeReq(new Request.RequestCallback() { @Override @@ -451,20 +579,20 @@ public class HuaweiFileDownloadManager { try { getFileBlockRequest.doPerform(); } catch (IOException e) { - supportProvider.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileBlockRequest, e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileBlockRequest, e)); reset(); } } private void handleFileData(boolean newSync, byte number, byte[] data) { if (newSync && currentFileRequest.fileId != number) { - supportProvider.downloadException(new HuaweiFileDownloadFileMismatchException(currentFileRequest, number, true)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadFileMismatchException(currentFileRequest, number, true)); reset(); return; } if (!newSync) { if (currentFileRequest.lastPacketNumber != number - 1) { - supportProvider.downloadException(new HuaweiFileDownloadFileMismatchException(currentFileRequest, number, false)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadFileMismatchException(currentFileRequest, number, false)); reset(); return; } @@ -496,13 +624,12 @@ public class HuaweiFileDownloadManager { try { getFileDownloadCompleteRequest.doPerform(); } catch (IOException e) { - supportProvider.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileDownloadCompleteRequest, e)); + currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileDownloadCompleteRequest, e)); reset(); } // Handle file data - if (currentFileRequest.buffer != null) // File size was zero - supportProvider.downloadComplete(currentFileRequest.filename, currentFileRequest.buffer.array()); + currentFileRequest.fileDownloadCallback.downloadComplete(currentFileRequest); if (!this.currentFileRequest.newSync && !this.fileRequests.isEmpty() && !this.fileRequests.get(0).newSync) { // Old sync can potentially take a shortcut diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java index bb4b74aaf..1a5ea9839 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java @@ -30,6 +30,7 @@ import androidx.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.nio.charset.StandardCharsets; import java.io.IOException; import java.util.ArrayList; @@ -54,6 +55,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupplier; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupplier.HuaweiDeviceType; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiGpsParser; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTruSleepParser; @@ -71,6 +73,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao; +import nodomain.freeyourgadget.gadgetbridge.export.ActivityTrackExporter; +import nodomain.freeyourgadget.gadgetbridge.export.GPXExporter; import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationProviderType; import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationService; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -79,11 +83,14 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.Contact; +import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; @@ -166,6 +173,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetN import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FitnessData; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetWorkModeRequest; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.MediaManager; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; @@ -173,6 +182,48 @@ import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class HuaweiSupportProvider { private static final Logger LOG = LoggerFactory.getLogger(HuaweiSupportProvider.class); + private class SyncState { + private boolean activitySync = false; + private boolean workoutSync = false; + private int workoutGpsDownload = 0; + + public void setActivitySync(boolean state) { + this.activitySync = state; + updateState(); + } + + public void setWorkoutSync(boolean state) { + this.workoutSync = state; + updateState(); + } + + public void startWorkoutGpsDownload() { + this.workoutGpsDownload += 1; + } + + public void stopWorkoutGpsDownload() { + this.workoutGpsDownload -= 1; + updateState(); + } + + public void updateState() { + updateState(true); + } + + public void updateState(boolean needSync) { + if (!activitySync && !workoutSync && workoutGpsDownload == 0) { + if (getDevice().isBusy()) { + getDevice().unsetBusyTask(); + getDevice().sendDeviceUpdateIntent(context); + } + if (needSync) + GB.signalActivityDataFinish(getDevice()); + } + } + } + + private final SyncState syncState = new SyncState(); + private final int initTimeout = 2000; private HuaweiBRSupport brSupport; @@ -1097,6 +1148,8 @@ public class HuaweiSupportProvider { } private void fetchActivityData() { + syncState.setActivitySync(true); + int sleepStart = 0; int stepStart = 0; final int end = (int) (System.currentTimeMillis() / 1000); @@ -1144,13 +1197,13 @@ public class HuaweiSupportProvider { @Override public void call() { if (!downloadTruSleepData(start, end)) - handleSyncFinished(); + syncState.setActivitySync(false); } @Override public void handleException(Request.ResponseParseException e) { LOG.error("Fitness totals exception", e); - handleSyncFinished(); + syncState.setActivitySync(false); } }); @@ -1161,14 +1214,14 @@ public class HuaweiSupportProvider { getFitnessTotalsRequest.doPerform(); } catch (IOException e) { LOG.error("Exception on starting fitness totals request", e); - handleSyncFinished(); + syncState.setActivitySync(false); } } @Override public void handleException(Request.ResponseParseException e) { LOG.error("Step data count exception", e); - handleSyncFinished(); + syncState.setActivitySync(false); } }); @@ -1179,14 +1232,14 @@ public class HuaweiSupportProvider { getStepDataCountRequest.doPerform(); } catch (IOException e) { LOG.error("Exception on starting step data count request", e); - handleSyncFinished(); + syncState.setActivitySync(false); } } @Override public void handleException(Request.ResponseParseException e) { LOG.error("Sleep data count exception", e); - handleSyncFinished(); + syncState.setActivitySync(false); } }); @@ -1194,11 +1247,13 @@ public class HuaweiSupportProvider { getSleepDataCountRequest.doPerform(); } catch (IOException e) { LOG.error("Exception on starting sleep data count request", e); - handleSyncFinished(); + syncState.setActivitySync(false); } } private void fetchWorkoutData() { + syncState.setWorkoutSync(true); + int start = 0; int end = (int) (System.currentTimeMillis() / 1000); @@ -1261,13 +1316,13 @@ public class HuaweiSupportProvider { getWorkoutCountRequest.setFinalizeReq(new RequestCallback() { @Override public void call() { - handleSyncFinished(); + syncState.setWorkoutSync(false); } @Override public void handleException(Request.ResponseParseException e) { LOG.error("Workout parsing exception", e); - handleSyncFinished(); + syncState.setWorkoutSync(false); } }); @@ -1275,18 +1330,10 @@ public class HuaweiSupportProvider { getWorkoutCountRequest.doPerform(); } catch (IOException e) { LOG.error("Exception on starting workout count request", e); - handleSyncFinished(); + syncState.setWorkoutSync(false); } } - private void handleSyncFinished() { - if (gbDevice.isBusy()) { - gbDevice.unsetBusyTask(); - gbDevice.sendDeviceUpdateIntent(context); - } - GB.signalActivityDataFinish(getDevice()); - } - public void onReset(int flags) { try { if(flags== GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET) { @@ -1559,7 +1606,8 @@ public class HuaweiSupportProvider { packet.poolLength, packet.laps, packet.avgSwolf, - raw + raw, + null ); db.getDaoSession().getHuaweiWorkoutSummarySampleDao().insertOrReplace(summarySample); @@ -2073,52 +2121,151 @@ public class HuaweiSupportProvider { if (!getHuaweiCoordinator().supportsTruSleep()) return false; - huaweiFileDownloadManager.downloadSleep( + huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.sleepStateFileRequest( getHuaweiCoordinator().getSupportsTruSleepNewSync(), - "sleep_state.bin", // new String[] {"sleep_state.bin"}, // Later also "sleep_data.bin", but we don't use it right now start, - end - ); + end, + new HuaweiFileDownloadManager.FileDownloadCallback() { + @Override + public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) { + if (fileRequest.getData().length != 0) { + LOG.debug("Parsing sleep state file"); + HuaweiTruSleepParser.TruSleepStatus[] results = HuaweiTruSleepParser.parseState(fileRequest.getData()); + for (HuaweiTruSleepParser.TruSleepStatus status : results) + addSleepActivity(status.startTime, status.endTime, (byte) 0x06, (byte) 0x0a); + } else + LOG.debug("Sleep state file empty"); + syncState.setActivitySync(false); + } + + @Override + public void downloadException(HuaweiFileDownloadManager.HuaweiFileDownloadException e) { + super.downloadException(e); + syncState.setActivitySync(false); + } + } + ), true); return true; } - /** - * Called when a file download is complete - * @param fileName Filename of the file - * @param fileContents Contents of the file - */ - public void downloadComplete(String fileName, byte[] fileContents) { - LOG.debug("File download complete: {}: {}", fileName, GB.hexdump(fileContents)); + public void downloadWorkoutGpsFiles(short workoutId, Long databaseId) { + syncState.startWorkoutGpsDownload(); - if (fileName.equals("sleep_state.bin")) { - HuaweiTruSleepParser.TruSleepStatus[] results = HuaweiTruSleepParser.parseState(fileContents); - for (HuaweiTruSleepParser.TruSleepStatus status : results) - addSleepActivity(status.startTime, status.endTime, (byte) 0x06, (byte) 0x0a); - // This should only be called once per sync - also if we start downloading more sleep data - GB.signalActivityDataFinish(getDevice()); - // Unsetting busy is done at the end of all downloads - } // "sleep_data.bin" later as well + huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.workoutGpsFileRequest( + getHuaweiCoordinator().isSupportsGpsNewSync(), + workoutId, + databaseId, + new HuaweiFileDownloadManager.FileDownloadCallback() { + @Override + public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) { + syncState.stopWorkoutGpsDownload(); + + if (fileRequest.getData().length == 0) { + LOG.debug("GPS file empty"); + return; + } + + LOG.debug("Parsing GPS file"); + + HuaweiGpsParser.GpsPoint[] points = HuaweiGpsParser.parseHuaweiGps(fileRequest.getData()); + + LOG.debug("Received {} GPS points", points.length); + + if (points.length == 0) { + LOG.debug("No GPS points returned"); + return; + } + + ActivityTrack track = new ActivityTrack(); + + track.setName("Workout " + fileRequest.getWorkoutId()); + track.setBaseTime(DateTimeUtils.parseTimeStamp(points[0].timestamp)); + + try (DBHandler db = GBApplication.acquireDB()) { + track.setUser(DBHelper.getUser(db.getDaoSession())); + track.setDevice(DBHelper.getDevice(gbDevice, db.getDaoSession())); + } catch (Exception e) { + LOG.error("Cannot acquire DB, set user, or set device for Activity track, continuing anyway"); + } + + for (HuaweiGpsParser.GpsPoint point : points) { + GPSCoordinate coordinate; + if (point.altitudeSupported) + coordinate = new GPSCoordinate(point.longitude, point.latitude, point.altitude); + else + coordinate = new GPSCoordinate(point.longitude, point.latitude); + + ActivityPoint activityPoint = new ActivityPoint(); + activityPoint.setTime(DateTimeUtils.parseTimeStamp(point.timestamp)); + activityPoint.setLocation(coordinate); + + track.addTrackPoint(activityPoint); + } + + String filename = FileUtils.makeValidFileName("workout_" + fileRequest.getWorkoutId() + "_" + points[0].timestamp + ".gpx"); + File targetFile; + try { + targetFile = new File( + getDevice().getDeviceCoordinator().getWritableExportDirectory(getDevice()), + filename + ); + } catch (IOException e) { + // TODO: Translatable string + GB.toast(context, "Could not open Workout GPS file to write to", Toast.LENGTH_SHORT, GB.ERROR, e); + LOG.error("Could not open Workout GPS file to write to", e); + return; + } + + GPXExporter exporter = new GPXExporter(); + exporter.setCreator(GBApplication.app().getNameAndVersion()); + try { + exporter.performExport(track, targetFile); + } catch (IOException | ActivityTrackExporter.GPXTrackEmptyException e) { + // TODO: Translatable string + GB.toast(context, "Failed to export Workout GPX file", Toast.LENGTH_SHORT, GB.ERROR, e); + LOG.error("Failed to export Workout GPX file", e); + return; + } + + Long databaseId = fileRequest.getDatabaseId(); + if (databaseId == null) { + // TODO: Translatable string + GB.toast(context, "Cannot link GPX to workout", Toast.LENGTH_SHORT, GB.ERROR); + LOG.error("Cannot link GPX to workout"); + return; + } + + try (DBHandler db = GBApplication.acquireDB()) { + DaoSession daoSession = db.getDaoSession(); + HuaweiWorkoutSummarySample sample = daoSession.getHuaweiWorkoutSummarySampleDao().load(databaseId); + sample.setGpxFileLocation(targetFile.getAbsolutePath()); + sample.update(); + } catch (Exception e) { + // TODO: Translatable string + GB.toast(context, "Failed to save Workout GPX file location", Toast.LENGTH_SHORT, GB.ERROR, e); + LOG.error("Failed to save Workout GPX file location", e); + return; + } + + new HuaweiWorkoutGbParser(getDevice()).parseWorkout(databaseId); + + LOG.debug("Completed workout GPS parsing and inserting"); + } + + @Override + public void downloadException(HuaweiFileDownloadManager.HuaweiFileDownloadException e) { + super.downloadException(e); + syncState.stopWorkoutGpsDownload(); + } + } + ), true); } /** * Called when there are no more files left to download */ - public void downloadQueueEmpty() { - if (gbDevice.isBusy()) { - gbDevice.unsetBusyTask(); - gbDevice.sendDeviceUpdateIntent(context); - } - } - - public void downloadException(HuaweiFileDownloadManager.HuaweiFileDownloadException e) { - GB.toast("Error downloading file", Toast.LENGTH_SHORT, GB.ERROR, e); - if (e.fileRequest != null) - LOG.error("Error downloading file: {}{}", e.fileRequest.filename, e.fileRequest.newSync ? " (newsync)" : "", e); - else - LOG.error("Error in file download", e); - - // We also reset the sync state, just to get back to working as nicely as possible - handleSyncFinished(); + public void downloadQueueEmpty(boolean needSync) { + syncState.updateState(needSync); } public void onTestNewFunction() { @@ -2126,17 +2273,27 @@ public class HuaweiSupportProvider { gbDevice.setBusyTask("Downloading file..."); gbDevice.sendDeviceUpdateIntent(getContext()); - huaweiFileDownloadManager.downloadSleep( + huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.sleepStateFileRequest( getHuaweiCoordinator().getSupportsTruSleepNewSync(), - "sleep_state.bin", 0, - (int) (System.currentTimeMillis() / 1000) - ); - huaweiFileDownloadManager.downloadSleep( + (int) (System.currentTimeMillis() / 1000), + new HuaweiFileDownloadManager.FileDownloadCallback() { + @Override + public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) { + // Handle file contents + } + } + ), true); + huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.sleepDataFileRequest( getHuaweiCoordinator().getSupportsTruSleepNewSync(), - "sleep_data.bin", 0, - (int) (System.currentTimeMillis() / 1000) - ); + (int) (System.currentTimeMillis() / 1000), + new HuaweiFileDownloadManager.FileDownloadCallback() { + @Override + public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) { + // Handle file contents + } + } + ), true); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java index fa5ab5a19..8a9ac1cf9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiWorkoutGbParser.java @@ -304,6 +304,10 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { } public void parseWorkout(Long workoutId) { + LOG.debug("Parsing workout ID {}", workoutId); + if (workoutId == null) + return; + try (DBHandler db = GBApplication.acquireDB()) { final DaoSession session = db.getDaoSession(); final Device device = DBHelper.getDevice(gbDevice, session); @@ -718,6 +722,11 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { if (baseSummary.getName() == null) { baseSummary.setName("Workout " + summary.getWorkoutNumber()); } + + if (baseSummary.getGpxTrack() == null) { + baseSummary.setGpxTrack(summary.getGpxFileLocation()); + } + // start time never changes baseSummary.setEndTime(new Date(summary.getEndTimestamp() * 1000L)); baseSummary.setActivityKind(type.getCode()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileBlockRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileBlockRequest.java index fcc4389bb..1a1beb3cc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileBlockRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileBlockRequest.java @@ -29,7 +29,7 @@ public class GetFileBlockRequest extends Request { public GetFileBlockRequest(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request) { super(support); - if (request.newSync) { + if (request.isNewSync()) { this.serviceId = FileDownloadService2C.id; this.commandId = FileDownloadService2C.RequestBlock.id; } else { @@ -42,20 +42,20 @@ public class GetFileBlockRequest extends Request { @Override protected List createRequest() throws Request.RequestCreationException { try { - if (this.request.newSync) + if (this.request.isNewSync()) return new FileDownloadService2C.RequestBlock( paramsProvider, - this.request.fileId, - this.request.buffer.position(), - this.request.currentBlockSize, - this.request.noEncrypt + this.request.getFileId(), + this.request.getCurrentOffset(), + this.request.getCurrentBlockSize(), + this.request.isNoEncrypt() ).serialize(); else return new FileDownloadService0A.RequestBlock.Request( paramsProvider, - this.request.filename, - this.request.buffer.position(), - this.request.currentBlockSize + this.request.getFilename(), + this.request.getCurrentOffset(), + this.request.getCurrentBlockSize() ).serialize(); } catch (HuaweiPacket.CryptoException e) { throw new Request.RequestCreationException(e); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadCompleteRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadCompleteRequest.java index 425c14e70..a0168863b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadCompleteRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadCompleteRequest.java @@ -30,7 +30,7 @@ public class GetFileDownloadCompleteRequest extends Request { public GetFileDownloadCompleteRequest(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request) { super(support); - if (request.newSync) { + if (request.isNewSync()) { this.serviceId = FileDownloadService2C.id; this.commandId = FileDownloadService2C.FileDownloadCompleteRequest.id; } else { @@ -43,8 +43,8 @@ public class GetFileDownloadCompleteRequest extends Request { @Override protected List createRequest() throws RequestCreationException { try { - if (request.newSync) - return new FileDownloadService2C.FileDownloadCompleteRequest(paramsProvider, this.request.fileId).serialize(); + if (request.isNewSync()) + return new FileDownloadService2C.FileDownloadCompleteRequest(paramsProvider, this.request.getFileId()).serialize(); else return new FileDownloadService0A.FileDownloadCompleteRequest(paramsProvider).serialize(); } catch (HuaweiPacket.CryptoException e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadInitRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadInitRequest.java index ebe80c53a..2e8951046 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadInitRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileDownloadInitRequest.java @@ -41,7 +41,7 @@ public class GetFileDownloadInitRequest extends Request { public GetFileDownloadInitRequest(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request) { super(support); - if (request.newSync) { + if (request.isNewSync()) { this.serviceId = FileDownloadService2C.id; this.commandId = FileDownloadService2C.FileDownloadInit.id; } else { @@ -57,6 +57,8 @@ public class GetFileDownloadInitRequest extends Request { return FileDownloadService2C.FileType.SLEEP_STATE; case SLEEP_DATA: return FileDownloadService2C.FileType.SLEEP_DATA; + case GPS: + return FileDownloadService2C.FileType.GPS; default: return FileDownloadService2C.FileType.UNKNOWN; } @@ -68,6 +70,8 @@ public class GetFileDownloadInitRequest extends Request { return HuaweiFileDownloadManager.FileType.SLEEP_STATE; case SLEEP_DATA: return HuaweiFileDownloadManager.FileType.SLEEP_DATA; + case GPS: + return HuaweiFileDownloadManager.FileType.GPS; default: return HuaweiFileDownloadManager.FileType.UNKNOWN; } @@ -76,16 +80,18 @@ public class GetFileDownloadInitRequest extends Request { @Override protected List createRequest() throws RequestCreationException { try { - if (this.request.newSync) { - FileDownloadService2C.FileType type = convertFileTypeTo2C(request.fileType); + if (this.request.isNewSync()) { + FileDownloadService2C.FileType type = convertFileTypeTo2C(request.getFileType()); if (type == FileDownloadService2C.FileType.UNKNOWN) - throw new RequestCreationException("Cannot convert type " + request.fileType); - return new FileDownloadService2C.FileDownloadInit.Request(paramsProvider, request.filename, type, request.startTime, request.endTime).serialize(); + throw new RequestCreationException("Cannot convert type " + request.getFileType()); + return new FileDownloadService2C.FileDownloadInit.Request(paramsProvider, request.getFilename(), type, request.getStartTime(), request.getEndTime()).serialize(); } else { - if (this.request.fileType == HuaweiFileDownloadManager.FileType.DEBUG) + if (this.request.getFileType() == HuaweiFileDownloadManager.FileType.DEBUG) return new FileDownloadService0A.FileDownloadInit.DebugFilesRequest(paramsProvider).serialize(); - else if (this.request.fileType == HuaweiFileDownloadManager.FileType.SLEEP_STATE) - return new FileDownloadService0A.FileDownloadInit.SleepFilesRequest(paramsProvider, request.startTime, request.endTime).serialize(); + else if (this.request.getFileType() == HuaweiFileDownloadManager.FileType.SLEEP_STATE) + return new FileDownloadService0A.FileDownloadInit.SleepFilesRequest(paramsProvider, request.getStartTime(), request.getEndTime()).serialize(); + else if (this.request.getFileType() == HuaweiFileDownloadManager.FileType.GPS) + return new FileDownloadService0A.FileDownloadInit.GpsFileRequest(paramsProvider, request.getWorkoutId()).serialize(); else throw new RequestCreationException("Unknown file type"); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileInfoRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileInfoRequest.java index 92965a356..b55d402a3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileInfoRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileInfoRequest.java @@ -40,7 +40,7 @@ public class GetFileInfoRequest extends Request { public GetFileInfoRequest(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request) { super(support); - if (request.newSync) { + if (request.isNewSync()) { this.serviceId = FileDownloadService2C.id; this.commandId = FileDownloadService2C.FileInfo.id; } else { @@ -53,10 +53,10 @@ public class GetFileInfoRequest extends Request { @Override protected List createRequest() throws RequestCreationException { try { - if (this.request.newSync) - return new FileDownloadService2C.FileInfo.Request(paramsProvider, this.request.fileId).serialize(); + if (this.request.isNewSync()) + return new FileDownloadService2C.FileInfo.Request(paramsProvider, this.request.getFileId()).serialize(); else - return new FileDownloadService0A.FileInfo.Request(paramsProvider, this.request.filename).serialize(); + return new FileDownloadService0A.FileInfo.Request(paramsProvider, this.request.getFilename()).serialize(); } catch (HuaweiPacket.CryptoException e) { throw new RequestCreationException(e); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileParametersRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileParametersRequest.java index 8002aeb6f..0251c088d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileParametersRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetFileParametersRequest.java @@ -24,19 +24,22 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupport public class GetFileParametersRequest extends Request { + private final boolean truSleep; + private int maxBlockSize; private int timeout; - public GetFileParametersRequest(HuaweiSupportProvider support) { + public GetFileParametersRequest(HuaweiSupportProvider support, boolean truSleep) { super(support); this.serviceId = FileDownloadService0A.id; this.commandId = FileDownloadService0A.FileParameters.id; + this.truSleep = truSleep; } @Override protected List createRequest() throws RequestCreationException { try { - return new FileDownloadService0A.FileParameters.Request(paramsProvider).serialize(); + return new FileDownloadService0A.FileParameters.Request(paramsProvider, truSleep).serialize(); } catch (HuaweiPacket.CryptoException e) { throw new RequestCreationException(e); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetSettingRelatedRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetSettingRelatedRequest.java index 009d273df..e113548ac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetSettingRelatedRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetSettingRelatedRequest.java @@ -56,5 +56,6 @@ public class GetSettingRelatedRequest extends Request { LOG.debug("handle Setting Related"); supportProvider.getHuaweiCoordinator().setSupportsTruSleepNewSync(((DeviceConfig.SettingRelated.Response) receivedPacket).truSleepNewSync); + supportProvider.getHuaweiCoordinator().setSupportsGpsNewSync(((DeviceConfig.SettingRelated.Response) receivedPacket).gpsNewSync); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutDataRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutDataRequest.java index b339e42e0..c2e268c24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutDataRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutDataRequest.java @@ -113,6 +113,7 @@ public class GetWorkoutDataRequest extends Request { this.nextRequest(nextRequest); } else { new HuaweiWorkoutGbParser(getDevice()).parseWorkout(this.databaseId); + supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId); if (!remainder.isEmpty()) { GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest( diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutPaceRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutPaceRequest.java index 3145dcba0..babb367aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutPaceRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutPaceRequest.java @@ -90,6 +90,7 @@ public class GetWorkoutPaceRequest extends Request { this.nextRequest(nextRequest); } else { new HuaweiWorkoutGbParser(getDevice()).parseWorkout(this.databaseId); + supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId); if (!remainder.isEmpty()) { GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest( diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutTotalsRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutTotalsRequest.java index 476873f10..c98551354 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutTotalsRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutTotalsRequest.java @@ -104,6 +104,7 @@ public class GetWorkoutTotalsRequest extends Request { this.nextRequest(nextRequest); } else { new HuaweiWorkoutGbParser(getDevice()).parseWorkout(databaseId); + supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, databaseId); if (!remainder.isEmpty()) { GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest(