From 12ecfa0c4e16e60226d59835404fad4d8a0f0759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Wed, 28 Aug 2024 13:43:12 +0100 Subject: [PATCH] Garmin: Parse strength training workout sets --- .../activities/ActivitySummaryDetail.java | 2 +- .../adapter/ActivitySummariesAdapter.java | 4 +- .../devices/AbstractDeviceCoordinator.java | 3 +- .../devices/DeviceCoordinator.java | 4 +- .../cmfwatchpro/CmfWatchProCoordinator.java | 2 +- .../devices/garmin/GarminCoordinator.java | 4 +- .../devices/garmin/GarminWorkoutParser.java | 52 +++++++++++++++++++ .../devices/huami/HuamiCoordinator.java | 2 +- .../huami/zeppos/ZeppOsCoordinator.java | 2 +- .../devices/test/TestDeviceCoordinator.java | 4 +- .../devices/xiaomi/XiaomiCoordinator.java | 3 +- .../model/ActivitySummaryData.java | 16 +++++- .../model/ActivitySummaryEntries.java | 2 + .../model/ActivitySummaryJsonSummary.java | 4 ++ .../devices/garmin/fit/FitImporter.java | 3 +- .../devices/garmin/fit/GlobalFITMessage.java | 5 +- .../devices/garmin/fit/messages/FitSet.java | 19 ++++++- .../fetch/FetchSportsSummaryOperation.java | 2 +- .../gadgetbridge/util/GBPrefs.java | 6 +++ app/src/main/res/values/strings.xml | 5 ++ 20 files changed, 121 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummaryDetail.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummaryDetail.java index 70ddef0fe..7ef8c3230 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummaryDetail.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummaryDetail.java @@ -422,7 +422,7 @@ public class ActivitySummaryDetail extends AbstractGBActivity { private void makeSummaryContent(BaseActivitySummary item) { final DeviceCoordinator coordinator = gbDevice.getDeviceCoordinator(); - final ActivitySummaryParser summaryParser = coordinator.getActivitySummaryParser(gbDevice); + final ActivitySummaryParser summaryParser = coordinator.getActivitySummaryParser(gbDevice, this); //make view of data from summaryData of item String units = GBApplication.getPrefs().getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, GBApplication.getContext().getString(R.string.p_unit_metric)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java index a66465faf..6b391446e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java @@ -40,7 +40,6 @@ import java.util.concurrent.TimeUnit; import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; @@ -52,7 +51,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryJsonSummary; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; -import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.FormatUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -205,7 +203,7 @@ public class ActivitySummariesAdapter extends AbstractActivityListingAdapter timesInZone = new ArrayList<>(); private final List activityPoints = new ArrayList<>(); private FitSession session = null; private FitSport sport = null; private FitPhysiologicalMetrics physiologicalMetrics = null; + private final List sets = new ArrayList<>(); + + public GarminWorkoutParser(final Context context) { + this.context = context; + } @Override public BaseActivitySummary parseBinaryData(final BaseActivitySummary summary, final boolean forDetails) { @@ -84,6 +98,7 @@ public class GarminWorkoutParser implements ActivitySummaryParser { session = null; sport = null; physiologicalMetrics = null; + sets.clear(); } public boolean handleRecord(final RecordData record) { @@ -111,6 +126,9 @@ public class GarminWorkoutParser implements ActivitySummaryParser { } else if (record instanceof FitTimeInZone) { LOG.trace("Time in zone: {}", record); timesInZone.add((FitTimeInZone) record); + } else if (record instanceof FitSet) { + LOG.trace("Set: {}", record); + sets.add((FitSet) record); } else { return false; } @@ -198,6 +216,40 @@ public class GarminWorkoutParser implements ActivitySummaryParser { } } + if (!sets.isEmpty()) { + final boolean isMetric = GBApplication.getPrefs().isMetricUnits(); + + int i = 1; + for (final FitSet set : sets) { + if (set.getSetType() != null && set.getDuration() != null && set.getSetType() == 1) { + final StringBuilder sb = new StringBuilder(); + + if (set.getRepetitions() != null) { + if (set.getWeight() != null) { + if (isMetric) { + sb.append(context.getString(R.string.workout_set_repetitions_weight_kg, set.getRepetitions(), set.getWeight())); + } else { + sb.append(context.getString(R.string.workout_set_repetitions_weight_lbs, set.getRepetitions(), set.getWeight() * 2.2046226f)); + } + } else { + sb.append(context.getString(R.string.workout_set_repetitions, set.getRepetitions())); + } + + sb.append(", "); + } + + sb.append(DateTimeUtils.formatDurationHoursMinutes(set.getDuration().longValue(), TimeUnit.SECONDS)); + + summaryData.add( + SETS, + context.getString(R.string.workout_set_i, i), + sb.toString() + ); + i++; + } + } + } + summary.setSummaryData(summaryData.toString()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java index 77bf357b7..4efc852f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java @@ -181,7 +181,7 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator { } @Override - public ActivitySummaryParser getActivitySummaryParser(final GBDevice device) { + public ActivitySummaryParser getActivitySummaryParser(final GBDevice device, final Context context) { return new HuamiActivitySummaryParser(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java index 83dd0e407..8c149bfcc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java @@ -296,7 +296,7 @@ public abstract class ZeppOsCoordinator extends HuamiCoordinator { } @Override - public ActivitySummaryParser getActivitySummaryParser(final GBDevice device) { + public ActivitySummaryParser getActivitySummaryParser(final GBDevice device, final Context context) { return new ZeppOsActivitySummaryParser(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java index 8d3945d23..e8106c691 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java @@ -182,8 +182,8 @@ public class TestDeviceCoordinator extends AbstractDeviceCoordinator { @Nullable @Override - public ActivitySummaryParser getActivitySummaryParser(final GBDevice device) { - return supportsActivityTracks() ? new TestActivitySummaryParser() : super.getActivitySummaryParser(device); + public ActivitySummaryParser getActivitySummaryParser(final GBDevice device, final Context context) { + return supportsActivityTracks() ? new TestActivitySummaryParser() : super.getActivitySummaryParser(device, context); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java index 254078106..67eaac831 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java @@ -20,6 +20,7 @@ import static nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.Xiaomi import android.app.Activity; import android.bluetooth.le.ScanFilter; +import android.content.Context; import android.os.ParcelUuid; import androidx.annotation.NonNull; @@ -167,7 +168,7 @@ public abstract class XiaomiCoordinator extends AbstractBLEDeviceCoordinator { @Nullable @Override - public ActivitySummaryParser getActivitySummaryParser(final GBDevice device) { + public ActivitySummaryParser getActivitySummaryParser(final GBDevice device, final Context context) { return new WorkoutSummaryParser(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryData.java index d73eebb65..970219db8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryData.java @@ -31,9 +31,20 @@ public class ActivitySummaryData extends JSONObject { private static final Logger LOG = LoggerFactory.getLogger(FitImporter.class); public void add(final String key, final float value, final String unit) { + add(null, key, value, unit); + } + + public void add(final String key, final String value) { + add(null, key, value); + } + + public void add(final String group, final String key, final float value, final String unit) { if (value > 0) { try { final JSONObject innerData = new JSONObject(); + if (group != null) { + innerData.put("group", group); + } innerData.put("value", value); innerData.put("unit", unit); put(key, innerData); @@ -43,10 +54,13 @@ public class ActivitySummaryData extends JSONObject { } } - public void add(final String key, final String value) { + public void add(final String group, final String key, final String value) { if (key != null && !key.isEmpty() && value != null && !value.isEmpty()) { try { final JSONObject innerData = new JSONObject(); + if (group != null) { + innerData.put("group", group); + } innerData.put("value", value); innerData.put("unit", "string"); put(key, innerData); 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 002b77f10..cff10357d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryEntries.java @@ -112,6 +112,8 @@ public class ActivitySummaryEntries { public static final String CYCLING_POWER_MIN = "cyclingPowerMin"; public static final String CYCLING_POWER_MAX = "cyclingPowerMax"; + public static final String SETS = "workoutSets"; + public static final String UNIT_BPM = "bpm"; public static final String UNIT_CM = "cm"; public static final String UNIT_UNIX_EPOCH_SECONDS = "unix_epoch_seconds"; 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 afe2960ea..321d2b3dd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryJsonSummary.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummaryJsonSummary.java @@ -193,6 +193,8 @@ public class ActivitySummaryJsonSummary { } return defaultGroup; } + + /** @noinspection ArraysAsListWithZeroOrOneArgument*/ private JSONObject createActivitySummaryGroups(){ final Map> groupDefinitions = new LinkedHashMap>() {{ // NB: Default group Activity must be present in this definition, otherwise it wouldn't @@ -237,6 +239,8 @@ public class ActivitySummaryJsonSummary { FORE_FOOT_LANDINGS, MID_FOOT_LANDINGS, BACK_FOOT_LANDINGS, EVERSION_ANGLE_AVG, EVERSION_ANGLE_MAX )); + put(SETS, Arrays.asList( + )); }}; return new JSONObject(groupDefinitions); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java index b0c56539e..da3d15548 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitImporter.java @@ -81,11 +81,12 @@ public class FitImporter { private final Map unknownRecords = new HashMap<>(); private FitFileId fileId = null; - private final GarminWorkoutParser workoutParser = new GarminWorkoutParser(); + private final GarminWorkoutParser workoutParser; public FitImporter(final Context context, final GBDevice gbDevice) { this.context = context; this.gbDevice = gbDevice; + this.workoutParser = new GarminWorkoutParser(context); } /** @noinspection StatementWithEmptyBody*/ 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 abc197a36..c387ac510 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 @@ -259,9 +259,12 @@ public class GlobalFITMessage { )); public static GlobalFITMessage SET = new GlobalFITMessage(225, "SET", Arrays.asList( - new FieldDefinitionPrimitive(0, BaseType.UINT32, "duration"), + new FieldDefinitionPrimitive(0, BaseType.UINT32, "duration", 1000, 0), // seconds + new FieldDefinitionPrimitive(3, BaseType.UINT16, "repetitions"), + new FieldDefinitionPrimitive(4, BaseType.UINT16, "weight", 16, 0), // kg new FieldDefinitionPrimitive(5, BaseType.UINT8, "set_type"), // 1 active 0 rest new FieldDefinitionPrimitive(6, BaseType.UINT32, "start_time", FieldDefinitionFactory.FIELD.TIMESTAMP), + new FieldDefinitionPrimitive(7, BaseType.UINT16, "category"), new FieldDefinitionPrimitive(10, BaseType.UINT16, "message_index"), new FieldDefinitionPrimitive(254, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP) )); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSet.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSet.java index cb254b282..255793f70 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSet.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitSet.java @@ -21,8 +21,18 @@ public class FitSet extends RecordData { } @Nullable - public Long getDuration() { - return (Long) getFieldByNumber(0); + public Double getDuration() { + return (Double) getFieldByNumber(0); + } + + @Nullable + public Integer getRepetitions() { + return (Integer) getFieldByNumber(3); + } + + @Nullable + public Float getWeight() { + return (Float) getFieldByNumber(4); } @Nullable @@ -35,6 +45,11 @@ public class FitSet extends RecordData { return (Long) getFieldByNumber(6); } + @Nullable + public Integer getCategory() { + return (Integer) getFieldByNumber(7); + } + @Nullable public Integer getMessageIndex() { return (Integer) getFieldByNumber(10); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSportsSummaryOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSportsSummaryOperation.java index 4204f1f8f..4617930fb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSportsSummaryOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/fetch/FetchSportsSummaryOperation.java @@ -75,7 +75,7 @@ public class FetchSportsSummaryOperation extends AbstractFetchOperation { } final DeviceCoordinator coordinator = getDevice().getDeviceCoordinator(); - final ActivitySummaryParser summaryParser = coordinator.getActivitySummaryParser(getDevice()); + final ActivitySummaryParser summaryParser = coordinator.getActivitySummaryParser(getDevice(), getContext()); BaseActivitySummary summary = new BaseActivitySummary(); summary.setStartTime(getLastStartTimestamp().getTime()); // due to a bug this has to be set diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java index 2e4bf8cce..71ff3989b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java @@ -36,6 +36,8 @@ import java.util.Date; import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -184,4 +186,8 @@ public class GBPrefs extends Prefs { public LocalTime getNotificationTimesEnd() { return getLocalTime("notification_times_end", "22:00"); } + + public boolean isMetricUnits() { + return getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, "metric").equals("metric"); + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 31020e8ab..ec1e2d1e4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2180,6 +2180,11 @@ Average cycling power Min cycling power Max cycling power + Sets + Set %1d + %1d x + %1d x %2$.2f kg + %1d x %2$.2f lbs m cm