decode and display exercise categories in garmin watches

This commit is contained in:
punchdeerflyscorpion
2025-07-19 20:17:37 +02:00
parent 0b761558bd
commit eca17af9fb
7 changed files with 153 additions and 3 deletions
@@ -31,6 +31,7 @@ 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.exception.FitParseException;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionExerciseCategory.ExerciseCategory;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitLap;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitPhysiologicalMetrics;
@@ -516,6 +517,7 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
if (!sets.isEmpty()) {
final ActivitySummaryTableBuilder tableBuilder = new ActivitySummaryTableBuilder(SETS, "sets_header", Arrays.asList(
"set",
"category",
"workout_set_reps",
"menuitem_weight",
"activity_detail_duration_label"
@@ -524,10 +526,16 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
int i = 1;
for (final FitSet set : sets) {
if (set.getSetType() != null && set.getDuration() != null && set.getSetType() == 1) {
ExerciseCategory category = null;
if (set.getCategory() != null && set.getCategory().length > 0) {
category = set.getCategory()[0];
}
tableBuilder.addRow(
"set_" + i,
Arrays.asList(
new ActivitySummaryValue(i, UNIT_NONE),
new ActivitySummaryValue(category != null ? context.getString(category.getNameResId()) : null, UNIT_NONE),
new ActivitySummaryValue(set.getRepetitions() != null ? String.valueOf(set.getRepetitions()) : null),
new ActivitySummaryValue(set.getWeight(), weightUnit),
new ActivitySummaryValue(set.getDuration().longValue(), UNIT_SECONDS)
@@ -5,6 +5,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefi
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionArray;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionCoordinate;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionDayOfWeek;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionExerciseCategory;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionFileType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalSource;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalType;
@@ -32,6 +33,8 @@ public class FieldDefinitionFactory {
return new FieldDefinitionArray(localNumber, size, baseType, name, scale, offset);
case DAY_OF_WEEK:
return new FieldDefinitionDayOfWeek(localNumber, size, baseType, name);
case EXERCISE_CATEGORY:
return new FieldDefinitionExerciseCategory(localNumber, size, baseType, name);
case FILE_TYPE:
return new FieldDefinitionFileType(localNumber, size, baseType, name);
case GOAL_SOURCE:
@@ -71,6 +74,7 @@ public class FieldDefinitionFactory {
ALARM,
ARRAY,
DAY_OF_WEEK,
EXERCISE_CATEGORY,
FILE_TYPE,
GOAL_SOURCE,
GOAL_TYPE,
@@ -400,7 +400,7 @@ public class GlobalFITMessage {
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(7, BaseType.UINT16, "category", FieldDefinitionFactory.FIELD.EXERCISE_CATEGORY),
new FieldDefinitionPrimitive(10, BaseType.UINT16, "message_index"),
new FieldDefinitionPrimitive(254, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
));
@@ -27,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalFIT
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;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionExerciseCategory;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalSource;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionGoalType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionHrvStatus;
@@ -249,6 +250,8 @@ public class FitCodeGen {
return Number[].class;
case DAY_OF_WEEK:
return DayOfWeek.class;
case EXERCISE_CATEGORY:
return FieldDefinitionExerciseCategory.ExerciseCategory[].class;
case FILE_TYPE:
return FileType.FILETYPE.class;
case GOAL_SOURCE:
@@ -0,0 +1,102 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
public class FieldDefinitionExerciseCategory extends FieldDefinition {
public FieldDefinitionExerciseCategory(int localNumber, int size, BaseType baseType, String name) {
super(localNumber, size, baseType, name, 1, 0);
}
@Override
public Object decode(ByteBuffer byteBuffer) {
final Object rawObj = baseType.decode(byteBuffer, scale, offset);
if (rawObj != null) {
final int raw = (int) rawObj;
return ExerciseCategory.fromId(raw);
}
return null;
}
@Override
public void encode(ByteBuffer byteBuffer, Object o) {
if (o instanceof ExerciseCategory) {
baseType.encode(byteBuffer, (((ExerciseCategory) o).getId()), scale, offset);
return;
}
baseType.encode(byteBuffer, o, scale, offset);
}
public enum ExerciseCategory {
// see https://github.com/dtcooper/python-fitparse/blob/master/fitparse/profile.py
CATEGORY_BENCH_PRESS(0, R.string.exercise_category_bench_press),
CATEGORY_CALF_RAISE(1, R.string.exercise_category_calf_raise),
CATEGORY_CARDIO(2, R.string.activity_type_cardio),
CATEGORY_CARRY(3, R.string.exercise_category_carry),
CATEGORY_CHOP(4, R.string.exercise_category_chop),
CATEGORY_CORE(5, R.string.exercise_category_core),
CATEGORY_CRUNCH(6, R.string.exercise_category_crunch),
CATEGORY_CURL(7, R.string.activity_type_curling),
CATEGORY_DEADLIFT(8, R.string.activity_type_deadlift),
CATEGORY_FLYE(9, R.string.exercise_category_flye),
CATEGORY_HIP_RAISE(10, R.string.exercise_category_hip_raise),
CATEGORY_HIP_STABILITY(11, R.string.exercise_category_hip_stability),
CATEGORY_HIP_SWING(12, R.string.exercise_category_hip_swing),
CATEGORY_HYPEREXTENSION(13, R.string.exercise_category_hyperextension),
CATEGORY_LATERAL_RAISE(14, R.string.exercise_category_lateral_raise),
CATEGORY_LEG_CURL(15, R.string.exercise_category_leg_curl),
CATEGORY_LEG_RAISE(16, R.string.exercise_category_leg_raise),
CATEGORY_LUNGE(17, R.string.exercise_category_lunge),
CATEGORY_OLYMPIC_LIFT(18, R.string.exercise_category_olympic_lift),
CATEGORY_PLANK(19, R.string.activity_type_plank),
CATEGORY_PLYO(20, R.string.exercise_category_plyo),
CATEGORY_PULL_UP(21, R.string.activity_type_pull_ups),
CATEGORY_PUSH_UP(22, R.string.activity_type_push_ups),
CATEGORY_ROW(23, R.string.activity_type_rowing),
CATEGORY_SHOULDER_PRESS(24, R.string.exercise_category_shoulder_press),
CATEGORY_SHOULDER_STABILITY(25, R.string.exercise_category_shoulder_stability),
CATEGORY_SHRUG(26, R.string.exercise_category_shrug),
CATEGORY_SIT_UP(27, R.string.activity_type_sit_ups),
CATEGORY_SQUAT(28, R.string.exercise_category_squat),
CATEGORY_TOTAL_BODY(29, R.string.exercise_category_total_body),
CATEGORY_TRICEPS_EXTENSION(30, R.string.exercise_category_triceps_extension),
CATEGORY_WARM_UP(31, R.string.hrZoneWarmUp),
CATEGORY_RUN(32, R.string.activity_type_running),
CATEGORY_UNKNOWN(250, R.string.unknown)
;
private final int id;
private final int nameResId;
ExerciseCategory(int i, int nameResId) {
this.id = i;
this.nameResId = nameResId;
}
@Nullable
public static ExerciseCategory fromId(int id) {
for (ExerciseCategory language : ExerciseCategory.values()) {
if (id == language.getId()) {
return language;
}
}
return CATEGORY_UNKNOWN;
}
public int getId() {
return id;
}
@StringRes
public int getNameResId() {
return nameResId;
}
}
}
@@ -5,6 +5,7 @@ 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;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionExerciseCategory.ExerciseCategory;
//
// WARNING: This class was auto-generated, please avoid modifying it directly.
@@ -46,8 +47,15 @@ public class FitSet extends RecordData {
}
@Nullable
public Integer getCategory() {
return (Integer) getFieldByNumber(7);
public ExerciseCategory[] getCategory() {
final Object[] objectsArray = (Object[]) getFieldByNumber(7);
if (objectsArray == null)
return null;
final ExerciseCategory[] ret = new ExerciseCategory[objectsArray.length];
for (int i = 0; i < objectsArray.length; i++) {
ret[i] = (ExerciseCategory) objectsArray[i];
}
return ret;
}
@Nullable
+25
View File
@@ -2722,6 +2722,31 @@
<string name="swim_style_butterfly">Butterfly</string>
<string name="swim_style_drill">Drill</string>
<string name="swim_style_mixed">Mixed</string>
<string name="exercise_category_bench_press">Bench press</string>
<string name="exercise_category_calf_raise">Calf raise</string>
<string name="exercise_category_carry">Carry</string>
<string name="exercise_category_chop">Chop</string>
<string name="exercise_category_core">Core</string>
<string name="exercise_category_crunch">Crunch</string>
<string name="exercise_category_flye">Flye</string>
<string name="exercise_category_hip_raise">Hip raise</string>
<string name="exercise_category_hip_stability">Hip stability</string>
<string name="exercise_category_hip_swing">Hip swing</string>
<string name="exercise_category_hyperextension">Hyperextension</string>
<string name="exercise_category_lateral_raise">Lateral raise</string>
<string name="exercise_category_leg_curl">Leg curl</string>
<string name="exercise_category_leg_raise">Leg raise</string>
<string name="exercise_category_lunge">Lunge</string>
<string name="exercise_category_olympic_lift">Olympic lift</string>
<string name="exercise_category_plyo">Plyo</string>
<string name="exercise_category_shoulder_press">Shoulder press</string>
<string name="exercise_category_shoulder_stability">Shoulder stability</string>
<string name="exercise_category_shrug">Shrug</string>
<string name="exercise_category_squat">Squat</string>
<string name="exercise_category_total_body">Total body</string>
<string name="exercise_category_triceps_extension">Triceps extension</string>
<string name="category">Category</string>
<string name="workout_intervals">Intervals</string>
<!-- Diving params -->
<string name="diving_maximum_diving_depth">Maximum Diving Depth</string>