mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Merge pull request 'ActivityPoint update and Garmin parsing' (#6067)
Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/6067
This commit is contained in:
+45
-16
@@ -1,7 +1,25 @@
|
||||
/* Copyright (C) 2024-2026 José Rebelo, Thomas Kuehne
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.workouts;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.DISTANCE_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_CM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_EPOC_TIME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_FOOT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_FOOT_PER_HOUR;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KG;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KILOMETERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KMPH;
|
||||
@@ -10,15 +28,21 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS_PER_HOUR;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS_PER_SECOND;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MILE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MILE_PER_HOUR;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MINUTES_PER_100_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MINUTES_PER_100_YARDS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MINUTES_PER_KM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MINUTES_PER_MILE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_NAUTICAL_MILES;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_RAW_STRING;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_100_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_100_YARDS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_KM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_M;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_SPORT;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -113,46 +137,46 @@ public class WorkoutValueFormatter {
|
||||
case UNIT_CM:
|
||||
if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value * 0.0328084;
|
||||
unit = "ft";
|
||||
unit = UNIT_FOOT;
|
||||
}
|
||||
break;
|
||||
case UNIT_METERS_PER_SECOND:
|
||||
if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value * 2.236936D;
|
||||
unit = "mi_h";
|
||||
unit = UNIT_MILE_PER_HOUR;
|
||||
} else { //metric
|
||||
value = value * 3.6;
|
||||
unit = "km_h";
|
||||
unit = UNIT_KMPH;
|
||||
}
|
||||
break;
|
||||
case UNIT_METERS_PER_HOUR:
|
||||
if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value * 3.28084D;
|
||||
unit = "foot_per_hour";
|
||||
unit = UNIT_FOOT_PER_HOUR;
|
||||
}
|
||||
break;
|
||||
case UNIT_SECONDS_PER_M:
|
||||
if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value * (1609.344 / 60D);
|
||||
unit = "minutes_mi";
|
||||
unit = UNIT_MINUTES_PER_MILE;
|
||||
} else { //metric
|
||||
value = value * (1000 / 60D);
|
||||
unit = "minutes_km";
|
||||
unit = UNIT_MINUTES_PER_KM;
|
||||
}
|
||||
break;
|
||||
case UNIT_SECONDS_PER_KM:
|
||||
if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value / 60D * 1.609344;
|
||||
unit = "minutes_mi";
|
||||
unit = UNIT_MINUTES_PER_MILE;
|
||||
} else { //metric
|
||||
value = value / 60D;
|
||||
unit = "minutes_km";
|
||||
unit = UNIT_MINUTES_PER_KM;
|
||||
}
|
||||
break;
|
||||
case UNIT_KILOMETERS:
|
||||
if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value * 0.621371D;
|
||||
unit = "mi";
|
||||
unit = UNIT_MILE;
|
||||
}
|
||||
break;
|
||||
case UNIT_METERS:
|
||||
@@ -161,15 +185,15 @@ public class WorkoutValueFormatter {
|
||||
unit = UNIT_NAUTICAL_MILES;
|
||||
} else if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value * 3.28084D;
|
||||
unit = "ft";
|
||||
unit = UNIT_FOOT;
|
||||
if (value > 6000) {
|
||||
value = value * 0.0001893939D;
|
||||
unit = "mi";
|
||||
unit = UNIT_MILE;
|
||||
}
|
||||
} else { //metric
|
||||
if (value > 2000) {
|
||||
value = value / 1000;
|
||||
unit = "km";
|
||||
unit = UNIT_KILOMETERS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -179,7 +203,7 @@ public class WorkoutValueFormatter {
|
||||
unit = UNIT_KNOTS;
|
||||
} else if (distanceUnit == DistanceUnit.IMPERIAL) {
|
||||
value = value * 0.621371D;
|
||||
unit = "mi_h";
|
||||
unit = UNIT_MILE_PER_HOUR;
|
||||
}
|
||||
break;
|
||||
case UNIT_SECONDS_PER_100_METERS:
|
||||
@@ -208,9 +232,14 @@ public class WorkoutValueFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
if (unit.equals("seconds") && !show_raw_data && showUnit) { //rather then plain seconds, show formatted duration
|
||||
if (unit.equals(UNIT_SECONDS) && !show_raw_data && showUnit) { //rather then plain seconds, show formatted duration
|
||||
return DateTimeUtils.formatDurationHoursMinutes((long) value, TimeUnit.SECONDS);
|
||||
} else if (unit.equals("minutes_km") || unit.equals("minutes_mi") || unit.equals("minutes_100m") || unit.equals("minutes_100yd")) {
|
||||
}else if (unit.equals(UNIT_SECONDS_SPORT) && !show_raw_data && showUnit) {
|
||||
return DateTimeUtils.formatSportsDuration(Math.round(1000L * (double) value), TimeUnit.MILLISECONDS);
|
||||
} else if (UNIT_EPOC_TIME.equals(unit) && !show_raw_data) {
|
||||
long epoc = ((Number) rawValue).longValue();
|
||||
return DateTimeUtils.formatLocalTime(epoc * 1000L);
|
||||
} else if (unit.equals(UNIT_MINUTES_PER_KM) || unit.equals(UNIT_MINUTES_PER_MILE) || unit.equals(UNIT_MINUTES_PER_100_METERS) || unit.equals(UNIT_MINUTES_PER_100_YARDS)) {
|
||||
// Format pace
|
||||
String format = showUnit ? "%d:%02d %s" : "%d:%02d";
|
||||
return String.format(
|
||||
|
||||
-33
@@ -76,7 +76,6 @@ public class DefaultWorkoutCharts {
|
||||
final List<Entry> temperatureDataPoints = new ArrayList<>(initalCapacity);
|
||||
final List<Entry> depthDataPoints = new ArrayList<>(initalCapacity);
|
||||
final List<Entry> distancePoints = new ArrayList<>(initalCapacity);
|
||||
final List<Entry> stridePoints = new ArrayList<>(initalCapacity);
|
||||
final List<Entry> staminaPoints = new ArrayList<>(initalCapacity);
|
||||
final List<Entry> bodyEnergyPoints = new ArrayList<>(initalCapacity);
|
||||
final List<Entry> stepLengthPoints = new ArrayList<>(initalCapacity);
|
||||
@@ -92,7 +91,6 @@ public class DefaultWorkoutCharts {
|
||||
boolean hasTemperatureValues = false;
|
||||
boolean hasDepthValues = false;
|
||||
boolean hasDistanceValues = false;
|
||||
boolean hasStrideValues = false;
|
||||
boolean hasBodyEnergyValues = false;
|
||||
boolean hasStaminaValues = false;
|
||||
boolean hasStepLengthValues = false;
|
||||
@@ -168,13 +166,6 @@ public class DefaultWorkoutCharts {
|
||||
hasDistanceValues = hasDistanceValues || (distance > 0);
|
||||
}
|
||||
|
||||
// Stride
|
||||
final float stride = point.getStride();
|
||||
if (stride >= 0.0f) {
|
||||
stridePoints.add(new Entry(tsShorten, stride));
|
||||
hasStrideValues = hasStrideValues || (stride > 0.0f);
|
||||
}
|
||||
|
||||
// Body Energy
|
||||
final float bodyEnergy = point.getBodyEnergy();
|
||||
if (bodyEnergy >= 0.0f) {
|
||||
@@ -247,10 +238,6 @@ public class DefaultWorkoutCharts {
|
||||
charts.add(createDistanceChart(context, distancePoints));
|
||||
}
|
||||
|
||||
if (hasStrideValues && !stridePoints.isEmpty()) {
|
||||
charts.add(createStrideChart(context, stridePoints));
|
||||
}
|
||||
|
||||
if (hasBodyEnergyValues && !bodyEnergyPoints.isEmpty()) {
|
||||
charts.add(createBodyEnergyChart(context, bodyEnergyPoints));
|
||||
}
|
||||
@@ -486,26 +473,6 @@ public class DefaultWorkoutCharts {
|
||||
);
|
||||
}
|
||||
|
||||
private static WorkoutChart createStrideChart(final Context context,
|
||||
final List<Entry> stridePoints) {
|
||||
final String label = String.format("%s(%s)", context.getString(R.string.stride), getUnitString(context, UNIT_MM));
|
||||
final LineDataSet dataset = createLineDataSet(context, stridePoints, label, ContextCompat.getColor(context, R.color.chart_line_stride));
|
||||
final ValueFormatter valueFormatter = new ValueFormatter() {
|
||||
@Override
|
||||
public String getFormattedValue(float value) {
|
||||
return String.valueOf((int) value);
|
||||
}
|
||||
};
|
||||
return new WorkoutChart(
|
||||
"chart_stride",
|
||||
context.getString(R.string.stride),
|
||||
ActivitySummaryEntries.GROUP_STEPS,
|
||||
new LineData(dataset),
|
||||
valueFormatter,
|
||||
getUnitString(context, UNIT_MM)
|
||||
);
|
||||
}
|
||||
|
||||
private static WorkoutChart createBodyEnergyChart(final Context context,
|
||||
final List<Entry> bodyEnergyPoints) {
|
||||
final String label = String.format("%s(%s)", context.getString(R.string.body_energy), getUnitString(context, UNIT_PERCENTAGE));
|
||||
|
||||
+3
@@ -266,6 +266,9 @@ object ActivitySummaryGroup {
|
||||
ActivitySummaryEntries.RECOVERY_TIME,
|
||||
ActivitySummaryEntries.BODY_ENERGY_AT_START,
|
||||
ActivitySummaryEntries.BODY_ENERGY_AT_END,
|
||||
ActivitySummaryEntries.STAMINA_AT_START,
|
||||
ActivitySummaryEntries.STAMINA_AT_END,
|
||||
ActivitySummaryEntries.STAMINA_MIN,
|
||||
ActivitySummaryEntries.LACTATE_THRESHOLD_HR,
|
||||
ActivitySummaryEntries.RATING_OF_PERCEIVED_EXERTION,
|
||||
ActivitySummaryEntries.WORKOUT_FEEL,
|
||||
|
||||
+92
-38
@@ -398,22 +398,26 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
summary.setEndTime(new Date(summary.getStartTime().getTime() + session.getTotalElapsedTime().intValue()));
|
||||
}
|
||||
|
||||
if (session.getTotalTimerTime() != null) {
|
||||
summaryData.add(ACTIVE_SECONDS, session.getTotalTimerTime() / 1000.0f, UNIT_SECONDS);
|
||||
}
|
||||
if (session.getTotalDistance() != null) {
|
||||
summaryData.add(DISTANCE_METERS, session.getTotalDistance() / 100.0f, UNIT_METERS);
|
||||
}
|
||||
summaryData.add(ACTIVE_SECONDS, session.getTotalTimerTime(), UNIT_SECONDS);
|
||||
summaryData.add(DISTANCE_METERS, session.getTotalDistance(), UNIT_METERS);
|
||||
summaryData.add(POOL_LENGTH, session.getPoolLength(), UNIT_METERS);
|
||||
summaryData.add(SWOLF_AVG, session.getAvgSwolf(), UNIT_NONE);
|
||||
if (session.getTotalCycles() != null && cycleUnit != ActivityKind.CycleUnit.NONE) {
|
||||
if (cycleUnit == ActivityKind.CycleUnit.STEPS) {
|
||||
summaryData.addTotal(session.getTotalCycles() * 2, cycleUnit);
|
||||
} else {
|
||||
// FIXME some of the rest might also need adjusting...
|
||||
summaryData.addTotal(session.getTotalCycles(), cycleUnit);
|
||||
if (cycleUnit != ActivityKind.CycleUnit.NONE) {
|
||||
Number totalCycles = session.getTotalCycles();
|
||||
if (totalCycles != null) {
|
||||
final Number totalFractionalCycles = session.getTotalFractionalCycles();
|
||||
if (totalFractionalCycles != null) {
|
||||
totalCycles = totalCycles.doubleValue() + totalFractionalCycles.doubleValue();
|
||||
}
|
||||
if (cycleUnit == ActivityKind.CycleUnit.STEPS) {
|
||||
summaryData.addTotal(totalCycles.doubleValue() * 2, cycleUnit);
|
||||
} else {
|
||||
// FIXME some of the rest might also need adjusting...
|
||||
summaryData.addTotal(totalCycles, cycleUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
summaryData.add(STEP_LENGTH_AVG, session.getAvgStepLength(), UNIT_MM);
|
||||
if (session.getTotalCalories() != null) {
|
||||
summaryData.add(CALORIES_CONSUMED, session.getCaloriesConsumed(), UNIT_KCAL);
|
||||
@@ -449,22 +453,34 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
session.getEnhancedAvgAltitude(), session.getAvgAltitude());
|
||||
|
||||
summaryData.add(STRESS_AVG, session.getAvgStress(), UNIT_NONE);
|
||||
if (session.getAvgCadence() != null) {
|
||||
Number avgCadence = session.getAvgCadence();
|
||||
if (avgCadence != null) {
|
||||
final Number avgFractionalCadence = session.getAvgFractionalCadence();
|
||||
if (avgFractionalCadence != null) {
|
||||
avgCadence = avgCadence.doubleValue() + avgFractionalCadence.doubleValue();
|
||||
}
|
||||
if (cycleUnit == ActivityKind.CycleUnit.STEPS) {
|
||||
summaryData.addCadenceAvg(session.getAvgCadence() * 2, cycleUnit);
|
||||
summaryData.addCadenceAvg(avgCadence.doubleValue() * 2, cycleUnit);
|
||||
} else {
|
||||
// FIXME some of the rest might also need adjusting...
|
||||
summaryData.addCadenceAvg(session.getAvgCadence(), cycleUnit);
|
||||
summaryData.addCadenceAvg(avgCadence, cycleUnit);
|
||||
}
|
||||
}
|
||||
if (session.getMaxCadence() != null) {
|
||||
|
||||
Number maxCadence = session.getMaxCadence();
|
||||
if (maxCadence != null) {
|
||||
final Number maxFractionalCadence = session.getMaxFractionalCadence();
|
||||
if (maxFractionalCadence != null) {
|
||||
maxCadence = maxCadence.doubleValue() + maxFractionalCadence.doubleValue();
|
||||
}
|
||||
if (cycleUnit == ActivityKind.CycleUnit.STEPS) {
|
||||
summaryData.addCadenceMax(session.getMaxCadence() * 2, cycleUnit);
|
||||
summaryData.addCadenceMax(maxCadence.doubleValue() * 2, cycleUnit);
|
||||
} else {
|
||||
// FIXME some of the rest might also need adjusting...
|
||||
summaryData.addCadenceMax(session.getMaxCadence(), cycleUnit);
|
||||
summaryData.addCadenceMax(maxCadence, cycleUnit);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ActivityKind.isDiving(activityKind)) {
|
||||
if(!summaryData.add(TOTAL_ASCENT, session.getTotalAscent(), UNIT_METERS) && physiologicalMetrics != null){
|
||||
summaryData.add(TOTAL_ASCENT, physiologicalMetrics.getTotalAscent(), UNIT_METERS);
|
||||
@@ -517,9 +533,7 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
summaryData.add(NORMALIZED_POWER, session.getNormalizedPower(), UNIT_WATT);
|
||||
summaryData.add(TOTAL_WORK, session.getTotalWork(), UNIT_JOULE);
|
||||
|
||||
if (session.getStandTime() != null) {
|
||||
summaryData.add(STANDING_TIME, session.getStandTime() / 1000, UNIT_SECONDS);
|
||||
}
|
||||
summaryData.add(STANDING_TIME, session.getStandTime(), UNIT_SECONDS);
|
||||
summaryData.add(STANDING_COUNT, session.getStandCount(), UNIT_NONE);
|
||||
summaryData.add(AVG_LEFT_PCO, session.getAvgLeftPco(), UNIT_MM);
|
||||
summaryData.add(AVG_RIGHT_PCO, session.getAvgRightPco(), UNIT_MM);
|
||||
@@ -527,13 +541,14 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
summaryData.add(AVG_VERTICAL_OSCILLATION, session.getAvgVerticalOscillation(), UNIT_MM);
|
||||
summaryData.add(AVG_GROUND_CONTACT_TIME, session.getAvgStanceTime(), UNIT_MILLISECONDS);
|
||||
summaryData.add(AVG_VERTICAL_RATIO, session.getAvgVerticalRatio(), UNIT_PERCENTAGE);
|
||||
if (session.getAvgStanceTimeBalance() != null) {
|
||||
final Float avgStanceTimeBalance = session.getAvgStanceTimeBalance();
|
||||
if (avgStanceTimeBalance != null && avgStanceTimeBalance > 0.0f) {
|
||||
summaryData.add(
|
||||
AVG_GROUND_CONTACT_TIME_BALANCE,
|
||||
context.getString(
|
||||
R.string.range_percentage_float,
|
||||
session.getAvgStanceTimeBalance(),
|
||||
100.0f - session.getAvgStanceTimeBalance()
|
||||
avgStanceTimeBalance,
|
||||
100.0f - avgStanceTimeBalance
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -713,13 +728,17 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!summaryData.add(TRAINING_EFFECT_ANAEROBIC, session.getTotalAnaerobicTrainingEffect(), UNIT_NONE, true)) {
|
||||
if (physiologicalMetrics != null) {
|
||||
summaryData.add(TRAINING_EFFECT_ANAEROBIC, physiologicalMetrics.getAnaerobicEffect(), UNIT_NONE, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (physiologicalMetrics != null) {
|
||||
if (physiologicalMetrics.getAerobicEffect() != null) {
|
||||
summaryData.add(TRAINING_EFFECT_AEROBIC, physiologicalMetrics.getAerobicEffect(), UNIT_NONE, true);
|
||||
}
|
||||
if (physiologicalMetrics.getAnaerobicEffect() != null) {
|
||||
summaryData.add(TRAINING_EFFECT_ANAEROBIC, physiologicalMetrics.getAnaerobicEffect(), UNIT_NONE, true);
|
||||
}
|
||||
if (physiologicalMetrics.getMetMax() != null) {
|
||||
summaryData.add(MAXIMUM_OXYGEN_UPTAKE, physiologicalMetrics.getMetMax().floatValue() * 3.5f, UNIT_ML_KG_MIN);
|
||||
}
|
||||
@@ -729,20 +748,24 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
summaryData.add(LACTATE_THRESHOLD_HR, physiologicalMetrics.getLactateThresholdHeartRate(), UNIT_BPM);
|
||||
}
|
||||
|
||||
// diving related
|
||||
summaryData.add(DIVE_NUMBER, UNIT_NONE, session.getDiveNumber(), diveSummary != null ? diveSummary.getDiveNumber() : null);
|
||||
if (diveSummary != null) {
|
||||
summaryData.add(DIVE_NUMBER, diveSummary.getDiveNumber(), UNIT_NONE);
|
||||
summaryData.add(BOTTOM_TIME, diveSummary.getBottomTime(), UNIT_SECONDS);
|
||||
summaryData.add(AVG_DEPTH, diveSummary.getAvgDepth(), UNIT_METERS);
|
||||
summaryData.add(MAX_DEPTH, diveSummary.getMaxDepth(), UNIT_METERS);
|
||||
}
|
||||
summaryData.add(AVG_DEPTH, UNIT_METERS, session.getAvgDepth(), diveSummary != null ? diveSummary.getAvgDepth() : null);
|
||||
summaryData.add(MAX_DEPTH, UNIT_METERS, session.getMaxDepth(), diveSummary != null ? diveSummary.getMaxDepth() : null);
|
||||
|
||||
// force display even if value is 0
|
||||
summaryData.add(START_CNS, diveSummary.getStartCns(), UNIT_PERCENTAGE, true);
|
||||
summaryData.add(END_CNS, diveSummary.getEndCns(), UNIT_PERCENTAGE, true);
|
||||
summaryData.add(START_N2, diveSummary.getStartN2(), UNIT_PERCENTAGE, true);
|
||||
summaryData.add(END_N2, diveSummary.getEndN2(), UNIT_PERCENTAGE, true);
|
||||
summaryData.add(OXYGEN_TOXICITY, diveSummary.getO2Toxicity(), UNIT_OXYGEN_TOXICITY_UNITs, true);
|
||||
// force display even if value is 0
|
||||
summaryData.add(START_CNS, UNIT_PERCENTAGE, session.getStartCns(), diveSummary != null ? diveSummary.getStartCns() : null, true);
|
||||
summaryData.add(END_CNS, UNIT_PERCENTAGE, session.getEndCns(), diveSummary != null ? diveSummary.getEndCns() : null, true);
|
||||
summaryData.add(START_N2, UNIT_PERCENTAGE, session.getStartN2(), diveSummary != null ? diveSummary.getStartN2() : null, true);
|
||||
summaryData.add(END_N2, UNIT_PERCENTAGE, session.getEndN2(), diveSummary != null ? diveSummary.getEndN2() : null, true);
|
||||
summaryData.add(OXYGEN_TOXICITY, UNIT_OXYGEN_TOXICITY_UNITs, session.getO2Toxicity(), diveSummary != null ? diveSummary.getO2Toxicity() : null, true);
|
||||
|
||||
summaryData.add(SURFACE_INTERVAL, diveSummary.getSurfaceInterval(), UNIT_SECONDS);
|
||||
summaryData.add(SURFACE_INTERVAL, UNIT_SECONDS, session.getSurfaceInterval(), diveSummary != null ? diveSummary.getSurfaceInterval() : null);
|
||||
|
||||
if (diveSummary != null) {
|
||||
summaryData.add(PRESSURE_SAC_AVG, diveSummary.getAvgPressureSac(), UNIT_BAR_PER_MINUTE);
|
||||
}
|
||||
|
||||
@@ -812,6 +835,9 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
summaryData.add(BODY_ENERGY_AT_END, physiologicalMetrics.getEndingBodyBattery(), UNIT_PERCENTAGE);
|
||||
}
|
||||
}
|
||||
summaryData.add(STAMINA_AT_START, session.getBeginningPotential(), UNIT_PERCENTAGE);
|
||||
summaryData.add(STAMINA_AT_END, session.getEndingPotential(), UNIT_PERCENTAGE);
|
||||
summaryData.add(STAMINA_MIN, session.getMinStamina(), UNIT_PERCENTAGE);
|
||||
|
||||
summaryData.add(SOLAR_INTENSITY, session.getSolarIntensity(), UNIT_PERCENTAGE);
|
||||
summaryData.add(BATTERY_GAIN, session.getBatteryGain(), UNIT_SECONDS);
|
||||
@@ -1010,7 +1036,35 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
|
||||
final boolean anySwimmingLaps = laps.stream()
|
||||
.anyMatch(lap -> lap.getSwimStyle() != null);
|
||||
|
||||
if (anyValidLaps && diveLaps.isEmpty()) {
|
||||
if (activityKind == ActivityKind.STOP_WATCH) {
|
||||
// The start value encoded in the FIT file is shifted to a later time if the stop watch was paused.
|
||||
// There is not enough information in the FIT file to fix this so display the values as they
|
||||
// were encoded.
|
||||
// The duration / active seconds are accurate.
|
||||
final ActivitySummaryTableBuilder tableBuilder = new ActivitySummaryTableBuilder(GROUP_INTERVALS, "intervals_header", Arrays.asList(
|
||||
"#",
|
||||
"start",
|
||||
"stop",
|
||||
ACTIVE_SECONDS
|
||||
));
|
||||
for (int lapIndex = 0; lapIndex < laps.size(); lapIndex++) {
|
||||
FitLap lap = laps.get(lapIndex);
|
||||
Integer index = lap.getMessageIndex();
|
||||
if (index != null) {
|
||||
index = index + 1;
|
||||
} else {
|
||||
index = lapIndex + 1;
|
||||
}
|
||||
|
||||
final List<ActivitySummaryValue> row = new ArrayList<>();
|
||||
row.add(new ActivitySummaryValue(index, UNIT_NONE));
|
||||
row.add(new ActivitySummaryValue(lap.getStartTime(), UNIT_EPOC_TIME));
|
||||
row.add(new ActivitySummaryValue(lap.getTimestamp(), UNIT_EPOC_TIME));
|
||||
row.add(new ActivitySummaryValue(lap.getTotalTimerTime(), UNIT_SECONDS_SPORT));
|
||||
tableBuilder.addRow("interval_" + lapIndex, row);
|
||||
}
|
||||
tableBuilder.addToSummaryData(summaryData);
|
||||
} else if (anyValidLaps && diveLaps.isEmpty()) {
|
||||
// Unfortunately our tables do not yet scroll horizontally, so can't always add all possible columns
|
||||
final List<String> header = new ArrayList<>();
|
||||
header.add("#");
|
||||
|
||||
@@ -60,7 +60,7 @@ public enum ActivityKind {
|
||||
|
||||
// Non-legacy activity kinds after 0x04000000
|
||||
NAVIGATE(0x04000000, R.string.activity_type_navigate, R.drawable.ic_navigation),
|
||||
INDOOR_TRACK(0x04000001, R.string.activity_type_indoor_track),
|
||||
INDOOR_TRACK_RUNNING(0x04000001, R.string.activity_type_indoor_track, R.drawable.ic_run_circle),
|
||||
HANDCYCLING(0x04000002, R.string.activity_type_handcycling),
|
||||
E_BIKE(0x04000003, R.string.activity_type_e_bike, R.drawable.ic_activity_electric_bike),
|
||||
BIKE_COMMUTE(0x04000004, R.string.activity_type_bike_commute, R.drawable.ic_activity_bike_lane),
|
||||
|
||||
@@ -27,7 +27,6 @@ public class ActivityPoint {
|
||||
private GPSCoordinate location;
|
||||
private int heartRate;
|
||||
private float speed = -1;
|
||||
private int stride = -1;
|
||||
private int stepLength = -1;
|
||||
private int cadence = -1;
|
||||
private float power = -1;
|
||||
@@ -145,12 +144,6 @@ public class ActivityPoint {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
/// distance from one foot landing to the same foot landing again
|
||||
/// @see ActivitySummaryEntries#UNIT_MM
|
||||
public int getStride() {
|
||||
return stride;
|
||||
}
|
||||
|
||||
/// distance from one foot landing to the opposite foot landing
|
||||
/// @see ActivitySummaryEntries#UNIT_MM
|
||||
public int getStepLength() {
|
||||
@@ -210,7 +203,6 @@ public class ActivityPoint {
|
||||
if (!(o instanceof ActivityPoint that)) return false;
|
||||
return heartRate == that.heartRate &&
|
||||
Float.compare(speed, that.speed) == 0 &&
|
||||
stride == that.stride &&
|
||||
cadence == that.cadence &&
|
||||
Float.compare(power, that.power) == 0 &&
|
||||
Float.compare(respiratoryRate, that.respiratoryRate) == 0 &&
|
||||
@@ -227,7 +219,7 @@ public class ActivityPoint {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(time, location, heartRate, speed, stride, cadence, power, respiratoryRate, depth, temperature, description, distance, altitude, bodyEnergy, stamina);
|
||||
return Objects.hash(time, location, heartRate, speed, cadence, power, respiratoryRate, depth, temperature, description, distance, altitude, bodyEnergy, stamina);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@@ -245,7 +237,6 @@ public class ActivityPoint {
|
||||
|
||||
private int heartRate = Integer.MIN_VALUE;
|
||||
private float speed = Float.NaN;
|
||||
private int stride = Integer.MIN_VALUE;
|
||||
private int stepLength = Integer.MIN_VALUE;
|
||||
private int cadence = Integer.MIN_VALUE;
|
||||
private float power = Float.NaN;
|
||||
@@ -349,23 +340,6 @@ public class ActivityPoint {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
/// @see ActivitySummaryEntries#UNIT_MM
|
||||
public int getStride() {
|
||||
return stride;
|
||||
}
|
||||
|
||||
/// distance from one foot landing to the same foot landing again
|
||||
/// @see ActivitySummaryEntries#UNIT_MM
|
||||
public void setStride(@Nullable Number stride) {
|
||||
this.stride = (stride == null) ? Integer.MIN_VALUE : stride.intValue();
|
||||
}
|
||||
|
||||
/// distance from one foot landing to the same foot landing again
|
||||
/// @see ActivitySummaryEntries#UNIT_MM
|
||||
public void setStride(int stride) {
|
||||
this.stride = stride;
|
||||
}
|
||||
|
||||
public int getCadence() {
|
||||
return cadence;
|
||||
}
|
||||
@@ -564,9 +538,6 @@ public class ActivityPoint {
|
||||
if (cadence > Integer.MIN_VALUE) {
|
||||
activityPoint.setCadence(cadence);
|
||||
}
|
||||
if (stride > Integer.MIN_VALUE) {
|
||||
activityPoint.stride = stride;
|
||||
}
|
||||
if (!Float.isNaN(power)) {
|
||||
activityPoint.setPower(power);
|
||||
}
|
||||
|
||||
+7
-2
@@ -69,10 +69,15 @@ public class ActivitySummaryData {
|
||||
|
||||
/// @return {@code true} if the value was actually added
|
||||
public boolean add(final String key, final String unit, final Number value, final Number valueFallback) {
|
||||
return add(key, unit, value, valueFallback, false);
|
||||
}
|
||||
|
||||
/// @return {@code true} if the value was actually added
|
||||
public boolean add(final String key, final String unit, final Number value, final Number valueFallback, boolean force) {
|
||||
if (value != null) {
|
||||
return add(null, key, value, unit, false);
|
||||
return add(null, key, value, unit, force);
|
||||
} else {
|
||||
return add(null, key, valueFallback, unit, false);
|
||||
return add(null, key, valueFallback, unit, force);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -267,6 +267,15 @@ public class ActivitySummaryEntries {
|
||||
public static final String UNIT_KG_PER_M3 = "unit_kg_per_m3";
|
||||
public static final String UNIT_BAR = "unit_bar";
|
||||
public static final String UNIT_LITER = "unit_liter";
|
||||
/// UNIX epoc seconds to be formated in local time without showing date
|
||||
public static final String UNIT_EPOC_TIME = "unit_epoc_time";
|
||||
/// display time including milli seconds if any
|
||||
public static final String UNIT_SECONDS_SPORT = "unit_sports_seconds";
|
||||
public static final String UNIT_FOOT = "ft";
|
||||
public static final String UNIT_FOOT_PER_HOUR = "foot_per_hour";
|
||||
public static final String UNIT_MILE = "mi";
|
||||
public static final String UNIT_MILE_PER_HOUR = "mi_h";
|
||||
public static final String UNIT_MINUTES_PER_MILE = "minutes_mi";
|
||||
|
||||
public static final String GROUP_PACE = "Pace";
|
||||
public static final String GROUP_ACTIVITY = "Activity";
|
||||
@@ -307,6 +316,9 @@ public class ActivitySummaryEntries {
|
||||
public static final String RECOVERY_TIME_REMAINING_AT_START = "recovery_time_remaining_at_start";
|
||||
public static final String BODY_ENERGY_AT_START = "body_energy_at_start";
|
||||
public static final String BODY_ENERGY_AT_END = "body_energy_at_end";
|
||||
public static final String STAMINA_AT_START = "stamina_at_start";
|
||||
public static final String STAMINA_AT_END = "stamina_at_end";
|
||||
public static final String STAMINA_MIN = "stamina_min";
|
||||
public static final String TOTAL_WORK = "total_work";
|
||||
public static final String BATTERY_LEVEL_START = "battery_level_start";
|
||||
public static final String BATTERY_LEVEL_END = "battery_level_end";
|
||||
|
||||
+25
@@ -22,6 +22,19 @@ import java.util.Locale
|
||||
data class AntGadget(val manufacturer: Int, val product: Int, val name: String) {
|
||||
companion object {
|
||||
val All: Collection<AntGadget> = listOf(
|
||||
AntGadget(1, 1, "HRM"),
|
||||
AntGadget(1, 5, "HRM2"),
|
||||
AntGadget(1, 7, "HRM3"),
|
||||
AntGadget(1, 8, "HRM Run"),
|
||||
AntGadget(1, 9, "BSM"),
|
||||
AntGadget(1, 10, "BCM"),
|
||||
AntGadget(1, 12, "HRM-Tri"),
|
||||
AntGadget(1, 13, "HRM4-Run"),
|
||||
AntGadget(1, 15, "BSM v3"),
|
||||
AntGadget(1, 16, "BCM v3"),
|
||||
AntGadget(1, 18, "HRM-Dual"),
|
||||
AntGadget(1, 21, "HRMPro+"),
|
||||
AntGadget(1, 22, "HRM-Fit"),
|
||||
AntGadget(1, 255, "OHR"),
|
||||
AntGadget(1, 357, "Rally RS200"),
|
||||
AntGadget(1, 359, "Varia RTL515"),
|
||||
@@ -77,6 +90,7 @@ data class AntGadget(val manufacturer: Int, val product: Int, val name: String)
|
||||
AntGadget(1, 2327, "HRM-Run"),
|
||||
AntGadget(1, 2337, "Vivoactive HR"),
|
||||
AntGadget(1, 2348, "Vivosmart HR"),
|
||||
AntGadget(1, 2396, "Forerunner 235 Asia"),
|
||||
AntGadget(1, 2413, "Fenix 3 HR"),
|
||||
AntGadget(1, 2413, "Fenix 3"),
|
||||
AntGadget(1, 2431, "Forerunner 235"),
|
||||
@@ -89,6 +103,7 @@ data class AntGadget(val manufacturer: Int, val product: Int, val name: String)
|
||||
AntGadget(1, 2544, "Fenix 5S"),
|
||||
AntGadget(1, 2567, "Varia UT800"),
|
||||
AntGadget(1, 2593, "RD Pod"),
|
||||
AntGadget(1, 2599, "Edge 820 China"),
|
||||
AntGadget(1, 2604, "Fenix 5X"),
|
||||
AntGadget(1, 2622, "Vivosmart 3"),
|
||||
AntGadget(1, 2623, "Vivosport"),
|
||||
@@ -295,11 +310,16 @@ data class AntGadget(val manufacturer: Int, val product: Int, val name: String)
|
||||
AntGadget(23, 56, "Suunto 5 Peak"),
|
||||
AntGadget(23, 58, "Suunto 9 Peak Pro"),
|
||||
AntGadget(23, 59, "Suunto Vertical"),
|
||||
AntGadget(23, 60, "Suunto Race"),
|
||||
AntGadget(23, 61, "Suunto Race S"),
|
||||
AntGadget(23, 62, "Suunto Ocean"),
|
||||
AntGadget(23, 65, "Suunto Run"),
|
||||
AntGadget(23, 66, "Suunto Race 2"),
|
||||
AntGadget(23, 67, "Suunto Vertical 2"),
|
||||
AntGadget(28, 0, "TrainingPeaks Virtual"),
|
||||
AntGadget(32, 8, "TICKRX"),
|
||||
AntGadget(32, 28, "ELEMNT"),
|
||||
AntGadget(32, 31, "ELEMNT BOLT"),
|
||||
AntGadget(32, 33, "ELEMNT RIVAL"),
|
||||
AntGadget(32, 35, "TICKR FIT"),
|
||||
AntGadget(32, 37, "ELEMNT ROAM"),
|
||||
@@ -309,6 +329,8 @@ data class AntGadget(val manufacturer: Int, val product: Int, val name: String)
|
||||
AntGadget(32, 338, "TRACKR RADAR"),
|
||||
AntGadget(69, 1, "Stages DashL50"),
|
||||
AntGadget(69, 3, "Stages M200"),
|
||||
AntGadget(86, 28, "Elite SUITO"),
|
||||
AntGadget(89, 2850, "Tacx NEO 2"),
|
||||
AntGadget(107, 344, "Magene C506SE"),
|
||||
AntGadget(123, 2, "Polar H10"),
|
||||
AntGadget(123, 3, "Polar H9"),
|
||||
@@ -319,6 +341,7 @@ data class AntGadget(val manufacturer: Int, val product: Int, val name: String)
|
||||
AntGadget(132, 3, "Cycplus C3"),
|
||||
AntGadget(136, 345, "Geoid CC600"),
|
||||
AntGadget(258, 4, "Lezyne Super GPS"),
|
||||
AntGadget(258, 9, "Lezyne Enhanced Micro C GPS"),
|
||||
AntGadget(263, 10, "Assioma Uno"),
|
||||
AntGadget(263, 12, "Assioma Duo"),
|
||||
AntGadget(267, 1902, "Rider 750"),
|
||||
@@ -330,9 +353,11 @@ data class AntGadget(val manufacturer: Int, val product: Int, val name: String)
|
||||
AntGadget(289, 1, "Karoo"),
|
||||
AntGadget(289, 2, "Karoo 2"),
|
||||
AntGadget(289, 3, "Karoo 3"),
|
||||
AntGadget(294, 801, "COROS PACE"),
|
||||
AntGadget(294, 802, "COROS PACE 2"),
|
||||
AntGadget(294, 804, "COROS PACE 3"),
|
||||
AntGadget(294, 805, "COROS PACE Pro"),
|
||||
AntGadget(294, 806, "COROS PACE 4"),
|
||||
AntGadget(294, 811, "COROS APEX 42mm"),
|
||||
AntGadget(294, 812, "COROS APEX 2"),
|
||||
AntGadget(294, 821, "COROS APEX"),
|
||||
|
||||
+3
@@ -309,6 +309,9 @@ public class FitImporter {
|
||||
if (event.getEvent() == null) {
|
||||
LOG.warn("Event in {} is null", event);
|
||||
continue;
|
||||
} else if (ts == null) {
|
||||
LOG.warn("Timestamp for {} is null", event);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG.trace("Event at {}: {}", ts, event);
|
||||
|
||||
+42
-10
@@ -156,6 +156,7 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(40, BaseType.UINT8Z, "rear_gear_num"),
|
||||
new FieldDefinitionPrimitive(41, BaseType.UINT8Z, "rear_gear", FieldDefinitionFactory.FIELD.ARRAY),
|
||||
new FieldDefinitionPrimitive(44, BaseType.ENUM, "shimano_di2_enabled", FieldDefinitionFactory.FIELD.BOOLEAN),
|
||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP),
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT16, "message_index")
|
||||
));
|
||||
|
||||
@@ -164,7 +165,9 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(1, BaseType.UINT8, "max_heart_rate"),
|
||||
new FieldDefinitionPrimitive(2, BaseType.UINT8, "threshold_heart_rate"),
|
||||
new FieldDefinitionPrimitive(5, BaseType.ENUM, "hr_calc_type"), //1=percent_max_hr
|
||||
new FieldDefinitionPrimitive(7, BaseType.ENUM, "pwr_calc_type") //1=percent_ftp
|
||||
new FieldDefinitionPrimitive(7, BaseType.ENUM, "pwr_calc_type"), //1=percent_ftp
|
||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP),
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT16, "message_index")
|
||||
));
|
||||
|
||||
public static final NativeFITMessage HR_ZONE = new NativeFITMessage(8, "HR_ZONE", Arrays.asList(
|
||||
@@ -293,9 +296,9 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(4, BaseType.SINT32, "start_longitude", FieldDefinitionFactory.FIELD.COORDINATE),
|
||||
new FieldDefinitionPrimitive(5, BaseType.ENUM, "sport"),
|
||||
new FieldDefinitionPrimitive(6, BaseType.ENUM, "sub_sport"),
|
||||
new FieldDefinitionPrimitive(7, BaseType.UINT32, "total_elapsed_time"), // with pauses
|
||||
new FieldDefinitionPrimitive(8, BaseType.UINT32, "total_timer_time"), // no pauses
|
||||
new FieldDefinitionPrimitive(9, BaseType.UINT32, "total_distance"), // cm
|
||||
new FieldDefinitionPrimitive(7, BaseType.UINT32, "total_elapsed_time"), // ms - with pauses - keep it in ms to avoid rounding issue for DB calculations
|
||||
new FieldDefinitionPrimitive(8, BaseType.UINT32, "total_timer_time", 1000, 0), // s - no pauses
|
||||
new FieldDefinitionPrimitive(9, BaseType.UINT32, "total_distance", 100, 0), // m
|
||||
new FieldDefinitionPrimitive(10, BaseType.UINT32, "total_cycles"),
|
||||
new FieldDefinitionPrimitive(11, BaseType.UINT16, "total_calories"),
|
||||
new FieldDefinitionPrimitive(13, BaseType.UINT16, "total_fat_calories"), // kcal
|
||||
@@ -387,7 +390,7 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(108, BaseType.UINT16, "rear_shifts"),
|
||||
new FieldDefinitionPrimitive(110, BaseType.STRING, 64, "sport_profile_name"),
|
||||
new FieldDefinitionPrimitive(111, BaseType.UINT8, "sport_index"),
|
||||
new FieldDefinitionPrimitive(112, BaseType.UINT32, "stand_time"), // s
|
||||
new FieldDefinitionPrimitive(112, BaseType.UINT32, "stand_time", 1000, 0), // s
|
||||
new FieldDefinitionPrimitive(113, BaseType.UINT16, "stand_count"),
|
||||
new FieldDefinitionPrimitive(114, BaseType.SINT8, "avg_left_pco"), // mm
|
||||
new FieldDefinitionPrimitive(115, BaseType.SINT8, "avg_right_pco"), // mm
|
||||
@@ -1126,8 +1129,9 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(1, BaseType.ENUM, "sport"),
|
||||
new FieldDefinitionPrimitive(2, BaseType.UINT32, "distance", 100, 0), // m
|
||||
new FieldDefinitionPrimitive(3, BaseType.UINT32, "time", 1000, 0), // s
|
||||
new FieldDefinitionPrimitive(4, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP),
|
||||
new FieldDefinitionPrimitive(5, BaseType.ENUM, "personal_record")
|
||||
new FieldDefinitionPrimitive(4, BaseType.UINT32, "start_time", FieldDefinitionFactory.FIELD.TIMESTAMP),
|
||||
new FieldDefinitionPrimitive(5, BaseType.ENUM, "personal_record"),
|
||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||
));
|
||||
|
||||
public static final NativeFITMessage PERSONAL_RECORD = new NativeFITMessage(114, "PERSONAL_RECORD", Arrays.asList(
|
||||
@@ -1328,6 +1332,19 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT16, "message_index")
|
||||
));
|
||||
|
||||
public static final NativeFITMessage MULTISPORT_SETTINGS = new NativeFITMessage(143, "MULTISPORT_SETTINGS", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.STRING, "name"),
|
||||
new FieldDefinitionPrimitive(1, BaseType.ENUM, "transitions"),
|
||||
new FieldDefinitionPrimitive(2, BaseType.UINT8, "number_of_activities"),
|
||||
new FieldDefinitionPrimitive(3, BaseType.ENUM, "auto_pause"),
|
||||
new FieldDefinitionPrimitive(4, BaseType.ENUM, "alerts"),
|
||||
new FieldDefinitionPrimitive(5, BaseType.ENUM, "auto_lap"),
|
||||
new FieldDefinitionPrimitive(6, BaseType.ENUM, "power_save_timeout"),
|
||||
new FieldDefinitionPrimitive(7, BaseType.ENUM, "auto_scroll"),
|
||||
new FieldDefinitionPrimitive(8, BaseType.ENUM, "repeat"),
|
||||
new FieldDefinitionPrimitive(10, BaseType.ENUM, "sport_change")
|
||||
));
|
||||
|
||||
public static final NativeFITMessage MULTISPORT_ACTIVITY = new NativeFITMessage(144, "MULTISPORT_ACTIVITY", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.ENUM, "sport"),
|
||||
new FieldDefinitionPrimitive(1, BaseType.ENUM, "sub_sport"),
|
||||
@@ -1507,6 +1524,11 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(101, BaseType.BASE_TYPE_BYTE, "field_bits")
|
||||
));
|
||||
|
||||
public static final NativeFITMessage CLUBS = new NativeFITMessage(173, "CLUBS", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(6, BaseType.UINT32, "average_distance", 100, 0), // m
|
||||
new FieldDefinitionPrimitive(19, BaseType.UINT32, "max_distance", 100, 0) // m
|
||||
));
|
||||
|
||||
public static final NativeFITMessage OBDII_DATA = new NativeFITMessage(174, "OBDII_DATA", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.UINT16, "timestamp_ms"), // ms
|
||||
new FieldDefinitionPrimitive(1, BaseType.UINT16, "time_offset", FieldDefinitionFactory.FIELD.ARRAY), // ms
|
||||
@@ -1573,6 +1595,10 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||
));
|
||||
|
||||
public static final NativeFITMessage WAYPOINT_HANDLING = new NativeFITMessage(189, "WAYPOINT_HANDLING", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.ENUM, "action") // 0=add_to_existing, 1=replace_existing 2=delete_all
|
||||
));
|
||||
|
||||
public static final NativeFITMessage GOLF_COURSE = new NativeFITMessage(190, "GOLF_COURSE", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.UINT32, "course_id"),
|
||||
new FieldDefinitionPrimitive(1, BaseType.STRING, "name"),
|
||||
@@ -1760,7 +1786,7 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(9, BaseType.UINT16, "weight_display_unit"),
|
||||
new FieldDefinitionPrimitive(10, BaseType.UINT16, "message_index"),
|
||||
new FieldDefinitionPrimitive(11, BaseType.UINT16, "wkt_step_index"),
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP) // this timestamp is really 254 and not the typical 253
|
||||
));
|
||||
public static final NativeFITMessage DIVE_SETTINGS = new NativeFITMessage(258, "DIVE_SETTINGS", Arrays.asList(
|
||||
new FieldDefinitionPrimitive(0, BaseType.STRING, "name"),
|
||||
@@ -2004,7 +2030,8 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(11, BaseType.SINT32, "end_position_lat", FieldDefinitionFactory.FIELD.COORDINATE),
|
||||
new FieldDefinitionPrimitive(12, BaseType.SINT32, "end_position_long", FieldDefinitionFactory.FIELD.COORDINATE),
|
||||
new FieldDefinitionPrimitive(13, BaseType.UINT32, "start_altitude", 500, 5), // m
|
||||
new FieldDefinitionPrimitive(14, BaseType.UINT32, "end_altitude", 500, 5) // m
|
||||
new FieldDefinitionPrimitive(14, BaseType.UINT32, "end_altitude", 500, 5), // m
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT16, "message_index")
|
||||
));
|
||||
|
||||
public static final NativeFITMessage SPLIT = new NativeFITMessage(312, "SPLIT", Arrays.asList(
|
||||
@@ -2027,6 +2054,7 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(74, BaseType.UINT32, "start_elevation", 5, 500), // meter
|
||||
new FieldDefinitionPrimitive(78, BaseType.UINT32, "active_time", 1000, 0), // s
|
||||
new FieldDefinitionPrimitive(110, BaseType.UINT32, "total_moving_time", 1000, 0), // seconds
|
||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP),
|
||||
new FieldDefinitionPrimitive(254, BaseType.UINT16, "message_index")
|
||||
));
|
||||
|
||||
@@ -2378,7 +2406,8 @@ public class NativeFITMessage {
|
||||
new FieldDefinitionPrimitive(5, BaseType.UINT8, "est_aerobic_te", 10, 0),
|
||||
new FieldDefinitionPrimitive(6, BaseType.UINT8, "est_anaer_te", 10, 0),
|
||||
new FieldDefinitionPrimitive(7, BaseType.ENUM, "sport"),
|
||||
new FieldDefinitionPrimitive(9, BaseType.UINT32, "duration",1000, 0) //s
|
||||
new FieldDefinitionPrimitive(9, BaseType.UINT32, "duration",1000, 0), //s
|
||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||
));
|
||||
|
||||
public static final NativeFITMessage SLEEP_DISRUPTION_SEVERITY_PERIOD = new NativeFITMessage(470, "SLEEP_DISRUPTION_SEVERITY_PERIOD", Arrays.asList(
|
||||
@@ -2457,6 +2486,7 @@ public class NativeFITMessage {
|
||||
put(140, PHYSIOLOGICAL_METRICS);
|
||||
put(141, EPO_STATUS);
|
||||
put(142, SEGMENT_LAP);
|
||||
put(143, MULTISPORT_SETTINGS);
|
||||
put(144, MULTISPORT_ACTIVITY);
|
||||
put(145, MEMO_GLOB);
|
||||
put(147, SENSOR_SETTINGS);
|
||||
@@ -2475,6 +2505,7 @@ public class NativeFITMessage {
|
||||
put(167, THREE_D_SENSOR_CALIBRATION);
|
||||
put(169, VIDEO_FRAME);
|
||||
put(170, CONNECT_IQ_FIELD);
|
||||
put(173, CLUBS);
|
||||
put(174, OBDII_DATA);
|
||||
put(177, NMEA_SENTENCE);
|
||||
put(178, AVIATION_ATTITUDE);
|
||||
@@ -2483,6 +2514,7 @@ public class NativeFITMessage {
|
||||
put(186, VIDEO_DESCRIPTION);
|
||||
put(187, VIDEO_CLIP);
|
||||
put(188, OHR_SETTINGS);
|
||||
put(189, WAYPOINT_HANDLING);
|
||||
put(190, GOLF_COURSE);
|
||||
put(191, GOLF_STATS);
|
||||
put(192, SCORE);
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public enum GarminSport {
|
||||
STREET_RUN(1, 2, ActivityKind.STREET_RUNNING),
|
||||
TRAIL_RUN(1, 3, ActivityKind.TRAIL_RUN),
|
||||
TRACK_RUN(1, 4, ActivityKind.TRACK_RUN),
|
||||
INDOOR_TRACK(1, 45, ActivityKind.INDOOR_TRACK),
|
||||
INDOOR_TRACK(1, 45, ActivityKind.INDOOR_TRACK_RUNNING),
|
||||
ORIENTEERING(1, 50, ActivityKind.ORIENTEERING),
|
||||
VIRTUAL_RUN(1, 58, ActivityKind.VIRTUAL_RUN),
|
||||
OBSTACLE_RACING(1, 59, ActivityKind.OBSTACLE_RACE),
|
||||
|
||||
+12
-2
@@ -55,7 +55,7 @@ public class FitBestEffort extends RecordData {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
public Long getStartTime() {
|
||||
return getFieldByNumber(4, Long.class);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,11 @@ public class FitBestEffort extends RecordData {
|
||||
return getFieldByNumber(5, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
return getFieldByNumber(253, Long.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
@@ -87,7 +92,7 @@ public class FitBestEffort extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTimestamp(final Long value) {
|
||||
public Builder setStartTime(final Long value) {
|
||||
setFieldByNumber(4, value);
|
||||
return this;
|
||||
}
|
||||
@@ -97,6 +102,11 @@ public class FitBestEffort extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTimestamp(final Long value) {
|
||||
setFieldByNumber(253, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitBestEffort build() {
|
||||
return (FitBestEffort) super.build();
|
||||
|
||||
+10
@@ -194,6 +194,11 @@ public class FitBikeProfile extends RecordData {
|
||||
return getFieldByNumber(44, Boolean.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
return getFieldByNumber(253, Long.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getMessageIndex() {
|
||||
return getFieldByNumber(254, Integer.class);
|
||||
@@ -362,6 +367,11 @@ public class FitBikeProfile extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTimestamp(final Long value) {
|
||||
setFieldByNumber(253, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMessageIndex(final Integer value) {
|
||||
setFieldByNumber(254, value);
|
||||
return this;
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/* Copyright (C) 2026 Freeyourgadget
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
|
||||
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 {@link nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen}
|
||||
*
|
||||
* @noinspection unused
|
||||
*/
|
||||
public class FitClubs extends RecordData {
|
||||
public FitClubs(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||
super(recordDefinition, recordHeader);
|
||||
|
||||
final int nativeNumber = recordDefinition.getNativeFITMessage().getNumber();
|
||||
if (nativeNumber != 173) {
|
||||
throw new IllegalArgumentException("FitClubs expects native messages of " + 173 + ", got " + nativeNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double getAverageDistance() {
|
||||
return getFieldByNumber(6, Double.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double getMaxDistance() {
|
||||
return getFieldByNumber(19, Double.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
public static class Builder extends FitRecordDataBuilder {
|
||||
public Builder() {
|
||||
super(173);
|
||||
}
|
||||
|
||||
public Builder setAverageDistance(final Double value) {
|
||||
setFieldByNumber(6, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxDistance(final Double value) {
|
||||
setFieldByNumber(19, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitClubs build() {
|
||||
return (FitClubs) super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitClubs build(final int localMessageType) {
|
||||
return (FitClubs) super.build(localMessageType);
|
||||
}
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/* Copyright (C) 2026 Freeyourgadget
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
|
||||
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 {@link nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen}
|
||||
*
|
||||
* @noinspection unused
|
||||
*/
|
||||
public class FitMultisportSettings extends RecordData {
|
||||
public FitMultisportSettings(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||
super(recordDefinition, recordHeader);
|
||||
|
||||
final int nativeNumber = recordDefinition.getNativeFITMessage().getNumber();
|
||||
if (nativeNumber != 143) {
|
||||
throw new IllegalArgumentException("FitMultisportSettings expects native messages of " + 143 + ", got " + nativeNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return getFieldByNumber(0, String.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getTransitions() {
|
||||
return getFieldByNumber(1, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getNumberOfActivities() {
|
||||
return getFieldByNumber(2, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getAutoPause() {
|
||||
return getFieldByNumber(3, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getAlerts() {
|
||||
return getFieldByNumber(4, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getAutoLap() {
|
||||
return getFieldByNumber(5, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getPowerSaveTimeout() {
|
||||
return getFieldByNumber(6, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getAutoScroll() {
|
||||
return getFieldByNumber(7, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getRepeat() {
|
||||
return getFieldByNumber(8, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getSportChange() {
|
||||
return getFieldByNumber(10, Integer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
public static class Builder extends FitRecordDataBuilder {
|
||||
public Builder() {
|
||||
super(143);
|
||||
}
|
||||
|
||||
public Builder setName(final String value) {
|
||||
setFieldByNumber(0, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTransitions(final Integer value) {
|
||||
setFieldByNumber(1, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNumberOfActivities(final Integer value) {
|
||||
setFieldByNumber(2, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAutoPause(final Integer value) {
|
||||
setFieldByNumber(3, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAlerts(final Integer value) {
|
||||
setFieldByNumber(4, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAutoLap(final Integer value) {
|
||||
setFieldByNumber(5, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPowerSaveTimeout(final Integer value) {
|
||||
setFieldByNumber(6, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAutoScroll(final Integer value) {
|
||||
setFieldByNumber(7, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRepeat(final Integer value) {
|
||||
setFieldByNumber(8, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSportChange(final Integer value) {
|
||||
setFieldByNumber(10, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitMultisportSettings build() {
|
||||
return (FitMultisportSettings) super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitMultisportSettings build(final int localMessageType) {
|
||||
return (FitMultisportSettings) super.build(localMessageType);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1025,7 +1025,7 @@ public class FitRecord extends RecordData {
|
||||
builder.setN2Load(getN2Load());
|
||||
builder.setPower(getPower());
|
||||
builder.setStamina(getStamina());
|
||||
builder.setStride(getStepLength());
|
||||
builder.setStepLength(getStepLength());
|
||||
builder.setTemperature(getTemperature());
|
||||
|
||||
final Double enhancedAltitude = getEnhancedAltitude();
|
||||
|
||||
+3
@@ -95,6 +95,7 @@ public class FitRecordDataFactory {
|
||||
case 140 -> new FitPhysiologicalMetrics(recordDefinition, recordHeader);
|
||||
case 141 -> new FitEpoStatus(recordDefinition, recordHeader);
|
||||
case 142 -> new FitSegmentLap(recordDefinition, recordHeader);
|
||||
case 143 -> new FitMultisportSettings(recordDefinition, recordHeader);
|
||||
case 144 -> new FitMultisportActivity(recordDefinition, recordHeader);
|
||||
case 145 -> new FitMemoGlob(recordDefinition, recordHeader);
|
||||
case 147 -> new FitSensorSettings(recordDefinition, recordHeader);
|
||||
@@ -113,6 +114,7 @@ public class FitRecordDataFactory {
|
||||
case 167 -> new FitThreeDSensorCalibration(recordDefinition, recordHeader);
|
||||
case 169 -> new FitVideoFrame(recordDefinition, recordHeader);
|
||||
case 170 -> new FitConnectIqField(recordDefinition, recordHeader);
|
||||
case 173 -> new FitClubs(recordDefinition, recordHeader);
|
||||
case 174 -> new FitObdiiData(recordDefinition, recordHeader);
|
||||
case 177 -> new FitNmeaSentence(recordDefinition, recordHeader);
|
||||
case 178 -> new FitAviationAttitude(recordDefinition, recordHeader);
|
||||
@@ -121,6 +123,7 @@ public class FitRecordDataFactory {
|
||||
case 186 -> new FitVideoDescription(recordDefinition, recordHeader);
|
||||
case 187 -> new FitVideoClip(recordDefinition, recordHeader);
|
||||
case 188 -> new FitOhrSettings(recordDefinition, recordHeader);
|
||||
case 189 -> new FitWaypointHandling(recordDefinition, recordHeader);
|
||||
case 190 -> new FitGolfCourse(recordDefinition, recordHeader);
|
||||
case 191 -> new FitGolfStats(recordDefinition, recordHeader);
|
||||
case 192 -> new FitScore(recordDefinition, recordHeader);
|
||||
|
||||
+9
-9
@@ -86,13 +86,13 @@ public class FitSession extends RecordData {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTotalTimerTime() {
|
||||
return getFieldByNumber(8, Long.class);
|
||||
public Double getTotalTimerTime() {
|
||||
return getFieldByNumber(8, Double.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTotalDistance() {
|
||||
return getFieldByNumber(9, Long.class);
|
||||
public Double getTotalDistance() {
|
||||
return getFieldByNumber(9, Double.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -551,8 +551,8 @@ public class FitSession extends RecordData {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getStandTime() {
|
||||
return getFieldByNumber(112, Long.class);
|
||||
public Double getStandTime() {
|
||||
return getFieldByNumber(112, Double.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1018,12 +1018,12 @@ public class FitSession extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTotalTimerTime(final Long value) {
|
||||
public Builder setTotalTimerTime(final Double value) {
|
||||
setFieldByNumber(8, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTotalDistance(final Long value) {
|
||||
public Builder setTotalDistance(final Double value) {
|
||||
setFieldByNumber(9, value);
|
||||
return this;
|
||||
}
|
||||
@@ -1483,7 +1483,7 @@ public class FitSession extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStandTime(final Long value) {
|
||||
public Builder setStandTime(final Double value) {
|
||||
setFieldByNumber(112, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
+10
@@ -134,6 +134,11 @@ public class FitSplit extends RecordData {
|
||||
return getFieldByNumber(110, Double.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
return getFieldByNumber(253, Long.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getMessageIndex() {
|
||||
return getFieldByNumber(254, Integer.class);
|
||||
@@ -242,6 +247,11 @@ public class FitSplit extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTimestamp(final Long value) {
|
||||
setFieldByNumber(253, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMessageIndex(final Integer value) {
|
||||
setFieldByNumber(254, value);
|
||||
return this;
|
||||
|
||||
+10
@@ -94,6 +94,11 @@ public class FitSplitTime extends RecordData {
|
||||
return getFieldByNumber(14, Double.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getMessageIndex() {
|
||||
return getFieldByNumber(254, Integer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
@@ -157,6 +162,11 @@ public class FitSplitTime extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMessageIndex(final Integer value) {
|
||||
setFieldByNumber(254, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitSplitTime build() {
|
||||
return (FitSplitTime) super.build();
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/* Copyright (C) 2026 Freeyourgadget
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
|
||||
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 {@link nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen}
|
||||
*
|
||||
* @noinspection unused
|
||||
*/
|
||||
public class FitWaypointHandling extends RecordData {
|
||||
public FitWaypointHandling(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||
super(recordDefinition, recordHeader);
|
||||
|
||||
final int nativeNumber = recordDefinition.getNativeFITMessage().getNumber();
|
||||
if (nativeNumber != 189) {
|
||||
throw new IllegalArgumentException("FitWaypointHandling expects native messages of " + 189 + ", got " + nativeNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getAction() {
|
||||
return getFieldByNumber(0, Integer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
public static class Builder extends FitRecordDataBuilder {
|
||||
public Builder() {
|
||||
super(189);
|
||||
}
|
||||
|
||||
public Builder setAction(final Integer value) {
|
||||
setFieldByNumber(0, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitWaypointHandling build() {
|
||||
return (FitWaypointHandling) super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitWaypointHandling build(final int localMessageType) {
|
||||
return (FitWaypointHandling) super.build(localMessageType);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -69,6 +69,11 @@ public class FitWorkoutSchedule extends RecordData {
|
||||
return getFieldByNumber(9, Double.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
return getFieldByNumber(253, Long.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
@@ -107,6 +112,11 @@ public class FitWorkoutSchedule extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTimestamp(final Long value) {
|
||||
setFieldByNumber(253, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitWorkoutSchedule build() {
|
||||
return (FitWorkoutSchedule) super.build();
|
||||
|
||||
+20
@@ -64,6 +64,16 @@ public class FitZonesTarget extends RecordData {
|
||||
return getFieldByNumber(7, Integer.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTimestamp() {
|
||||
return getFieldByNumber(253, Long.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getMessageIndex() {
|
||||
return getFieldByNumber(254, Integer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
@@ -97,6 +107,16 @@ public class FitZonesTarget extends RecordData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTimestamp(final Long value) {
|
||||
setFieldByNumber(253, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMessageIndex(final Integer value) {
|
||||
setFieldByNumber(254, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FitZonesTarget build() {
|
||||
return (FitZonesTarget) super.build();
|
||||
|
||||
+1
-1
@@ -316,7 +316,7 @@ public class ZeppOsActivityDetailsParser extends AbstractHuamiActivityDetailsPar
|
||||
final short pace = buf.getShort(); // sec/km
|
||||
|
||||
activityPointBuilder.setCadence(cadence);
|
||||
activityPointBuilder.setStride(stride * 10.0f);
|
||||
activityPointBuilder.setStepLength(stride / 2.0f * 10.0f); // stride cm -> step mm
|
||||
if (pace != 0) {
|
||||
activityPointBuilder.setSpeed(1000f / pace); // s/km -> m/s
|
||||
}
|
||||
|
||||
@@ -339,6 +339,17 @@ public class DateTimeUtils {
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/// format duration day - milli seconds with max 3 units shown
|
||||
public static String formatSportsDuration(long duration, TimeUnit unit) {
|
||||
DurationFormatter df = DurationFormatter.Builder.SYMBOLS
|
||||
.maximum(TimeUnit.HOURS)
|
||||
.minimum(TimeUnit.MILLISECONDS)
|
||||
.suppressZeros(DurationFormatter.SuppressZeros.LEADING, DurationFormatter.SuppressZeros.TRAILING)
|
||||
.maximumAmountOfUnitsToShow(3)
|
||||
.build();
|
||||
return df.format(duration, unit);
|
||||
}
|
||||
|
||||
/// number of seconds since UTC epoch of 1970-01-01T00:00:00Z
|
||||
public static long getEpochSeconds() {
|
||||
final long epoc;
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ internal object WorkoutSyncerUtils {
|
||||
ActivityKind.FITNESS_EQUIPMENT, ActivityKind.INDOOR_FITNESS, ActivityKind.SOMATOSENSORY_GAME, ActivityKind.VIDEO_GAMING,
|
||||
ActivityKind.HEALTH_SNAPSHOT, ActivityKind.TACTICAL, ActivityKind.GRINDING, ActivityKind.WINTER_SPORT, ActivityKind.TEAM_SPORT,
|
||||
ActivityKind.SNOW_SPORTS, ActivityKind.DISC_SPORTS, ActivityKind.OTHER_WATER_SPORTS, ActivityKind.OTHER_WINTER_SPORTS,
|
||||
ActivityKind.MARINE, ActivityKind.PARA_SPORT, ActivityKind.RACKET, ActivityKind.NAVIGATE, ActivityKind.INDOOR_TRACK,
|
||||
ActivityKind.MARINE, ActivityKind.PARA_SPORT, ActivityKind.RACKET, ActivityKind.NAVIGATE, ActivityKind.INDOOR_TRACK_RUNNING,
|
||||
ActivityKind.TRANSITION, ActivityKind.VIVOMOVE_HR_TRANSITION,
|
||||
ActivityKind.FLYING, ActivityKind.MOTORCYCLING, ActivityKind.BOATING, ActivityKind.DRIVING, ActivityKind.HUNTING, ActivityKind.FISHING,
|
||||
ActivityKind.AUTO_RACING, ActivityKind.SNOWMOBILING, ActivityKind.KARTING, ActivityKind.BILLIARDS, ActivityKind.BOWLING,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:autoMirrored="true">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M520,720L520,577L475,528L454,634L304,603L312,564L423,587L462,391L400,414L400,480L360,480L360,386L491,338Q506,333 520.5,339Q535,345 542,359Q567,410 597.5,425Q628,440 640,440L640,480Q619,480 585,463.5Q551,447 524,410L507,504L560,561L560,720L520,720ZM540,320Q523,320 511.5,308.5Q500,297 500,280Q500,263 511.5,251.5Q523,240 540,240Q557,240 568.5,251.5Q580,263 580,280Q580,297 568.5,308.5Q557,320 540,320ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Z"/>
|
||||
</vector>
|
||||
@@ -1059,9 +1059,9 @@
|
||||
<string name="stride">Stride</string>
|
||||
<string name="stamina">Stamina</string>
|
||||
<string name="step_length">Step Length</string>
|
||||
<string name="diving_cns_toxicity">CNS toxicity</string>
|
||||
<string name="diving_nitrogen_load">Nitrogen load</string>
|
||||
<string name="diving_oxygen_toxicity">Oxygen toxicity</string>
|
||||
<string name="diving_cns_toxicity">CNS Toxicity</string>
|
||||
<string name="diving_nitrogen_load">Nitrogen Load</string>
|
||||
<string name="diving_oxygen_toxicity">Oxygen Toxicity</string>
|
||||
<string name="unit_oxygen_toxicity_units">OTU</string>
|
||||
<string name="diving_surface_interval">Surface Interval</string>
|
||||
<string name="clock">Clock</string>
|
||||
@@ -2726,8 +2726,11 @@
|
||||
<string name="battery_status_new">New</string>
|
||||
<string name="battery_status_ok">Ok</string>
|
||||
<string name="battery_status_unknown">Unknown</string>
|
||||
<string name="body_energy_at_end">Finishing body energy</string>
|
||||
<string name="body_energy_at_start">Initial body energy</string>
|
||||
<string name="body_energy_at_end">Finishing Body Energy</string>
|
||||
<string name="body_energy_at_start">Initial Body Energy</string>
|
||||
<string name="stamina_at_end">Finishing Stamina</string>
|
||||
<string name="stamina_at_start">Initial Stamina</string>
|
||||
<string name="stamina_min">Minimum Stamina</string>
|
||||
<string name="device">Device</string>
|
||||
<string name="diving_pressure_sac_avg">Surface air consumption</string>
|
||||
<string name="hr_user_max">User Max Heart Rate</string>
|
||||
|
||||
+30
@@ -1,5 +1,21 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.enums;
|
||||
|
||||
/* Copyright (C) 2024-2026 José Rebelo, Thomas Kuehne
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.util.Pair;
|
||||
@@ -7,8 +23,10 @@ import android.util.Pair;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.test.TestBase;
|
||||
|
||||
public class GarminSportTest extends TestBase {
|
||||
@@ -28,4 +46,16 @@ public class GarminSportTest extends TestBase {
|
||||
|
||||
assertTrue("Duplicated sport codes: " + duplicates, duplicates.isEmpty());
|
||||
}
|
||||
|
||||
// Ensure that every GarminSport has a subsport=0 fallback
|
||||
@Test
|
||||
public void testFallbackSport() {
|
||||
int defaultIcon = ActivityKind.ACTIVITY.getIcon();
|
||||
for (final GarminSport sport : GarminSport.values()) {
|
||||
if (sport.getSubtype() != 0) {
|
||||
Optional<GarminSport> fallback = GarminSport.fromCodes(sport.getType(), 0);
|
||||
assertTrue(sport.name() + " has no fallback", fallback.isPresent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user