From a46e970f84fa52f0ec19326515854e79681c73ec Mon Sep 17 00:00:00 2001 From: Me7c7 Date: Sat, 26 Oct 2024 08:14:23 +0300 Subject: [PATCH] Huawei: more details for swim workout --- .../gadgetbridge/daogen/GBDaoGenerator.java | 27 +++- .../schema/GadgetbridgeUpdate_85.java | 22 ++++ .../devices/huawei/HuaweiCoordinator.java | 5 + .../devices/huawei/HuaweiPacket.java | 2 + .../devices/huawei/packets/Workout.java | 104 +++++++++++++++- .../devices/huawei/HuaweiSupportProvider.java | 43 ++++++- .../devices/huawei/HuaweiWorkoutGbParser.java | 117 ++++++++++++++++-- .../requests/GetWorkoutDataRequest.java | 10 ++ .../requests/GetWorkoutPaceRequest.java | 12 +- .../GetWorkoutSwimSegmentsRequest.java | 102 +++++++++++++++ .../requests/GetWorkoutTotalsRequest.java | 12 +- 11 files changed, 440 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_85.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutSwimSegmentsRequest.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index aca4e0940..77da82bde 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -54,7 +54,7 @@ public class GBDaoGenerator { public static void main(String[] args) throws Exception { - final Schema schema = new Schema(84, MAIN_PACKAGE + ".entities"); + final Schema schema = new Schema(85, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -151,6 +151,7 @@ public class GBDaoGenerator { Entity huaweiWorkoutSummary = addHuaweiWorkoutSummarySample(schema, user, device); addHuaweiWorkoutDataSample(schema, huaweiWorkoutSummary); addHuaweiWorkoutPaceSample(schema, huaweiWorkoutSummary); + addHuaweiWorkoutSwimSegmentsSample(schema, huaweiWorkoutSummary); addCalendarSyncState(schema, device); addAlarms(schema, user, device); @@ -1387,6 +1388,8 @@ public class GBDaoGenerator { workoutSummary.addByteArrayProperty("recoveryHeartRates"); + workoutSummary.addByteProperty("swimType").notNull(); + return workoutSummary; } @@ -1443,6 +1446,28 @@ public class GBDaoGenerator { return workoutPaceSample; } + private static Entity addHuaweiWorkoutSwimSegmentsSample(Schema schema, Entity summaryEntity) { + Entity workoutSwimSegmentsSample = addEntity(schema, "HuaweiWorkoutSwimSegmentsSample"); + + workoutSwimSegmentsSample.setJavaDoc("Contains Huawei Workout swim segments data samples"); + + Property id = workoutSwimSegmentsSample.addLongProperty("workoutId").primaryKey().notNull().getProperty(); + workoutSwimSegmentsSample.addToOne(summaryEntity, id); + + workoutSwimSegmentsSample.addIntProperty("segmentIndex").notNull().primaryKey(); + workoutSwimSegmentsSample.addIntProperty("distance").notNull().primaryKey(); + workoutSwimSegmentsSample.addByteProperty("type").notNull().primaryKey(); + workoutSwimSegmentsSample.addIntProperty("pace").notNull(); + workoutSwimSegmentsSample.addIntProperty("pointIndex").notNull(); + workoutSwimSegmentsSample.addIntProperty("segment").notNull(); + workoutSwimSegmentsSample.addByteProperty("swimType").notNull(); + workoutSwimSegmentsSample.addIntProperty("strokes").notNull(); + workoutSwimSegmentsSample.addIntProperty("avgSwolf").notNull(); + workoutSwimSegmentsSample.addIntProperty("time").notNull(); + + return workoutSwimSegmentsSample; + } + private static void addTemperatureProperties(Entity activitySample) { activitySample.addFloatProperty(SAMPLE_TEMPERATURE).notNull().codeBeforeGetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_TEMPERATURE_TYPE).notNull().codeBeforeGetter(OVERRIDE); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_85.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_85.java new file mode 100644 index 000000000..7579729d1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_85.java @@ -0,0 +1,22 @@ +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_85 implements DBUpdateScript { + @Override + public void upgradeSchema(final SQLiteDatabase db) { + if (!DBHelper.existsColumn(HuaweiWorkoutSummarySampleDao.TABLENAME, HuaweiWorkoutSummarySampleDao.Properties.SwimType.columnName, db)) { + final String statement = "ALTER TABLE " + HuaweiWorkoutSummarySampleDao.TABLENAME + " ADD COLUMN \"" + + HuaweiWorkoutSummarySampleDao.Properties.SwimType.columnName + "\" INTEGER NOT NULL DEFAULT -1;"; + db.execSQL(statement); + } + } + + @Override + public void downgradeSchema(final SQLiteDatabase db) { + } +} 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 448a9a912..b847d2270 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,7 @@ 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.entities.HuaweiWorkoutSwimSegmentsSampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -124,6 +125,10 @@ public class HuaweiCoordinator { session.getHuaweiWorkoutPaceSampleDao().queryBuilder().where( HuaweiWorkoutPaceSampleDao.Properties.WorkoutId.eq(sample.getWorkoutId()) ).buildDelete().executeDeleteWithoutDetachingEntities(); + + session.getHuaweiWorkoutSwimSegmentsSampleDao().queryBuilder().where( + HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(sample.getWorkoutId()) + ).buildDelete().executeDeleteWithoutDetachingEntities(); } session.getHuaweiWorkoutSummarySampleDao().queryBuilder().where(HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java index e653a2c5d..175a840ae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java @@ -570,6 +570,8 @@ public class HuaweiPacket { return new Workout.WorkoutData.Response(paramsProvider).fromPacket(this); case Workout.WorkoutPace.id: return new Workout.WorkoutPace.Response(paramsProvider).fromPacket(this); + case Workout.WorkoutSwimSegments.id: + return new Workout.WorkoutSwimSegments.Response(paramsProvider).fromPacket(this); default: this.isEncrypted = this.attemptDecrypt(); // Helps with debugging return this; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java index 7163e9066..01f9797ee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Workout.java @@ -58,6 +58,8 @@ public class Workout { public short workoutNumber; public short dataCount; public short paceCount; + public short segmentsCount = 0; + } public short count; @@ -84,6 +86,9 @@ public class Workout { workoutNumber.workoutNumber = subContainerTlv.getShort(0x06); workoutNumber.dataCount = subContainerTlv.getShort(0x07); workoutNumber.paceCount = subContainerTlv.getShort(0x08); + if(subContainerTlv.contains(0x09)) { + workoutNumber.segmentsCount = subContainerTlv.getShort(0x09); + } this.workoutNumbers.add(workoutNumber); } } @@ -143,6 +148,8 @@ public class Workout { public byte[] recoveryHeartRates = null; + public byte swimType = -1; + public Response(ParamsProvider paramsProvider) { super(paramsProvider); @@ -184,7 +191,8 @@ public class Workout { this.duration = container.getInteger(0x12); if (container.contains(0x14)) this.type = container.getByte(0x14); - // TODO: I'm guessing 0x15 is Main style for swimming, but cannot confirm. + if (container.contains(0x15)) + this.swimType = container.getByte(0x15); if (container.contains(0x16)) this.strokes = container.getShort(0x16); if (container.contains(0x17)) @@ -565,6 +573,100 @@ public class Workout { } } + public static class WorkoutSwimSegments { + public static final int id = 0x0e; + + public static class Request extends HuaweiPacket { + + public Request( + ParamsProvider paramsProvider, + short workoutNumber, + short segmentNumber + ) { + super(paramsProvider); + + this.serviceId = Workout.id; + this.commandId = id; + + this.tlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, workoutNumber) + .put(0x08, segmentNumber) + ); + + this.complete = true; + } + } + + public static class Response extends HuaweiPacket { + public static class Block { + public short distance = -1; + public byte type = -1; + public int pace = -1; + public short pointIndex = 0; + public short segment = -1; + public byte swimType= -1; + public short strokes = -1; + public short avgSwolf = -1; + public int time= -1; + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("Block{"); + sb.append("distance=").append(distance); + sb.append(", type=").append(type); + sb.append(", pace=").append(pace); + sb.append(", pointIndex=").append(pointIndex); + sb.append(", segment=").append(segment); + sb.append(", swimType=").append(swimType); + sb.append(", strokes=").append(strokes); + sb.append(", awgSwolf=").append(avgSwolf); + sb.append(", time=").append(time); + sb.append('}'); + return sb.toString(); + } + } + + public short workoutNumber; + public short segmentNumber; + public List blocks; + + public Response(ParamsProvider paramsProvider) { + super(paramsProvider); + } + + @Override + public void parseTlv() throws ParseException { + HuaweiTLV container = this.tlv.getObject(0x81); + + this.workoutNumber = container.getShort(0x02); + this.segmentNumber = container.getShort(0x08); + + this.blocks = new ArrayList<>(); + for (HuaweiTLV blockTlv : container.getObjects(0x83)) { + Block block = new Block(); + + block.distance = blockTlv.getShort(0x04); + block.type = blockTlv.getByte(0x05); + block.pace = blockTlv.getInteger(0x06); + if (blockTlv.contains(0x07)) + block.pointIndex = blockTlv.getShort(0x07); + if (blockTlv.contains(0x09)) + block.segment = blockTlv.getShort(0x09); + if (blockTlv.contains(0x0a)) + block.swimType= blockTlv.getByte(0x0a); + if (blockTlv.contains(0x0b)) + block.strokes = blockTlv.getShort(0x0b); + if (blockTlv.contains(0x0c)) + block.avgSwolf = blockTlv.getShort(0x0c); + if (blockTlv.contains(0x0d)) + block.time= blockTlv.getInteger(0x0d); + + blocks.add(block); + } + } + } + } + public static class NotifyHeartRate { public static final int id = 0x17; 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 0a1129a71..00595a1ca 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 @@ -74,6 +74,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.entities.HuaweiWorkoutSwimSegmentsSample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSwimSegmentsSampleDao; import nodomain.freeyourgadget.gadgetbridge.export.ActivityTrackExporter; import nodomain.freeyourgadget.gadgetbridge.export.GPXExporter; import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationProviderType; @@ -1646,7 +1648,8 @@ public class HuaweiSupportProvider { packet.recoveryTime, packet.minHeartRatePeak, packet.maxHeartRatePeak, - recoveryHeartRates + recoveryHeartRates, + packet.swimType ); db.getDaoSession().getHuaweiWorkoutSummarySampleDao().insertOrReplace(summarySample); @@ -1735,6 +1738,44 @@ public class HuaweiSupportProvider { } } + + public void addWorkoutSwimSegmentsData(Long workoutId, List paceList, short number) { + if (workoutId == null) + return; + + try (DBHandler db = GBApplication.acquireDB()) { + HuaweiWorkoutSwimSegmentsSampleDao dao = db.getDaoSession().getHuaweiWorkoutSwimSegmentsSampleDao(); + + if(number == 0) { + final DeleteQuery tableDeleteQuery = dao.queryBuilder() + .where(HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(workoutId)) + .buildDelete(); + tableDeleteQuery.executeDeleteWithoutDetachingEntities(); + } + + int paceIndex = (int) dao.queryBuilder().where(HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(workoutId)).count(); + for (Workout.WorkoutSwimSegments.Response.Block block : paceList) { + HuaweiWorkoutSwimSegmentsSample swimSectionSample = new HuaweiWorkoutSwimSegmentsSample( + workoutId, + paceIndex++, + block.distance, + block.type, + block.pace, + block.pointIndex, + block.segment, + block.swimType, + block.strokes, + block.avgSwolf, + block.time + ); + dao.insertOrReplace(swimSectionSample); + } + } catch (Exception e) { + LOG.error("Failed to add workout swim section data to database", e); + } + } + + public void setWearLocation() { try { SetWearLocationRequest setWearLocationReq = new SetWearLocationRequest(this); 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 efaf501ef..9fe6fdd2a 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 @@ -22,9 +22,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -50,6 +50,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.entities.HuaweiWorkoutSwimSegmentsSample; +import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSwimSegmentsSampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; @@ -310,6 +312,20 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { return ActivityKind.UNKNOWN; } + private String getSwimStyle(byte swimType) { + switch (swimType) { + case 1: + return "breaststroke"; + case 3: + return "butterfly"; + case 4: + return "backstroke"; + case 5: + return "medley"; + } + return "freestyle"; + } + public void parseWorkout(Long workoutId) { LOG.debug("Parsing workout ID {}", workoutId); if (workoutId == null) @@ -361,6 +377,10 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { HuaweiWorkoutPaceSampleDao.Properties.WorkoutId.eq(summary.getWorkoutId()) ); + QueryBuilder qbSegments = session.getHuaweiWorkoutSwimSegmentsSampleDao().queryBuilder().orderAsc(HuaweiWorkoutSwimSegmentsSampleDao.Properties.SegmentIndex).where( + HuaweiWorkoutSwimSegmentsSampleDao.Properties.WorkoutId.eq(summary.getWorkoutId()) + ); + ActivityKind type = huaweiTypeToGbType(summary.getType()); ActivitySummaryData summaryData = new ActivitySummaryData(); @@ -376,11 +396,6 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { summaryData.add(ActivitySummaryEntries.STROKES, summary.getStrokes(), ActivitySummaryEntries.UNIT_STROKES); } - if (summary.getAvgStrokeRate() != -1) { - // TODO: find out unit - summaryData.add(ActivitySummaryEntries.STROKE_RATE_AVG, summary.getAvgStrokeRate(), ActivitySummaryEntries.UNIT_NONE); - } - if (summary.getPoolLength() != -1) { summaryData.add(ActivitySummaryEntries.LANE_LENGTH, summary.getPoolLength(), ActivitySummaryEntries.UNIT_CM); } @@ -389,10 +404,6 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { summaryData.add(ActivitySummaryEntries.LAPS, summary.getLaps(), ActivitySummaryEntries.UNIT_LAPS); } - if (summary.getAvgSwolf() != -1) { - summaryData.add(ActivitySummaryEntries.SWOLF_AVG, summary.getAvgSwolf(), ActivitySummaryEntries.UNIT_NONE); - } - if(summary.getWorkoutLoad() > 0) { summaryData.add(ActivitySummaryEntries.WORKOUT_LOAD, summary.getWorkoutLoad(), ActivitySummaryEntries.UNIT_NONE); } @@ -409,6 +420,10 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { summaryData.add(ActivitySummaryEntries.RECOVERY_TIME, summary.getRecoveryTime() / 60.0, ActivitySummaryEntries.UNIT_HOURS); } + if(summary.getSwimType() != -1) { + summaryData.add(ActivitySummaryEntries.SWIM_STYLE, getSwimStyle(summary.getSwimType())); + } + Integer summaryMinAltitude = summary.getMinAltitude(); Integer summaryMaxAltitude = summary.getMaxAltitude(); Integer elevationGain = summary.getElevationGain(); @@ -417,6 +432,10 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { int minHeartRatePeak = summary.getMinHeartRatePeak() & 0xff; int maxHeartRatePeak = summary.getMaxHeartRatePeak() & 0xff; + int avgStrokeRate = summary.getAvgStrokeRate(); + + int avgSwolf = summary.getAvgSwolf(); + boolean unknownData = false; if (!dataSamples.isEmpty()) { int speed = 0; @@ -640,12 +659,17 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { } if (swolfCount > 0) { - summaryData.add(ActivitySummaryEntries.SWOLF_AVG, swolf, ActivitySummaryEntries.UNIT_NONE); + if(avgSwolf == -1) { + avgSwolf = swolf; + } summaryData.add(ActivitySummaryEntries.SWOLF_MAX, maxSwolf, ActivitySummaryEntries.UNIT_NONE); summaryData.add(ActivitySummaryEntries.SWOLF_MIN, minSwolf, ActivitySummaryEntries.UNIT_NONE); } if (strokeRateCount > 0) { + if(avgStrokeRate == -1) { + avgStrokeRate = strokeRate; + } // TODO: find out unit? summaryData.add(ActivitySummaryEntries.STROKE_RATE_AVG, strokeRate, ActivitySummaryEntries.UNIT_NONE); @@ -695,6 +719,14 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { } } + if(avgSwolf > 0) { + summaryData.add(ActivitySummaryEntries.SWOLF_AVG, avgSwolf, ActivitySummaryEntries.UNIT_NONE); + } + + if (avgStrokeRate > 0) { + summaryData.add(ActivitySummaryEntries.STROKE_RATE_AVG, avgStrokeRate, ActivitySummaryEntries.UNIT_STROKES_PER_MINUTE); + } + if (minHeartRatePeak > 0) { summaryData.add(ActivitySummaryEntries.HR_MIN, minHeartRatePeak, ActivitySummaryEntries.UNIT_BPM); } @@ -816,6 +848,69 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser { } } + final LinkedHashMap segmentsTable = new LinkedHashMap<>(); + + try (CloseableListIterator it = qbSegments.build().listIterator()) { + + int currentIndex = 1; + int tableIndex = 1; + while (it.hasNext()) { + HuaweiWorkoutSwimSegmentsSample sample = it.next(); + + if (sample.getType() != unitType) + continue; + + final List columns = new LinkedList<>(); + // TODO: add proper units for type == 1. MILES + columns.add(new ActivitySummaryValue(currentIndex++, ActivitySummaryEntries.UNIT_NONE)); + columns.add(new ActivitySummaryValue(getSwimStyle(sample.getSwimType()), ActivitySummaryEntries.UNIT_NONE)); + columns.add(new ActivitySummaryValue(sample.getDistance(), ActivitySummaryEntries.UNIT_METERS)); + columns.add(new ActivitySummaryValue(sample.getTime(), ActivitySummaryEntries.UNIT_SECONDS)); + + + segmentsTable.put("segments_table_" + tableIndex++, + new ActivitySummaryTableRowEntry( + ActivitySummaryEntries.GROUP_PACE, + columns, + true, + true + ) + ); + + final List columns2 = new LinkedList<>(); + // TODO: add proper units for type == 1. MILES and SECONDS PER MILE + columns2.add(new ActivitySummaryValue("", ActivitySummaryEntries.UNIT_NONE)); + columns2.add(new ActivitySummaryValue(sample.getStrokes(), ActivitySummaryEntries.UNIT_STROKES)); + columns2.add(new ActivitySummaryValue(sample.getAvgSwolf(), ActivitySummaryEntries.UNIT_NONE)); + columns2.add(new ActivitySummaryValue(sample.getPace(), ActivitySummaryEntries.UNIT_NONE)); //TODO: seconds / 100 meters + + + + segmentsTable.put("segments_table_" + tableIndex++, + new ActivitySummaryTableRowEntry( + ActivitySummaryEntries.GROUP_PACE, + columns2, + false, + false + ) + ); + segmentsTable.put("segments_table_" + tableIndex++, + new ActivitySummaryTableRowEntry( + ActivitySummaryEntries.GROUP_PACE, + new ArrayList<>(), + false, + false + ) + ); + } + + if (!segmentsTable.isEmpty()) { + for (final Map.Entry e : segmentsTable.entrySet()) { + summaryData.add(e.getKey(), e.getValue()); + } + } + } + if (unknownData) { summaryData.add( GBApplication.getContext().getString(R.string.unknownDataEncountered), 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 fba97a0bb..1e587e0f2 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 @@ -112,6 +112,16 @@ public class GetWorkoutDataRequest extends Request { ); nextRequest.setFinalizeReq(this.finalizeReq); this.nextRequest(nextRequest); + } else if (this.workoutNumbers.segmentsCount > 0) { + GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest( + this.supportProvider, + this.workoutNumbers, + this.remainder, + (short) 0, + this.databaseId + ); + nextRequest.setFinalizeReq(this.finalizeReq); + this.nextRequest(nextRequest); } else { new HuaweiWorkoutGbParser(getDevice()).parseWorkout(this.databaseId); supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, new Runnable() { 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 b9e3fa596..d859ca2c9 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 @@ -89,7 +89,17 @@ public class GetWorkoutPaceRequest extends Request { ); nextRequest.setFinalizeReq(this.finalizeReq); this.nextRequest(nextRequest); - } else { + } else if (this.workoutNumbers.segmentsCount > 0) { + GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest( + this.supportProvider, + this.workoutNumbers, + this.remainder, + (short) 0, + this.databaseId + ); + nextRequest.setFinalizeReq(this.finalizeReq); + this.nextRequest(nextRequest); + } else { new HuaweiWorkoutGbParser(getDevice()).parseWorkout(this.databaseId); supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, new Runnable() { @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutSwimSegmentsRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutSwimSegmentsRequest.java new file mode 100644 index 000000000..bbba995d4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetWorkoutSwimSegmentsRequest.java @@ -0,0 +1,102 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWorkoutGbParser; + +public class GetWorkoutSwimSegmentsRequest extends Request { + private static final Logger LOG = LoggerFactory.getLogger(GetWorkoutSwimSegmentsRequest.class); + + Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers; + List remainder; + short number; + Long databaseId; + + public GetWorkoutSwimSegmentsRequest(HuaweiSupportProvider support, Workout.WorkoutCount.Response.WorkoutNumbers workoutNumbers, List remainder, short number, Long databaseId) { + super(support); + + this.serviceId = Workout.id; + this.commandId = Workout.WorkoutSwimSegments.id; + + this.workoutNumbers = workoutNumbers; + this.remainder = remainder; + this.number = number; + + this.databaseId = databaseId; + } + + @Override + protected List createRequest() throws RequestCreationException { + try { + return new Workout.WorkoutSwimSegments.Request(paramsProvider,this.workoutNumbers.workoutNumber, this.number).serialize(); + } catch (HuaweiPacket.CryptoException e) { + throw new RequestCreationException(e); + } + } + + @Override + protected void processResponse() throws ResponseParseException { + if (!(receivedPacket instanceof Workout.WorkoutSwimSegments.Response)) + throw new ResponseTypeMismatchException(receivedPacket, Workout.WorkoutSwimSegments.Response.class); + + Workout.WorkoutSwimSegments.Response packet = (Workout.WorkoutSwimSegments.Response) receivedPacket; + + if (packet.workoutNumber != this.workoutNumbers.workoutNumber) + throw new WorkoutParseException("Incorrect workout number!"); + + if (packet.segmentNumber != this.number) + throw new WorkoutParseException("Incorrect pace number!"); + + LOG.info("Workout {} segment {}:", this.workoutNumbers.workoutNumber, this.number); + LOG.info("Workout : " + packet.workoutNumber); + LOG.info("Segments : " + packet.segmentNumber); + LOG.info("Block num: " + packet.blocks.size()); + LOG.info("Blocks : " + Arrays.toString(packet.blocks.toArray())); + + supportProvider.addWorkoutSwimSegmentsData(this.databaseId, packet.blocks, packet.segmentNumber); + + if (this.workoutNumbers.segmentsCount > this.number + 1) { + GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest( + this.supportProvider, + this.workoutNumbers, + this.remainder, + (short) (this.number + 1), + this.databaseId + ); + nextRequest.setFinalizeReq(this.finalizeReq); + this.nextRequest(nextRequest); + } else { + new HuaweiWorkoutGbParser(getDevice()).parseWorkout(this.databaseId); + supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, this.databaseId, new Runnable() { + @Override + public void run() { + if (!remainder.isEmpty()) { + GetWorkoutTotalsRequest nextRequest = new GetWorkoutTotalsRequest( + GetWorkoutSwimSegmentsRequest.this.supportProvider, + remainder.remove(0), + remainder + ); + nextRequest.setFinalizeReq(GetWorkoutSwimSegmentsRequest.this.finalizeReq); + // Cannot do this with nextRequest because it's in a callback + try { + nextRequest.doPerform(); + } catch (IOException e) { + finalizeReq.handleException(new ResponseParseException("Cannot send next request", e)); + } + } else { + supportProvider.endOfWorkoutSync(); + } + } + }); + } + + } +} 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 ef7d25ae6..ed49bfdbd 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 @@ -103,7 +103,17 @@ public class GetWorkoutTotalsRequest extends Request { ); nextRequest.setFinalizeReq(this.finalizeReq); this.nextRequest(nextRequest); - } else { + } else if (this.workoutNumbers.segmentsCount > 0) { + GetWorkoutSwimSegmentsRequest nextRequest = new GetWorkoutSwimSegmentsRequest( + this.supportProvider, + this.workoutNumbers, + this.remainder, + (short) 0, + databaseId + ); + nextRequest.setFinalizeReq(this.finalizeReq); + this.nextRequest(nextRequest); + } else { new HuaweiWorkoutGbParser(getDevice()).parseWorkout(databaseId); supportProvider.downloadWorkoutGpsFiles(this.workoutNumbers.workoutNumber, databaseId, new Runnable() { @Override