diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 99876b26fd..077aba697a 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -15,6 +15,8 @@ */ package nodomain.freeyourgadget.gadgetbridge.daogen; +import java.util.List; + import de.greenrobot.daogenerator.DaoGenerator; import de.greenrobot.daogenerator.Entity; import de.greenrobot.daogenerator.Index; @@ -56,7 +58,7 @@ public class GBDaoGenerator { private static final String TIMESTAMP_TO = "timestampTo"; public static void main(String[] args) throws Exception { - final Schema schema = new Schema(120, MAIN_PACKAGE + ".entities"); + final Schema schema = new Schema(121, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -187,6 +189,9 @@ public class GBDaoGenerator { addHuaweiWorkoutSpO2Sample(schema, huaweiWorkoutSummary); addHuaweiWorkoutSectionsSample(schema, huaweiWorkoutSummary); + Entity huaweiEcgSummary = addHuaweiEcgSummarySample(schema, user, device); + addHuaweiEcgDataSample(schema, huaweiEcgSummary); + Entity huaweiDictData = addHuaweiDictData(schema, user, device); addHuaweiDictDataValues(schema, huaweiDictData); @@ -1894,6 +1899,42 @@ public class GBDaoGenerator { return workoutSectionsSample; } + private static Entity addHuaweiEcgSummarySample(Schema schema, Entity user, Entity device) { + Entity ecgSummary = addEntity(schema, "HuaweiEcgSummarySample"); + + ecgSummary.setJavaDoc("Contains Huawei Ecg Summary samples (one per measurement)"); + + ecgSummary.addLongProperty("ecgId").primaryKey().autoincrement(); + + Property deviceId = ecgSummary.addLongProperty("deviceId").notNull().getProperty(); + ecgSummary.addToOne(device, deviceId); + Property userId = ecgSummary.addLongProperty("userId").notNull().getProperty(); + ecgSummary.addToOne(user, userId); + + ecgSummary.addLongProperty("startTimestamp").notNull().index(); + ecgSummary.addLongProperty("endTimestamp").notNull().index(); + ecgSummary.addStringProperty("appVersion").notNull(); + ecgSummary.addIntProperty("averageHeartRate").notNull(); + ecgSummary.addLongProperty("arrhythmiaType").notNull().index(); + ecgSummary.addLongProperty("userSymptoms").notNull(); + + return ecgSummary; + } + + private static Entity addHuaweiEcgDataSample(Schema schema, Entity summaryEntity) { + Entity ecgDataSample = addEntity(schema, "HuaweiEcgDataSample"); + + ecgDataSample.setJavaDoc("Contains Huawei Ecg Data samples (multiple per summary)"); + + Property id = ecgDataSample.addLongProperty("ecgId").primaryKey().notNull().getProperty(); + ecgDataSample.addToOne(summaryEntity, id); + + ecgDataSample.addIntProperty("timeDelta").notNull().primaryKey(); + ecgDataSample.addFloatProperty("value").notNull(); + + return ecgDataSample; + } + private static Entity addUltrahumanActivitySample(Schema schema, Entity user, Entity device) { Entity sample = addEntity(schema, "UltrahumanActivitySample"); 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 614c8f87c3..eb304477c2 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 @@ -50,6 +50,9 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiDictData; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiDictDataDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiDictDataValuesDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEmotionsSampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiHrvValueSampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStageSampleDao; @@ -187,8 +190,16 @@ public class HuaweiCoordinator { HuaweiDictDataValuesDao.Properties.DictId.eq(data.getDictId()) ).buildDelete().executeDeleteWithoutDetachingEntities(); } - session.getHuaweiDictDataDao().queryBuilder().where(HuaweiDictDataDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); + + QueryBuilder qb4 = session.getHuaweiEcgSummarySampleDao().queryBuilder(); + List ecgSummary = qb4.where(HuaweiEcgSummarySampleDao.Properties.DeviceId.eq(deviceId)).build().list(); + for (HuaweiEcgSummarySample sample : ecgSummary) { + session.getHuaweiEcgDataSampleDao().queryBuilder().where( + HuaweiEcgDataSampleDao.Properties.EcgId.eq(sample.getEcgId()) + ).buildDelete().executeDeleteWithoutDetachingEntities(); + } + session.getHuaweiEcgSummarySampleDao().queryBuilder().where(HuaweiEcgSummarySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); } private SharedPreferences getCapabilitiesSharedPreferences() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiEcgFileParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiEcgFileParser.java new file mode 100644 index 0000000000..5643766501 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiEcgFileParser.java @@ -0,0 +1,265 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei; + +import androidx.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HuaweiEcgFileParser { + private static final Logger LOG = LoggerFactory.getLogger(HuaweiEcgFileParser.class); + + public static class EcgParseException extends Exception { + public EcgParseException(String message) { + super(message); + } + } + + public static class EcgFileData { + private final List ecgDataList = new ArrayList<>(); + private int unkn1; + private long unkn2; + private byte[] extraData; + private int packageNameLength; + private String packageName; + private int version; + + + public void setUnkn1(int unkn1) { + this.unkn1 = unkn1; + } + public void setUnkn2(long unkn2) { + this.unkn2 = unkn2; + } + + public int getVersion() { + return this.version; + } + + public void setVersion(int version) { + this.version = version; + } + + public void setExtraData(byte[] extraData) { + this.extraData = extraData; + } + + public List getEcgDataList() { + return this.ecgDataList; + } + + public int getPackageNameLength() { + return this.packageNameLength; + } + + public void setPackageNameLength(int packageNameLength) { + this.packageNameLength = packageNameLength; + } + + public String getPackageName() { + return this.packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + @NonNull + @Override + public String toString() { + return "EcgFile{" + "ecgDataList=" + ecgDataList + + ", unkn1=" + unkn1 + + ", unkn2=" + unkn2 + + ", extraData='" + Arrays.toString(extraData) + '\'' + + ", packageNameLength=" + packageNameLength + + ", packageName='" + packageName + '\'' + + ", version=" + version + + '}'; + } + } + + public static class EcgData { + private long startTime; + private long endTime; + private int ecgDataLength; + private String appVersion; + private int averageHeartRate; + private long arrhythmiaType; + private long userSymptoms; + private List ecgData; + + public long getStartTime() { + return this.startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getEndTime() { + return this.endTime; + } + + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + public int getEcgDataLength() { + return this.ecgDataLength; + } + + public void setEcgDataLength(int ecgDataLength) { + this.ecgDataLength = ecgDataLength; + } + + public long getArrhythmiaType() { + return this.arrhythmiaType; + } + + public void setArrhythmiaType(long arrhythmiaType) { + this.arrhythmiaType = arrhythmiaType; + } + + public int getAverageHeartRate() { + return this.averageHeartRate; + } + + public void setAverageHeartRate(int averageHeartRate) { + this.averageHeartRate = averageHeartRate; + } + + public long getUserSymptoms() { + return this.userSymptoms; + } + + public void setUserSymptoms(long userSymptoms) { + this.userSymptoms = userSymptoms; + } + + public List getEcgData() { + return this.ecgData; + } + + public void setEcgData(List ecgData) { + this.ecgData = ecgData; + } + + public String getAppVersion() { + return this.appVersion; + } + + public void setAppVersion(String appVersion) { + this.appVersion = appVersion; + } + + @NonNull + @Override + public String toString() { + return "EcgData{" + "startTime=" + startTime + + ", endTime=" + endTime + + ", averageHeartRate=" + averageHeartRate + + ", arrhythmiaType=" + arrhythmiaType + + ", ecgAppVersion='" + appVersion + '\'' + + ", ecgDataLength=" + ecgDataLength + + ", ecgData=" + ecgData + + ", userSymptoms=" + userSymptoms + + '}'; + } + } + private static long checkTime(long tm) throws EcgParseException { + long time = tm * 1000; + if (time < 0 || time > System.currentTimeMillis()) { + LOG.error("wrong data."); + throw new EcgParseException("wrong time: " + tm); + } + return time; + } + + private static int checkDataLength(long len) { + if (len <= 0 || len > 2400000L) { + LOG.error("length is invalid: {}", len); + return 0; + } + return (int) len; + } + + private static void parseEcgData(ByteBuffer buffer, EcgFileData ecgFileData) throws EcgParseException { + while (buffer.remaining() >= 56) { + EcgData ecgData = new EcgData(); + long startTime = checkTime(buffer.getInt() & 0xFFFFFFFFL); + long endTime = checkTime(buffer.getInt() & 0xFFFFFFFFL); + if (startTime > endTime) { + throw new EcgParseException("wrong time. startTime: " + startTime + "endTime: " + endTime); + } + ecgData.setStartTime(startTime); + ecgData.setEndTime(endTime); + ecgData.setEcgDataLength(checkDataLength(buffer.getInt() & 0xFFFFFFFFL)); + byte[] appVersionBytes = new byte[32]; + buffer.get(appVersionBytes); + ecgData.setAppVersion(new String(appVersionBytes, StandardCharsets.UTF_8).trim()); + ecgData.setArrhythmiaType(buffer.getInt() & 0xFFFFFFFFL); + ecgData.setAverageHeartRate(buffer.getShort() & 0xFFFF); + buffer.getShort(); // unknown, always 0, can be a part of symptom or heart rate + ecgData.setUserSymptoms(buffer.getInt() & 0xFFFFFFFFL); + + if (buffer.remaining() >= ecgData.getEcgDataLength()) { + ArrayList arrayList = new ArrayList<>(); + int i = ecgData.getEcgDataLength(); + while (i > 0) { + float valueOf = buffer.getFloat(); //Float.intBitsToFloat((int) (buffer.getInt() & 0xFFFFFFFFL)); + if (Float.isNaN(valueOf)) { + System.out.println("unitStringFloat isNaN"); + valueOf = 0.0f; + } + arrayList.add(valueOf); + i -= 4; + } + ecgData.setEcgData(arrayList); + if (ecgFileData.getEcgDataList() != null) { + ecgFileData.getEcgDataList().add(ecgData); + } + } else { + throw new EcgParseException("ecgData is invalid"); + } + } + } + + public static EcgFileData parseEcgFile(byte[] data) throws EcgParseException { + if (data.length < 32) { + throw new EcgParseException("data is invalid"); + } + ByteBuffer buffer = ByteBuffer.wrap(data); + buffer.order(ByteOrder.BIG_ENDIAN); + + EcgFileData ecgFileData = new EcgFileData(); + ecgFileData.setUnkn2(buffer.getInt() & 0xFFFFFFFFL); + ecgFileData.setUnkn1(buffer.getShort() & 0xFFFF); + ecgFileData.setVersion(buffer.getShort() & 0xFFFF); + ecgFileData.setPackageNameLength(buffer.get() & 0xFF); + byte[] extraData = new byte[23]; + buffer.get(extraData); + ecgFileData.setExtraData(extraData); + + if (ecgFileData.getPackageNameLength() > buffer.remaining()) { + throw new EcgParseException("not enough data for package name"); + } + + byte[] packageNameBytes = new byte[ecgFileData.getPackageNameLength()]; + buffer.get(packageNameBytes); + ecgFileData.setPackageName(new String(packageNameBytes, StandardCharsets.UTF_8).trim()); + + if (ecgFileData.getVersion() == 0) { + parseEcgData(buffer, ecgFileData); + } else { + LOG.error("version is not supported"); + } + return ecgFileData; + } +} + 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 4ac7cb8e8f..059366d323 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 @@ -51,6 +51,7 @@ import java.util.Map; import java.util.Random; import java.util.UUID; +import de.greenrobot.dao.Property; import de.greenrobot.dao.query.DeleteQuery; import de.greenrobot.dao.query.QueryBuilder; import kotlin.Triple; @@ -72,6 +73,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupp import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupplier.HuaweiDeviceType; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiDictTypes; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiEcgFileParser; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiEmotionsSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiGpsParser; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiHrvValueSampleProvider; @@ -97,11 +99,17 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary; import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEmotionsSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiHrvValueSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepApneaSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStageSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStatsSample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStatsSampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiStressSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiTemperatureSample; import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSample; @@ -1491,6 +1499,7 @@ public class HuaweiSupportProvider { int sleepStart = 0; int stepStart = 0; + int ecgStart = 0; final int end = (int) (System.currentTimeMillis() / 1000); SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()); @@ -1498,6 +1507,7 @@ public class HuaweiSupportProvider { if (prefLastSyncTime != 0) { sleepStart = (int) (prefLastSyncTime / 1000); stepStart = (int) (prefLastSyncTime / 1000); + ecgStart = (int) (prefLastSyncTime / 1000); // Reset for next calls sharedPreferences.edit().putLong("lastSyncTimeMillis", 0).apply(); @@ -1510,6 +1520,24 @@ public class HuaweiSupportProvider { HuaweiSleepStatsSampleProvider sleepStatsSampleProvider = new HuaweiSleepStatsSampleProvider(gbDevice, db.getDaoSession()); int sleepStart2 = (int) (sleepStatsSampleProvider.getLastSleepFetchTimestamp() / 1000L); sleepStart = Math.max(sleepStart, sleepStart2); + + QueryBuilder qb = db.getDaoSession().getHuaweiEcgSummarySampleDao().queryBuilder(); + Device dbDevice = DBHelper.findDevice(gbDevice, db.getDaoSession()); + if (dbDevice != null) { + final Property deviceProperty = HuaweiEcgSummarySampleDao.Properties.DeviceId; + final Property timestampProperty = HuaweiEcgSummarySampleDao.Properties.EndTimestamp; + + qb.where(deviceProperty.eq(dbDevice.getId())) + .orderDesc(timestampProperty) + .limit(1); + + List samples = qb.build().list(); + if (!samples.isEmpty()) { + HuaweiEcgSummarySample sample = samples.get(0); + ecgStart = (int) (sample.getEndTimestamp() / 1000); + } + } + } catch (Exception e) { LOG.warn("Exception for getting start times, using 01/01/2000 - 00:00:00."); } @@ -1519,6 +1547,10 @@ public class HuaweiSupportProvider { sleepStart = 946684800; if (stepStart == 0) stepStart = 946684800; + + // To avoid of downloading of very big file and OOM error set ECG start time to step start time. + if(ecgStart == 0) + ecgStart = stepStart; } final GetSleepDataCountRequest getSleepDataCountRequest; if (isBLE()) { @@ -1536,10 +1568,11 @@ public class HuaweiSupportProvider { final GetFitnessTotalsRequest getFitnessTotalsRequest = new GetFitnessTotalsRequest(this); final int start = sleepStart; + final int ecgSyncStart = ecgStart; getFitnessTotalsRequest.setFinalizeReq(new RequestCallback() { @Override public void call() { - if (!(downloadTruSleepData(start, end) && downloadStressData(start, end))) + if (!(downloadTruSleepData(start, end) && downloadStressData(start, end) && downloadEcgData(ecgSyncStart, end))) syncState.stopActivitySync(); } @@ -2121,6 +2154,57 @@ public class HuaweiSupportProvider { } } + public void addEcgData(HuaweiEcgFileParser.EcgData data) { + try (DBHandler db = GBApplication.acquireDB()) { + Long userId = DBHelper.getUser(db.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId(); + + // Avoid duplicates + QueryBuilder qb = db.getDaoSession().getHuaweiEcgSummarySampleDao().queryBuilder().where( + HuaweiEcgSummarySampleDao.Properties.UserId.eq(userId), + HuaweiEcgSummarySampleDao.Properties.DeviceId.eq(deviceId), + HuaweiEcgSummarySampleDao.Properties.StartTimestamp.eq(data.getStartTime()) + ); + List results = qb.build().list(); + Long ecgId = null; + if (!results.isEmpty()) + ecgId = results.get(0).getEcgId(); + + HuaweiEcgSummarySample summarySample = new HuaweiEcgSummarySample( + ecgId, + deviceId, + userId, + data.getStartTime(), + data.getEndTime(), + data.getAppVersion(), + data.getAverageHeartRate(), + data.getArrhythmiaType(), + data.getUserSymptoms() + ); + + db.getDaoSession().getHuaweiEcgSummarySampleDao().insertOrReplace(summarySample); + + // We should completely replace values. Delete all and insert again. + final DeleteQuery tableDeleteQuery = db.getDaoSession().getHuaweiEcgDataSampleDao().queryBuilder() + .where(HuaweiEcgDataSampleDao.Properties.EcgId.eq(summarySample.getEcgId())) + .buildDelete(); + tableDeleteQuery.executeDeleteWithoutDetachingEntities(); + + int sampleRate = (int) (data.getEcgData().size() / ((data.getEndTime() - data.getStartTime())/ 1000)); + int delta = 0; + List res = new ArrayList<>(); + for (Float d: data.getEcgData()) { + HuaweiEcgDataSample dataSample = new HuaweiEcgDataSample(summarySample.getEcgId(), delta, d); + res.add(dataSample); + delta += sampleRate; + } + db.getDaoSession().getHuaweiEcgDataSampleDao().insertInTx(res); + + } catch (Exception e) { + LOG.error("Failed to add ECG data to database", e); + } + } + public void addTotalFitnessData(int steps, int calories, int distance) { LOG.debug("FITNESS total steps: {}", steps); LOG.debug("FITNESS total calories: {}", calories); // TODO: May actually be kilocalories @@ -2217,7 +2301,7 @@ public class HuaweiSupportProvider { // We should completely replace values. Delete all and insert again. final DeleteQuery tableDeleteQuery = db.getDaoSession().getHuaweiWorkoutSummaryAdditionalValuesSampleDao().queryBuilder() - .where(HuaweiWorkoutSummaryAdditionalValuesSampleDao.Properties.WorkoutId.eq(workoutId)) + .where(HuaweiWorkoutSummaryAdditionalValuesSampleDao.Properties.WorkoutId.eq(summarySample.getWorkoutId())) .buildDelete(); tableDeleteQuery.executeDeleteWithoutDetachingEntities(); @@ -3321,6 +3405,49 @@ public class HuaweiSupportProvider { return true; } + public boolean downloadEcgData(int start, int end) { + if (!getHuaweiCoordinator().supportsECG()) + return false; + + syncState.setEcgSync(true); + + huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.ecgAnalysisFileRequest( + start, + end, + new HuaweiFileDownloadManager.FileDownloadCallback() { + @Override + public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) { + if (fileRequest.getData().length != 0) { + LOG.debug("Parsing ECG file"); + + HuaweiEcgFileParser.EcgFileData results = null; + try { + results = HuaweiEcgFileParser.parseEcgFile(fileRequest.getData()); + } catch (HuaweiEcgFileParser.EcgParseException e) { + LOG.error("Error parse ECG file", e); + } + LOG.info("ECG result: {}", results); + if (results != null && !results.getEcgDataList().isEmpty()) { + for(HuaweiEcgFileParser.EcgData dt: results.getEcgDataList()) { + addEcgData(dt); + } + } + } else { + LOG.debug("ECG file is empty"); + } + syncState.setEcgSync(false); + } + + @Override + public void downloadException(HuaweiFileDownloadManager.HuaweiFileDownloadException e) { + super.downloadException(e); + syncState.setEcgSync(false); + } + } + ), true); + return true; + } + public void nextWorkoutSync(List remainder, RequestCallback finalizeReq) { if (!remainder.isEmpty()) { GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest( diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSyncState.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSyncState.java index 460bfacd02..020edf2ad6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSyncState.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSyncState.java @@ -18,6 +18,7 @@ class HuaweiSyncState { private boolean activitySync = false; private boolean p2pSync = false; private boolean stressSync = false; + private boolean ecgSync = false; private boolean workoutSync = false; private int workoutGpsDownload = 0; @@ -26,7 +27,7 @@ class HuaweiSyncState { } private boolean isSyncActive() { - return activitySync || p2pSync || stressSync || workoutSync || workoutGpsDownload != 0; + return activitySync || p2pSync || stressSync || ecgSync || workoutSync || workoutGpsDownload != 0; } private String activeSync() { @@ -37,6 +38,8 @@ class HuaweiSyncState { retv.append("p2pSync,"); if (stressSync) retv.append("stressSync,"); + if (ecgSync) + retv.append("ecgSync,"); if (workoutSync) retv.append("workoutSync,"); if (workoutGpsDownload != 0) { @@ -95,7 +98,7 @@ class HuaweiSyncState { // We cannot do the syncActive check for the P2P sync as it runs in parallel with the activity sync LOG.debug("Set p2p sync state to {}", state); this.p2pSync = state; - if (!state && !this.activitySync && !this.stressSync) { + if (!state && !this.activitySync && !this.stressSync && !this.ecgSync) { this.syncQueue.remove((Integer) RecordedDataTypes.TYPE_ACTIVITY); supportProvider.fetchRecodedDataFromQueue(); } @@ -106,6 +109,17 @@ class HuaweiSyncState { // We cannot do the syncActive check for the stress sync as it runs in parallel with the activity sync (sleep file specifically) LOG.debug("Set stress sync state to {}", state); this.stressSync = state; + if (!state && !this.activitySync && !this.p2pSync && !this.ecgSync) { + this.syncQueue.remove((Integer) RecordedDataTypes.TYPE_ACTIVITY); + supportProvider.fetchRecodedDataFromQueue(); + } + updateState(); + } + + public void setEcgSync(boolean state) { + // We cannot do the syncActive check for the stress sync as it runs in parallel with the activity sync (sleep file specifically) + LOG.debug("Set ECG sync state to {}", state); + this.ecgSync = state; if (!state && !this.activitySync && !this.p2pSync) { this.syncQueue.remove((Integer) RecordedDataTypes.TYPE_ACTIVITY); supportProvider.fetchRecodedDataFromQueue();