mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Garmin: Swimming workout intervals
This commit is contained in:
+55
-45
@@ -14,16 +14,18 @@ import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryProgressEntry;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryTableRowEntry;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryTableBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryValue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitFile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||
@@ -211,6 +213,10 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
summaryData.add(STEP_LENGTH_AVG, session.getAvgStepLength(), UNIT_MM);
|
||||
if (session.getTotalCalories() != null) {
|
||||
summaryData.add(CALORIES_BURNT, session.getTotalCalories(), UNIT_KCAL);
|
||||
if (session.getRestingCalories() != null) {
|
||||
summaryData.add(CALORIES_ACTIVE, session.getTotalCalories() - session.getRestingCalories(), UNIT_KCAL);
|
||||
summaryData.add(CALORIES_RESTING, session.getRestingCalories(), UNIT_KCAL);
|
||||
}
|
||||
}
|
||||
if (session.getEstimatedSweatLoss() != null) {
|
||||
summaryData.add(ESTIMATED_SWEAT_LOSS, session.getEstimatedSweatLoss(), UNIT_ML);
|
||||
@@ -305,7 +311,7 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
context.getString(
|
||||
R.string.range_percentage_float,
|
||||
session.getAvgStanceTimeBalance(),
|
||||
100f - session.getAvgStanceTimeBalance()
|
||||
100f - session.getAvgStanceTimeBalance()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -505,61 +511,65 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
summaryData.add(TRAINING_STRESS_SCORE, session.getTrainingStressScore(), UNIT_NONE);
|
||||
|
||||
if (!sets.isEmpty()) {
|
||||
final boolean anyReps = sets.stream().anyMatch(s -> s.getRepetitions() != null);
|
||||
final boolean anyWeight = sets.stream().anyMatch(s -> s.getWeight() != null);
|
||||
|
||||
final List<ActivitySummaryValue> header = new LinkedList<>();
|
||||
header.add(new ActivitySummaryValue("set"));
|
||||
header.add(new ActivitySummaryValue("workout_set_reps"));
|
||||
header.add(new ActivitySummaryValue("menuitem_weight"));
|
||||
header.add(new ActivitySummaryValue("activity_detail_duration_label"));
|
||||
|
||||
summaryData.add(
|
||||
"sets_header",
|
||||
new ActivitySummaryTableRowEntry(
|
||||
SETS,
|
||||
header,
|
||||
true,
|
||||
true
|
||||
)
|
||||
);
|
||||
final ActivitySummaryTableBuilder tableBuilder = new ActivitySummaryTableBuilder(SETS, "sets_header", Arrays.asList(
|
||||
"set",
|
||||
"workout_set_reps",
|
||||
"menuitem_weight",
|
||||
"activity_detail_duration_label"
|
||||
));
|
||||
|
||||
int i = 1;
|
||||
for (final FitSet set : sets) {
|
||||
if (set.getSetType() != null && set.getDuration() != null && set.getSetType() == 1) {
|
||||
final List<ActivitySummaryValue> columns = new LinkedList<>();
|
||||
columns.add(new ActivitySummaryValue(i, UNIT_NONE));
|
||||
|
||||
if (set.getRepetitions() != null) {
|
||||
columns.add(new ActivitySummaryValue(String.valueOf(set.getRepetitions())));
|
||||
} else {
|
||||
columns.add(new ActivitySummaryValue("stats_empty_value"));
|
||||
}
|
||||
|
||||
if (set.getWeight() != null) {
|
||||
columns.add(new ActivitySummaryValue(set.getWeight(), weightUnit));
|
||||
} else {
|
||||
columns.add(new ActivitySummaryValue("stats_empty_value"));
|
||||
}
|
||||
|
||||
columns.add(new ActivitySummaryValue(set.getDuration().longValue(), UNIT_SECONDS));
|
||||
|
||||
summaryData.add(
|
||||
tableBuilder.addRow(
|
||||
"set_" + i,
|
||||
new ActivitySummaryTableRowEntry(
|
||||
SETS,
|
||||
columns,
|
||||
false,
|
||||
true
|
||||
Arrays.asList(
|
||||
new ActivitySummaryValue(i, UNIT_NONE),
|
||||
new ActivitySummaryValue(set.getRepetitions() != null ? String.valueOf(set.getRepetitions()) : null),
|
||||
new ActivitySummaryValue(set.getWeight(), weightUnit),
|
||||
new ActivitySummaryValue(set.getDuration().longValue(), UNIT_SECONDS)
|
||||
)
|
||||
);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
tableBuilder.addToSummaryData(summaryData);
|
||||
}
|
||||
|
||||
if (!laps.isEmpty()) {
|
||||
final boolean anySwolf = laps.stream().anyMatch(l -> l.getAvgSwolf() != null);
|
||||
// FIXME: For now we only support swimming intervals
|
||||
final boolean anyValidLaps = laps.stream()
|
||||
.anyMatch(lap -> lap.getTotalDistance() != null && lap.getTotalDistance() != 0 && lap.getSwimStyle() != null);
|
||||
|
||||
if (anyValidLaps) {
|
||||
final ActivitySummaryTableBuilder tableBuilder = new ActivitySummaryTableBuilder(GROUP_INTERVALS, "intervals_header", Arrays.asList(
|
||||
"#",
|
||||
"swimming_stroke",
|
||||
"Distance",
|
||||
"pref_header_time"
|
||||
));
|
||||
|
||||
int i = 1;
|
||||
for (final FitLap lap : laps) {
|
||||
if (lap.getTotalDistance() == null || lap.getTotalDistance() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tableBuilder.addRow(
|
||||
"interval_" + i,
|
||||
Arrays.asList(
|
||||
new ActivitySummaryValue(i, UNIT_NONE),
|
||||
new ActivitySummaryValue(lap.getSwimStyle() != null ? context.getString(lap.getSwimStyle().getNameResId()) : null, UNIT_NONE),
|
||||
new ActivitySummaryValue(lap.getTotalDistance(), UNIT_METERS),
|
||||
new ActivitySummaryValue(lap.getTotalTimerTime(), UNIT_SECONDS)
|
||||
)
|
||||
);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
tableBuilder.addToSummaryData(summaryData);
|
||||
}
|
||||
|
||||
summaryData.add(
|
||||
|
||||
@@ -446,13 +446,24 @@ public enum ActivityKind {
|
||||
}
|
||||
|
||||
public enum CycleUnit {
|
||||
NONE,
|
||||
UNKNOWN,
|
||||
STEPS,
|
||||
STROKES,
|
||||
JUMPS,
|
||||
REPS,
|
||||
REVOLUTIONS,
|
||||
SWINGS,
|
||||
NONE("n_a"),
|
||||
UNKNOWN("unknown"),
|
||||
STEPS("steps"),
|
||||
STROKES("strokes"),
|
||||
JUMPS("Jumps"),
|
||||
REPS("workout_set_reps"),
|
||||
REVOLUTIONS("workout_revolutions"),
|
||||
SWINGS("workout_swings"),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
|
||||
CycleUnit(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -156,6 +156,8 @@ public class ActivitySummaryEntries {
|
||||
public static final String SWIM_AVG_CADENCE = "swim_avg_cadence";
|
||||
|
||||
public static final String CALORIES_BURNT = "caloriesBurnt";
|
||||
public static final String CALORIES_ACTIVE = "active_calories";
|
||||
public static final String CALORIES_RESTING = "restingCalories";
|
||||
public static final String TRAINING_EFFECT_AEROBIC = "aerobicTrainingEffect";
|
||||
public static final String TRAINING_EFFECT_ANAEROBIC = "anaerobicTrainingEffect";
|
||||
public static final String WORKOUT_LOAD = "currentWorkoutLoad";
|
||||
@@ -226,6 +228,7 @@ public class ActivitySummaryEntries {
|
||||
public static final String GROUP_TRAINING_EFFECT = "TrainingEffect";
|
||||
public static final String GROUP_LAPS = "laps";
|
||||
public static final String GROUP_RUNNING_FORM = "RunningForm";
|
||||
public static final String GROUP_INTERVALS = "workout_intervals";
|
||||
|
||||
/**
|
||||
* Used to signal that this activity has a gps track. This is currently used by ActivitySummaryDetail
|
||||
|
||||
+5
-1
@@ -14,6 +14,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefi
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionLanguage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSwimStyle;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTemperature;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTimestamp;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherAqi;
|
||||
@@ -59,6 +60,8 @@ public class FieldDefinitionFactory {
|
||||
return new FieldDefinitionWeatherAqi(localNumber, size, baseType, name);
|
||||
case COORDINATE:
|
||||
return new FieldDefinitionCoordinate(localNumber, size, baseType, name);
|
||||
case SWIM_STYLE:
|
||||
return new FieldDefinitionSwimStyle(localNumber, size, baseType, name);
|
||||
default:
|
||||
return new FieldDefinition(localNumber, size, baseType, name);
|
||||
}
|
||||
@@ -81,6 +84,7 @@ public class FieldDefinitionFactory {
|
||||
LANGUAGE,
|
||||
SLEEP_STAGE,
|
||||
WEATHER_AQI,
|
||||
COORDINATE
|
||||
COORDINATE,
|
||||
SWIM_STYLE,
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ public class GlobalFITMessage {
|
||||
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(38, BaseType.ENUM, "swim_style", FieldDefinitionFactory.FIELD.SWIM_STYLE),
|
||||
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"),
|
||||
|
||||
+3
@@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefi
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionLanguage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionMeasurementSystem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSwimStyle;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherAqi;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherCondition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
@@ -274,6 +275,8 @@ public class FitCodeGen {
|
||||
return FieldDefinitionWeatherAqi.AQI_LEVELS.class;
|
||||
case COORDINATE:
|
||||
return Double.class;
|
||||
case SWIM_STYLE:
|
||||
return FieldDefinitionSwimStyle.SwimStyle.class;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unknown field type " + primitive.getType());
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
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 FieldDefinitionSwimStyle extends FieldDefinition {
|
||||
|
||||
public FieldDefinitionSwimStyle(int localNumber, int size, BaseType baseType, String name) {
|
||||
super(localNumber, size, baseType, name, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(ByteBuffer byteBuffer) {
|
||||
int raw = (int) baseType.decode(byteBuffer, scale, offset);
|
||||
return SwimStyle.fromId(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuffer byteBuffer, Object o) {
|
||||
if (o instanceof SwimStyle) {
|
||||
baseType.encode(byteBuffer, (((SwimStyle) o).getId()), scale, offset);
|
||||
return;
|
||||
}
|
||||
baseType.encode(byteBuffer, o, scale, offset);
|
||||
}
|
||||
|
||||
public enum SwimStyle {
|
||||
FREESTYLE(0, R.string.freestyle),
|
||||
BACKSTROKE(1, R.string.backstroke),
|
||||
BREASTSTROKE(2, R.string.breaststroke),
|
||||
BUTTERFLY(3, R.string.swim_style_butterfly),
|
||||
DRILL(4, R.string.swim_style_drill),
|
||||
MIXED(5, R.string.swim_style_mixed),
|
||||
;
|
||||
|
||||
private final int id;
|
||||
private final int nameResId;
|
||||
|
||||
SwimStyle(int i, int nameResId) {
|
||||
this.id = i;
|
||||
this.nameResId = nameResId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SwimStyle fromId(int id) {
|
||||
for (SwimStyle language : SwimStyle.values()) {
|
||||
if (id == language.getId()) {
|
||||
return language;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
public int getNameResId() {
|
||||
return nameResId;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -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.FieldDefinitionSwimStyle.SwimStyle;
|
||||
|
||||
//
|
||||
// WARNING: This class was auto-generated, please avoid modifying it directly.
|
||||
@@ -131,8 +132,8 @@ public class FitLap extends RecordData {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getSwimStyle() {
|
||||
return (Integer) getFieldByNumber(38);
|
||||
public SwimStyle getSwimStyle() {
|
||||
return (SwimStyle) getFieldByNumber(38);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -2389,6 +2389,7 @@
|
||||
<string name="distance_total">Distance Total</string>
|
||||
<string name="activeSeconds">Active</string>
|
||||
<string name="caloriesBurnt">Calories</string>
|
||||
<string name="restingCalories">Resting Calories</string>
|
||||
<string name="maxSpeed">Maximum</string>
|
||||
<string name="minSpeed">Minimum</string>
|
||||
<string name="minPace">Slowest Pace</string>
|
||||
@@ -2535,6 +2536,7 @@
|
||||
<string name="ground_contact_time_balance">Ground Contact Time Balance</string>
|
||||
<string name="workout_repetitions">Repetitions</string>
|
||||
<string name="workout_revolutions">Revolutions</string>
|
||||
<string name="workout_swings">Swings</string>
|
||||
<string name="seconds">sec</string>
|
||||
<string name="milliseconds">milliseconds</string>
|
||||
<string name="milliseconds_ms">ms</string>
|
||||
@@ -2553,6 +2555,7 @@
|
||||
<string name="Pace">Pace</string>
|
||||
<!-- activity summary groups-->
|
||||
<string name="Strokes">Strokes</string>
|
||||
<string name="swimming_stroke">Stroke</string>
|
||||
<string name="Jumps">Jumps</string>
|
||||
<string name="Swimming">Swimming</string>
|
||||
<string name="Distance">Distance</string>
|
||||
@@ -2615,6 +2618,10 @@
|
||||
<string name="freestyle">Freestyle</string>
|
||||
<string name="backstroke">Backstroke</string>
|
||||
<string name="medley">Medley</string>
|
||||
<string name="swim_style_butterfly">Butterfly</string>
|
||||
<string name="swim_style_drill">Drill</string>
|
||||
<string name="swim_style_mixed">Mixed</string>
|
||||
<string name="workout_intervals">Intervals</string>
|
||||
<string name="devicetype_nut_mini">Nut mini</string>
|
||||
<string name="qhybrid_calibration_align_hint">Use the buttons below to align the watch hands to 12:00.</string>
|
||||
<string name="lorem_ipsum" translatable="false">Lorem Ipsum</string>
|
||||
|
||||
Reference in New Issue
Block a user