diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminWorkoutParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminWorkoutParser.java index ae95af115..cdb35fd2f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminWorkoutParser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminWorkoutParser.java @@ -28,6 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitFile; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.enums.GarminSport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitLap; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitPhysiologicalMetrics; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRecord; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession; @@ -47,6 +48,7 @@ public class GarminWorkoutParser implements ActivitySummaryParser { private FitSport sport = null; private FitPhysiologicalMetrics physiologicalMetrics = null; private final List sets = new ArrayList<>(); + private final List laps = new ArrayList<>(); public GarminWorkoutParser(final Context context) { this.context = context; @@ -102,6 +104,7 @@ public class GarminWorkoutParser implements ActivitySummaryParser { sport = null; physiologicalMetrics = null; sets.clear(); + laps.clear(); } public boolean handleRecord(final RecordData record) { @@ -132,6 +135,9 @@ public class GarminWorkoutParser implements ActivitySummaryParser { } else if (record instanceof FitSet) { LOG.trace("Set: {}", record); sets.add((FitSet) record); + } else if (record instanceof FitLap) { + LOG.trace("Lap: {}", record); + laps.add((FitLap) record); } else { return false; } @@ -166,6 +172,12 @@ public class GarminWorkoutParser implements ActivitySummaryParser { if (session.getTotalDistance() != null) { summaryData.add(DISTANCE_METERS, session.getTotalDistance() / 100f, UNIT_METERS); } + if (session.getPoolLength() != null) { + summaryData.add(POOL_LENGTH, session.getPoolLength(), UNIT_METERS); + } + if (session.getAvgSwolf() != null) { + summaryData.add(SWOLF_AVG, session.getAvgSwolf(), UNIT_NONE); + } if (session.getTotalSteps() != null) { summaryData.add(STEPS, session.getTotalSteps(), UNIT_STEPS); } @@ -214,6 +226,9 @@ public class GarminWorkoutParser implements ActivitySummaryParser { if (session.getTotalDescent() != null) { summaryData.add(DESCENT_DISTANCE, session.getTotalDescent(), UNIT_METERS); } + if (session.getAvgSwimCadence() != null) { + summaryData.add(SWIM_AVG_CADENCE, session.getAvgSwimCadence(), UNIT_STROKES_PER_LENGTH); + } if (session.getEnhancedAvgSpeed() != null) { if (ActivityKind.isPaceActivity(activityKind)) { @@ -343,6 +358,10 @@ public class GarminWorkoutParser implements ActivitySummaryParser { } } + if (!laps.isEmpty()) { + final boolean anySwolf = laps.stream().anyMatch(l -> l.getAvgSwolf() != null); + } + summaryData.add( INTERNAL_HAS_GPS, String.valueOf(activityPoints.stream().anyMatch(p -> p.getLocation() != null)) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java index e7d3a601c..d38ac8f00 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java @@ -67,6 +67,7 @@ public class ActivitySummaryEntries { public static final String EVERSION_ANGLE_MAX = "eversionAngleMax"; public static final String DISTANCE_METERS = "distanceMeters"; + public static final String POOL_LENGTH = "poolLength"; public static final String ELEVATION_GAIN = "elevationGain"; public static final String ELEVATION_LOSS = "elevationLoss"; @@ -119,6 +120,7 @@ public class ActivitySummaryEntries { public static final String SWOLF_AVG = "swolfAvg"; public static final String SWOLF_MAX = "swolfMax"; public static final String SWOLF_MIN = "swolfMin"; + public static final String SWIM_AVG_CADENCE = "swim_avg_cadence"; public static final String CALORIES_BURNT = "caloriesBurnt"; public static final String TRAINING_EFFECT_AEROBIC = "aerobicTrainingEffect"; @@ -159,6 +161,7 @@ public class ActivitySummaryEntries { public static final String UNIT_STROKES = "strokes_unit"; public static final String UNIT_STROKES_PER_MINUTE = "strokes_minute"; public static final String UNIT_STROKES_PER_SECOND = "strokes_second"; + public static final String UNIT_STROKES_PER_LENGTH = "strokes_per_length"; public static final String UNIT_JUMPS = "jumps_unit"; public static final String UNIT_JUMPS_PER_MINUTE = "jumps_minute"; public static final String UNIT_YARD = "yard"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryJsonSummary.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryJsonSummary.java index d8d03d594..4cffcb72e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryJsonSummary.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryJsonSummary.java @@ -190,6 +190,7 @@ public class ActivitySummaryJsonSummary { JUMPS, JUMP_RATE_AVG, JUMP_RATE_MAX )); put(GROUP_SWIMMING, Arrays.asList( + POOL_LENGTH, SWIM_AVG_CADENCE, SWOLF_INDEX, SWOLF_AVG, SWOLF_MAX, SWOLF_MIN, SWIM_STYLE )); put(GROUP_TRAINING_EFFECT, Arrays.asList( diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java index 7468684b6..4e0e83158 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java @@ -10,6 +10,7 @@ import java.util.Map; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType; +/** @noinspection ArraysAsListWithZeroOrOneArgument*/ public class GlobalFITMessage { public static GlobalFITMessage FILE_ID = new GlobalFITMessage(0, "FILE_ID", Arrays.asList( new FieldDefinitionPrimitive(0, BaseType.ENUM, "type", FieldDefinitionFactory.FIELD.FILE_TYPE), @@ -112,8 +113,13 @@ public class GlobalFITMessage { new FieldDefinitionPrimitive(30, BaseType.SINT32, "nec_longitude", FieldDefinitionFactory.FIELD.COORDINATE), new FieldDefinitionPrimitive(31, BaseType.SINT32, "swc_latitude", FieldDefinitionFactory.FIELD.COORDINATE), new FieldDefinitionPrimitive(32, BaseType.SINT32, "swc_longitude", FieldDefinitionFactory.FIELD.COORDINATE), + new FieldDefinitionPrimitive(33, BaseType.UINT16, "num_lengths"), new FieldDefinitionPrimitive(38, BaseType.SINT32, "end_latitude", FieldDefinitionFactory.FIELD.COORDINATE), new FieldDefinitionPrimitive(39, BaseType.SINT32, "end_longitude", FieldDefinitionFactory.FIELD.COORDINATE), + new FieldDefinitionPrimitive(44, BaseType.UINT16, "pool_length", 100, 0), // m + new FieldDefinitionPrimitive(47, BaseType.UINT16, "num_active_lengths"), + new FieldDefinitionPrimitive(79, BaseType.UINT16, "avg_swim_cadence", 10, 0), // rpm + new FieldDefinitionPrimitive(80, BaseType.UINT16, "avg_swolf"), new FieldDefinitionPrimitive(110, BaseType.STRING, 64, "sport_profile_name"), new FieldDefinitionPrimitive(124, BaseType.UINT32, "enhanced_avg_speed", 1000, 0), // m/s new FieldDefinitionPrimitive(125, BaseType.UINT32, "enhanced_max_speed", 1000, 0), // m/s @@ -121,14 +127,19 @@ public class GlobalFITMessage { new FieldDefinitionPrimitive(170, BaseType.UINT16, "enhanced_max_respiration_rate", 100, 0 ), // breaths/min new FieldDefinitionPrimitive(178, BaseType.UINT16, "estimated_sweat_loss"), // ml new FieldDefinitionPrimitive(180, BaseType.UINT16, "enhanced_min_respiration_rate", 100, 0 ), // breaths/min + new FieldDefinitionPrimitive(188, BaseType.ENUM, "primary_benefit"), // 1 recovery new FieldDefinitionPrimitive(194, BaseType.UINT8, "avg_spo2"), new FieldDefinitionPrimitive(195, BaseType.UINT8, "avg_stress"), + new FieldDefinitionPrimitive(196, BaseType.UINT16, "resting_calories"), // kcal new FieldDefinitionPrimitive(197, BaseType.UINT8, "hrv_sdrr"), // ms new FieldDefinitionPrimitive(198, BaseType.UINT8, "hrv_rmssd"), // ms new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP) )); public static GlobalFITMessage LAP = new GlobalFITMessage(19, "LAP", Arrays.asList( + new FieldDefinitionPrimitive(0, BaseType.ENUM, "event"), // 9 lap + new FieldDefinitionPrimitive(1, BaseType.ENUM, "event_type"), // 1 stop + new FieldDefinitionPrimitive(2, BaseType.UINT32, "start_time"), new FieldDefinitionPrimitive(3, BaseType.SINT32, "start_lat", FieldDefinitionFactory.FIELD.COORDINATE), new FieldDefinitionPrimitive(4, BaseType.SINT32, "start_long", FieldDefinitionFactory.FIELD.COORDINATE), new FieldDefinitionPrimitive(5, BaseType.SINT32, "end_lat", FieldDefinitionFactory.FIELD.COORDINATE), @@ -136,8 +147,24 @@ public class GlobalFITMessage { new FieldDefinitionPrimitive(7, BaseType.UINT32, "total_elapsed_time", 1000, 0), // s new FieldDefinitionPrimitive(8, BaseType.UINT32, "total_timer_time", 1000, 0), // s new FieldDefinitionPrimitive(9, BaseType.UINT32, "total_distance", 100, 0), // m + new FieldDefinitionPrimitive(10, BaseType.UINT32, "total_cycles"), + new FieldDefinitionPrimitive(11, BaseType.UINT16, "total_calores"), + new FieldDefinitionPrimitive(15, BaseType.UINT8, "avg_heart_rate"), + new FieldDefinitionPrimitive(16, BaseType.UINT8, "max_heart_rate"), + new FieldDefinitionPrimitive(17, BaseType.UINT8, "avg_cadence"), // rpm new FieldDefinitionPrimitive(21, BaseType.UINT16, "total_ascent"), // m new FieldDefinitionPrimitive(22, BaseType.UINT16, "total_descent"), // m + new FieldDefinitionPrimitive(24, BaseType.ENUM, "lap_trigger"), // 0 manual + new FieldDefinitionPrimitive(25, BaseType.ENUM, "sport"), // 5 swimming + new FieldDefinitionPrimitive(32, BaseType.UINT16, "num_lengths"), + new FieldDefinitionPrimitive(35, BaseType.UINT16, "first_length_index"), + new FieldDefinitionPrimitive(37, BaseType.UINT16, "avg_stroke_distance"), + new FieldDefinitionPrimitive(38, BaseType.ENUM, "swim_style"), // 0 freestyle 2 breaststroke 5 mixed + new FieldDefinitionPrimitive(39, BaseType.ENUM, "sub_sport"), // 17 = lap swimming + new FieldDefinitionPrimitive(40, BaseType.UINT16, "num_active_lengths"), + new FieldDefinitionPrimitive(73, BaseType.UINT16, "avg_swolf"), + new FieldDefinitionPrimitive(110, BaseType.UINT32, "enhanced_avg_speed", 100, 0), // m/s + new FieldDefinitionPrimitive(111, BaseType.UINT32, "enhanced_max_speed", 100, 0), // m/s new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP) )); @@ -193,6 +220,16 @@ public class GlobalFITMessage { new FieldDefinitionPrimitive(5, BaseType.STRING, 16, "name") )); + public static GlobalFITMessage ACTIVITY = new GlobalFITMessage(34, "ACTIVITY", Arrays.asList( + new FieldDefinitionPrimitive(0, BaseType.UINT32, "total_timer_time"), + new FieldDefinitionPrimitive(1, BaseType.UINT16, "num_sessions"), + new FieldDefinitionPrimitive(2, BaseType.ENUM, "type"), // 0 + new FieldDefinitionPrimitive(3, BaseType.ENUM, "event"), // 26 activity + new FieldDefinitionPrimitive(4, BaseType.ENUM, "event_type"), // 1 stop + new FieldDefinitionPrimitive(5, BaseType.UINT32, "local_timestamp"), // garmin timestamp, but in user timezone + new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP) + )); + public static GlobalFITMessage FILE_CREATOR = new GlobalFITMessage(49, "FILE_CREATOR", Arrays.asList( new FieldDefinitionPrimitive(0, BaseType.UINT16, "software_version"), new FieldDefinitionPrimitive(1, BaseType.UINT8, "hardware_version") @@ -427,6 +464,7 @@ public class GlobalFITMessage { put(23, DEVICE_INFO); put(26, WORKOUT); put(31, COURSE); + put(34, ACTIVITY); put(49, FILE_CREATOR); put(55, MONITORING); put(103, MONITORING_INFO); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitActivity.java new file mode 100644 index 000000000..11f1c97f1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitActivity.java @@ -0,0 +1,57 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages; + +import androidx.annotation.Nullable; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData; +import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition; +import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader; + +// +// WARNING: This class was auto-generated, please avoid modifying it directly. +// See nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen +// +public class FitActivity extends RecordData { + public FitActivity(final RecordDefinition recordDefinition, final RecordHeader recordHeader) { + super(recordDefinition, recordHeader); + + final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber(); + if (globalNumber != 34) { + throw new IllegalArgumentException("FitActivity expects global messages of " + 34 + ", got " + globalNumber); + } + } + + @Nullable + public Long getTotalTimerTime() { + return (Long) getFieldByNumber(0); + } + + @Nullable + public Integer getNumSessions() { + return (Integer) getFieldByNumber(1); + } + + @Nullable + public Integer getType() { + return (Integer) getFieldByNumber(2); + } + + @Nullable + public Integer getEvent() { + return (Integer) getFieldByNumber(3); + } + + @Nullable + public Integer getEventType() { + return (Integer) getFieldByNumber(4); + } + + @Nullable + public Long getLocalTimestamp() { + return (Long) getFieldByNumber(5); + } + + @Nullable + public Long getTimestamp() { + return (Long) getFieldByNumber(253); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitLap.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitLap.java index cb23ed9a8..460bef1fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitLap.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitLap.java @@ -20,6 +20,21 @@ public class FitLap extends RecordData { } } + @Nullable + public Integer getEvent() { + return (Integer) getFieldByNumber(0); + } + + @Nullable + public Integer getEventType() { + return (Integer) getFieldByNumber(1); + } + + @Nullable + public Long getStartTime() { + return (Long) getFieldByNumber(2); + } + @Nullable public Double getStartLat() { return (Double) getFieldByNumber(3); @@ -55,6 +70,31 @@ public class FitLap extends RecordData { return (Double) getFieldByNumber(9); } + @Nullable + public Long getTotalCycles() { + return (Long) getFieldByNumber(10); + } + + @Nullable + public Integer getTotalCalores() { + return (Integer) getFieldByNumber(11); + } + + @Nullable + public Integer getAvgHeartRate() { + return (Integer) getFieldByNumber(15); + } + + @Nullable + public Integer getMaxHeartRate() { + return (Integer) getFieldByNumber(16); + } + + @Nullable + public Integer getAvgCadence() { + return (Integer) getFieldByNumber(17); + } + @Nullable public Integer getTotalAscent() { return (Integer) getFieldByNumber(21); @@ -65,6 +105,61 @@ public class FitLap extends RecordData { return (Integer) getFieldByNumber(22); } + @Nullable + public Integer getLapTrigger() { + return (Integer) getFieldByNumber(24); + } + + @Nullable + public Integer getSport() { + return (Integer) getFieldByNumber(25); + } + + @Nullable + public Integer getNumLengths() { + return (Integer) getFieldByNumber(32); + } + + @Nullable + public Integer getFirstLengthIndex() { + return (Integer) getFieldByNumber(35); + } + + @Nullable + public Integer getAvgStrokeDistance() { + return (Integer) getFieldByNumber(37); + } + + @Nullable + public Integer getSwimStyle() { + return (Integer) getFieldByNumber(38); + } + + @Nullable + public Integer getSubSport() { + return (Integer) getFieldByNumber(39); + } + + @Nullable + public Integer getNumActiveLengths() { + return (Integer) getFieldByNumber(40); + } + + @Nullable + public Integer getAvgSwolf() { + return (Integer) getFieldByNumber(73); + } + + @Nullable + public Double getEnhancedAvgSpeed() { + return (Double) getFieldByNumber(110); + } + + @Nullable + public Double getEnhancedMaxSpeed() { + return (Double) getFieldByNumber(111); + } + @Nullable public Long getTimestamp() { return (Long) getFieldByNumber(253); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRecordDataFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRecordDataFactory.java index 583bf6623..50c7dfa9b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRecordDataFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitRecordDataFactory.java @@ -41,6 +41,8 @@ public class FitRecordDataFactory { return new FitWorkout(recordDefinition, recordHeader); case 31: return new FitCourse(recordDefinition, recordHeader); + case 34: + return new FitActivity(recordDefinition, recordHeader); case 49: return new FitFileCreator(recordDefinition, recordHeader); case 55: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSession.java index 7ef307bb7..f51dc1bde 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSession.java @@ -140,6 +140,11 @@ public class FitSession extends RecordData { return (Double) getFieldByNumber(32); } + @Nullable + public Integer getNumLengths() { + return (Integer) getFieldByNumber(33); + } + @Nullable public Double getEndLatitude() { return (Double) getFieldByNumber(38); @@ -150,6 +155,26 @@ public class FitSession extends RecordData { return (Double) getFieldByNumber(39); } + @Nullable + public Float getPoolLength() { + return (Float) getFieldByNumber(44); + } + + @Nullable + public Integer getNumActiveLengths() { + return (Integer) getFieldByNumber(47); + } + + @Nullable + public Float getAvgSwimCadence() { + return (Float) getFieldByNumber(79); + } + + @Nullable + public Integer getAvgSwolf() { + return (Integer) getFieldByNumber(80); + } + @Nullable public String getSportProfileName() { return (String) getFieldByNumber(110); @@ -185,6 +210,11 @@ public class FitSession extends RecordData { return (Float) getFieldByNumber(180); } + @Nullable + public Integer getPrimaryBenefit() { + return (Integer) getFieldByNumber(188); + } + @Nullable public Integer getAvgSpo2() { return (Integer) getFieldByNumber(194); @@ -195,6 +225,11 @@ public class FitSession extends RecordData { return (Integer) getFieldByNumber(195); } + @Nullable + public Integer getRestingCalories() { + return (Integer) getFieldByNumber(196); + } + @Nullable public Integer getHrvSdrr() { return (Integer) getFieldByNumber(197); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b63f7d93..efaff749c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2208,6 +2208,9 @@ Smart alarm interval is interval before of installed alarm. In this interval device is trying to detect lightest phase of sleep to awake user Distance + Pool length + Avg Swim Cadence + strokes/length Uphill Downhill Uphill distance