mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
igpsport: Use GarminWorkoutParser
This commit is contained in:
committed by
José Rebelo
parent
d3103400fc
commit
5216752665
+2
-1
@@ -27,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminWorkoutParser;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||||
@@ -52,7 +53,7 @@ public abstract class IGPSportAbstractCoordinator extends AbstractBLEDeviceCoord
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public ActivitySummaryParser getActivitySummaryParser(final GBDevice device, final Context context) {
|
public ActivitySummaryParser getActivitySummaryParser(final GBDevice device, final Context context) {
|
||||||
return new IGPSportWorkoutParser(context);
|
return new GarminWorkoutParser(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
-780
@@ -1,780 +0,0 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.devices.igpsport;
|
|
||||||
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.ACTIVE_SECONDS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.ASCENT_DISTANCE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_CADENCE_SEATING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_CADENCE_STANDING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_DEPTH;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_GROUND_CONTACT_TIME;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_GROUND_CONTACT_TIME_BALANCE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_LEFT_PCO;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_LEFT_POWER_PHASE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_LEFT_POWER_PHASE_PEAK;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_PEDAL_SMOOTHNESS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_POWER;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_POWER_SEATING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_POWER_STANDING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_RIGHT_PCO;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_RIGHT_POWER_PHASE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_RIGHT_POWER_PHASE_PEAK;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_TORQUE_EFFECTIVENESS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_VERTICAL_OSCILLATION;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.AVG_VERTICAL_RATIO;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.BOTTOM_TIME;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.CALORIES_ACTIVE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.CALORIES_BURNT;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.CALORIES_RESTING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.DESCENT_DISTANCE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.DISTANCE_METERS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.DIVE_NUMBER;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.END_CNS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.END_N2;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.ESTIMATED_SWEAT_LOSS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.FRONT_GEAR_SHIFTS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.GAS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.GROUP_INTERVALS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HRV_RMSSD;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HRV_SDRR;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_AVG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_MAX;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_AEROBIC;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_EASY;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_MAXIMUM;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_NA;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_THRESHOLD;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_WARM_UP;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.INTENSITY_FACTOR;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.INTERNAL_HAS_GPS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.LACTATE_THRESHOLD_HR;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.LEFT_RIGHT_BALANCE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAXIMUM_OXYGEN_UPTAKE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAX_CADENCE_SEATING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAX_CADENCE_STANDING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAX_DEPTH;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAX_POWER;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAX_POWER_SEATING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAX_POWER_STANDING;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.NORMALIZED_POWER;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.PACE_AVG_SECONDS_KM;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.PACE_MAX;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.POOL_LENGTH;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.REAR_GEAR_SHIFTS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.RECOVERY_TIME;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.RESPIRATION_AVG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.RESPIRATION_MAX;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.RESPIRATION_MIN;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.SETS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.SPEED_AVG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.SPEED_MAX;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.SPO2_AVG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.STANDING_COUNT;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.STANDING_TIME;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.START_CNS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.START_N2;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.STEP_LENGTH_AVG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.STRESS_AVG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.SWIM_AVG_CADENCE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.SWOLF_AVG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TRAINING_EFFECT_AEROBIC;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TRAINING_EFFECT_ANAEROBIC;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TRAINING_LOAD;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TRAINING_STRESS_SCORE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BPM;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BREATHS_PER_MIN;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KCAL;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KMPH;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_LB;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MILLISECONDS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_ML;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_ML_KG_MIN;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_MM;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_NONE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_PERCENTAGE;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_RPM;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_STROKES_PER_LENGTH;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_WATT;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryProgressEntry;
|
|
||||||
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.ActivitySummaryParser;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitFile;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.enums.GarminSport;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.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.FitDiveGas;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitDiveSummary;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitLap;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitPhysiologicalMetrics;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRecord;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSet;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSport;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitTimeInZone;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitUserProfile;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
|
||||||
|
|
||||||
public class IGPSportWorkoutParser implements ActivitySummaryParser {
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(IGPSportWorkoutParser.class);
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
private final List<FitTimeInZone> timesInZone = new ArrayList<>();
|
|
||||||
private final List<ActivityPoint> activityPoints = new ArrayList<>();
|
|
||||||
private FitSession session = null;
|
|
||||||
private FitSport sport = null;
|
|
||||||
private FitUserProfile userProfile = null;
|
|
||||||
private FitPhysiologicalMetrics physiologicalMetrics = null;
|
|
||||||
private FitDiveSummary diveSummary = null;
|
|
||||||
private final List<FitDiveGas> diveGases = new ArrayList<>();
|
|
||||||
private final List<FitSet> sets = new ArrayList<>();
|
|
||||||
private final List<FitLap> laps = new ArrayList<>();
|
|
||||||
|
|
||||||
public IGPSportWorkoutParser(final Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseActivitySummary parseBinaryData(final BaseActivitySummary summary, final boolean forDetails) {
|
|
||||||
if (!forDetails) {
|
|
||||||
// Our parsing is too slow, especially without a RecyclerView
|
|
||||||
return summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
final long nanoStart = System.nanoTime();
|
|
||||||
|
|
||||||
reset();
|
|
||||||
|
|
||||||
final String rawDetailsPath = summary.getRawDetailsPath();
|
|
||||||
if (rawDetailsPath == null) {
|
|
||||||
LOG.warn("No rawDetailsPath");
|
|
||||||
return summary;
|
|
||||||
}
|
|
||||||
final File file = FileUtils.tryFixPath(new File(rawDetailsPath));
|
|
||||||
if (file == null || !file.isFile() || !file.canRead()) {
|
|
||||||
LOG.warn("Unable to read {}", rawDetailsPath);
|
|
||||||
return summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
final FitFile fitFile;
|
|
||||||
try {
|
|
||||||
fitFile = FitFile.parseIncoming(file);
|
|
||||||
} catch (final IOException | FitParseException e) {
|
|
||||||
LOG.error("Failed to parse fit file", e);
|
|
||||||
return summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final RecordData record : fitFile.getRecords()) {
|
|
||||||
handleRecord(record);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSummary(summary);
|
|
||||||
|
|
||||||
final long nanoEnd = System.nanoTime();
|
|
||||||
final long executionTime = (nanoEnd - nanoStart) / 1000000;
|
|
||||||
LOG.trace("Updating summary took {}ms", executionTime);
|
|
||||||
|
|
||||||
return summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
timesInZone.clear();
|
|
||||||
activityPoints.clear();
|
|
||||||
session = null;
|
|
||||||
sport = null;
|
|
||||||
userProfile = null;
|
|
||||||
physiologicalMetrics = null;
|
|
||||||
diveSummary = null;
|
|
||||||
diveGases.clear();
|
|
||||||
sets.clear();
|
|
||||||
laps.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean handleRecord(final RecordData record) {
|
|
||||||
if (record instanceof FitRecord) {
|
|
||||||
activityPoints.add(((FitRecord) record).toActivityPoint());
|
|
||||||
} else if (record instanceof FitSession) {
|
|
||||||
LOG.debug("Session: {}", record);
|
|
||||||
if (session != null) {
|
|
||||||
LOG.warn("Got multiple sessions - NOT SUPPORTED: {}", record);
|
|
||||||
} else {
|
|
||||||
// We only support 1 session
|
|
||||||
session = (FitSession) record;
|
|
||||||
}
|
|
||||||
} else if (record instanceof FitPhysiologicalMetrics) {
|
|
||||||
LOG.debug("Physiological Metrics: {}", record);
|
|
||||||
physiologicalMetrics = (FitPhysiologicalMetrics) record;
|
|
||||||
} else if (record instanceof FitSport) {
|
|
||||||
LOG.debug("Sport: {}", record);
|
|
||||||
if (sport != null) {
|
|
||||||
LOG.warn("Got multiple sports - NOT SUPPORTED: {}", record);
|
|
||||||
} else {
|
|
||||||
// We only support 1 sport
|
|
||||||
sport = (FitSport) record;
|
|
||||||
}
|
|
||||||
} else if (record instanceof FitTimeInZone) {
|
|
||||||
LOG.trace("Time in zone: {}", record);
|
|
||||||
timesInZone.add((FitTimeInZone) record);
|
|
||||||
} else if (record instanceof FitSet) {
|
|
||||||
LOG.trace("Set: {}", record);
|
|
||||||
sets.add((FitSet) record);
|
|
||||||
} else if (record instanceof FitLap) {
|
|
||||||
LOG.trace("Lap: {}", record);
|
|
||||||
laps.add((FitLap) record);
|
|
||||||
} else if (record instanceof FitUserProfile) {
|
|
||||||
LOG.trace("User Profile: {}", record);
|
|
||||||
if (userProfile != null) {
|
|
||||||
LOG.warn("Got multiple user profiles - NOT SUPPORTED: {}", record);
|
|
||||||
} else {
|
|
||||||
// We only support 1 user profile
|
|
||||||
userProfile = (FitUserProfile) record;
|
|
||||||
}
|
|
||||||
} else if (record instanceof FitDiveSummary) {
|
|
||||||
LOG.trace("Dive summary: {}", record);
|
|
||||||
if (diveSummary != null) {
|
|
||||||
return false; // for some reason there is more than one message, in my activities the first one contains all data
|
|
||||||
}
|
|
||||||
diveSummary = (FitDiveSummary) record;
|
|
||||||
} else if (record instanceof FitDiveGas) {
|
|
||||||
LOG.trace("Dive gas: {}", record);
|
|
||||||
diveGases.add((FitDiveGas) record);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateSummary(final BaseActivitySummary summary) {
|
|
||||||
if (session == null) {
|
|
||||||
LOG.error("Got workout, but no session");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ActivitySummaryData summaryData = new ActivitySummaryData();
|
|
||||||
|
|
||||||
final ActivityKind activityKind;
|
|
||||||
if (sport != null) {
|
|
||||||
if (StringUtils.isNullOrEmpty(summary.getName())) {
|
|
||||||
summary.setName(sport.getName());
|
|
||||||
}
|
|
||||||
activityKind = getActivityKind(sport.getSport(), sport.getSubSport());
|
|
||||||
} else {
|
|
||||||
activityKind = getActivityKind(session.getSport(), session.getSubSport());
|
|
||||||
}
|
|
||||||
final ActivityKind.CycleUnit cycleUnit = ActivityKind.getCycleUnit(activityKind);
|
|
||||||
|
|
||||||
final String weightUnit;
|
|
||||||
if (userProfile != null && userProfile.getWeightSetting() != null) {
|
|
||||||
weightUnit = FieldDefinitionMeasurementSystem.Type.metric.equals(userProfile.getWeightSetting()) ? UNIT_KG : UNIT_LB;
|
|
||||||
} else {
|
|
||||||
weightUnit = UNIT_KG;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary.setActivityKind(activityKind.getCode());
|
|
||||||
|
|
||||||
if (session.getTotalElapsedTime() != null) {
|
|
||||||
summary.setEndTime(new Date(summary.getStartTime().getTime() + session.getTotalElapsedTime().intValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session.getTotalTimerTime() != null) {
|
|
||||||
summaryData.add(ACTIVE_SECONDS, session.getTotalTimerTime() / 1000f, UNIT_SECONDS);
|
|
||||||
}
|
|
||||||
if (session.getTotalDistance() != null) {
|
|
||||||
summaryData.add(DISTANCE_METERS, session.getTotalDistance() / 100f, UNIT_METERS);
|
|
||||||
}
|
|
||||||
if (session.getPoolLength() != null) {
|
|
||||||
summaryData.add(POOL_LENGTH, session.getPoolLength(), UNIT_METERS);
|
|
||||||
}
|
|
||||||
if (session.getAvgSwolf() != null) {
|
|
||||||
summaryData.add(SWOLF_AVG, session.getAvgSwolf(), UNIT_NONE);
|
|
||||||
}
|
|
||||||
if (session.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (session.getAverageHeartRate() != null) {
|
|
||||||
summaryData.add(HR_AVG, session.getAverageHeartRate(), UNIT_BPM);
|
|
||||||
}
|
|
||||||
if (session.getMaxHeartRate() != null) {
|
|
||||||
summaryData.add(HR_MAX, session.getMaxHeartRate(), UNIT_BPM);
|
|
||||||
}
|
|
||||||
if (session.getHrvSdrr() != null) {
|
|
||||||
summaryData.add(HRV_SDRR, session.getHrvSdrr(), UNIT_MILLISECONDS);
|
|
||||||
}
|
|
||||||
if (session.getHrvRmssd() != null) {
|
|
||||||
summaryData.add(HRV_RMSSD, session.getHrvRmssd(), UNIT_MILLISECONDS);
|
|
||||||
}
|
|
||||||
if (session.getAvgSpo2() != null) {
|
|
||||||
summaryData.add(SPO2_AVG, session.getAvgSpo2(), UNIT_PERCENTAGE);
|
|
||||||
}
|
|
||||||
if (session.getEnhancedAvgRespirationRate() != null) {
|
|
||||||
summaryData.add(RESPIRATION_AVG, session.getEnhancedAvgRespirationRate(), UNIT_BREATHS_PER_MIN);
|
|
||||||
}
|
|
||||||
if (session.getEnhancedMaxRespirationRate() != null) {
|
|
||||||
summaryData.add(RESPIRATION_MAX, session.getEnhancedMaxRespirationRate(), UNIT_BREATHS_PER_MIN);
|
|
||||||
}
|
|
||||||
if (session.getEnhancedMinRespirationRate() != null) {
|
|
||||||
summaryData.add(RESPIRATION_MIN, session.getEnhancedMinRespirationRate(), UNIT_BREATHS_PER_MIN);
|
|
||||||
}
|
|
||||||
if (session.getAvgStress() != null) {
|
|
||||||
summaryData.add(STRESS_AVG, session.getAvgStress(), UNIT_NONE);
|
|
||||||
}
|
|
||||||
if (session.getAvgCadence() != null) {
|
|
||||||
if (cycleUnit == ActivityKind.CycleUnit.STEPS) {
|
|
||||||
summaryData.addCadenceAvg(session.getAvgCadence() * 2, cycleUnit);
|
|
||||||
} else {
|
|
||||||
// FIXME some of the rest might also need adjusting...
|
|
||||||
summaryData.addCadenceAvg(session.getAvgCadence(), cycleUnit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (session.getMaxCadence() != null) {
|
|
||||||
if (cycleUnit == ActivityKind.CycleUnit.STEPS) {
|
|
||||||
summaryData.addCadenceMax(session.getMaxCadence() * 2, cycleUnit);
|
|
||||||
} else {
|
|
||||||
// FIXME some of the rest might also need adjusting...
|
|
||||||
summaryData.addCadenceMax(session.getMaxCadence(), cycleUnit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (session.getTotalAscent() != null && !ActivityKind.isDiving(activityKind)) {
|
|
||||||
summaryData.add(ASCENT_DISTANCE, session.getTotalAscent(), UNIT_METERS);
|
|
||||||
}
|
|
||||||
if (session.getTotalDescent() != null && !ActivityKind.isDiving(activityKind)) {
|
|
||||||
summaryData.add(DESCENT_DISTANCE, session.getTotalDescent(), UNIT_METERS);
|
|
||||||
}
|
|
||||||
if (session.getAvgSwimCadence() != null) {
|
|
||||||
summaryData.add(SWIM_AVG_CADENCE, session.getAvgSwimCadence(), UNIT_STROKES_PER_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session.getEnhancedAvgSpeed() != null) {
|
|
||||||
if (ActivityKind.isPaceActivity(activityKind)) {
|
|
||||||
summaryData.add(PACE_AVG_SECONDS_KM, Math.round((60 / (session.getEnhancedAvgSpeed() * 3.6)) * 60), UNIT_SECONDS);
|
|
||||||
} else {
|
|
||||||
summaryData.add(SPEED_AVG, Math.round((session.getEnhancedAvgSpeed() * 3600 / 1000) * 100.0) / 100.0, UNIT_KMPH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session.getEnhancedMaxSpeed() != null) {
|
|
||||||
if (ActivityKind.isPaceActivity(activityKind)) {
|
|
||||||
summaryData.add(PACE_MAX, Math.round((60 / (session.getEnhancedMaxSpeed() * 3.6)) * 60), UNIT_SECONDS);
|
|
||||||
} else if (ActivityKind.isDiving(activityKind)) { // Hide average speed for diving activities
|
|
||||||
} else {
|
|
||||||
summaryData.add(SPEED_MAX, Math.round((session.getEnhancedMaxSpeed() * 3600 / 1000) * 100.0) / 100.0, UNIT_KMPH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
summaryData.add(TRAINING_LOAD, safeRound(session.getTrainingLoadPeak()), UNIT_NONE);
|
|
||||||
summaryData.add(AVG_POWER, session.getAvgPower(), UNIT_WATT);
|
|
||||||
summaryData.add(MAX_POWER, session.getMaxPower(), UNIT_WATT);
|
|
||||||
summaryData.add(NORMALIZED_POWER, session.getNormalizedPower(), UNIT_WATT);
|
|
||||||
|
|
||||||
if (session.getStandTime() != null) {
|
|
||||||
summaryData.add(STANDING_TIME, session.getStandTime() / 1000, 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);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
summaryData.add(
|
|
||||||
AVG_GROUND_CONTACT_TIME_BALANCE,
|
|
||||||
context.getString(
|
|
||||||
R.string.range_percentage_float,
|
|
||||||
session.getAvgStanceTimeBalance(),
|
|
||||||
100f - session.getAvgStanceTimeBalance()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] avgLeftPowerPhase = session.getAvgLeftPowerPhase();
|
|
||||||
if (avgLeftPowerPhase != null && avgLeftPowerPhase.length == 4) {
|
|
||||||
final Number startAngle = avgLeftPowerPhase[0];
|
|
||||||
final Number endAngle = avgLeftPowerPhase[1];
|
|
||||||
if (startAngle != null && endAngle != null) {
|
|
||||||
summaryData.add(
|
|
||||||
AVG_LEFT_POWER_PHASE,
|
|
||||||
context.getString(
|
|
||||||
R.string.range_degrees,
|
|
||||||
Math.round(startAngle.floatValue() / 0.7111111),
|
|
||||||
Math.round(endAngle.floatValue() / 0.7111111)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] avgRightPowerPhase = session.getAvgRightPowerPhase();
|
|
||||||
if (avgRightPowerPhase != null && avgRightPowerPhase.length == 4) {
|
|
||||||
final Number startAngle = avgRightPowerPhase[0];
|
|
||||||
final Number endAngle = avgRightPowerPhase[1];
|
|
||||||
if (startAngle != null && endAngle != null) {
|
|
||||||
summaryData.add(
|
|
||||||
AVG_RIGHT_POWER_PHASE,
|
|
||||||
context.getString(
|
|
||||||
R.string.range_degrees,
|
|
||||||
Math.round(startAngle.floatValue() / 0.7111111),
|
|
||||||
Math.round(endAngle.floatValue() / 0.7111111)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] avgLeftPowerPhasePeak = session.getAvgLeftPowerPhasePeak();
|
|
||||||
if (avgLeftPowerPhasePeak != null && avgLeftPowerPhasePeak.length == 4) {
|
|
||||||
final Number startAngle = avgLeftPowerPhasePeak[0];
|
|
||||||
final Number endAngle = avgLeftPowerPhasePeak[1];
|
|
||||||
if (startAngle != null && endAngle != null) {
|
|
||||||
summaryData.add(
|
|
||||||
AVG_LEFT_POWER_PHASE_PEAK,
|
|
||||||
context.getString(
|
|
||||||
R.string.range_degrees,
|
|
||||||
Math.round(startAngle.floatValue() / 0.7111111),
|
|
||||||
Math.round(endAngle.floatValue() / 0.7111111)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] avgRightPowerPhasePeak = session.getAvgRightPowerPhasePeak();
|
|
||||||
if (avgRightPowerPhasePeak != null && avgRightPowerPhasePeak.length == 4) {
|
|
||||||
final Number startAngle = avgRightPowerPhasePeak[0];
|
|
||||||
final Number endAngle = avgRightPowerPhasePeak[1];
|
|
||||||
if (startAngle != null && endAngle != null) {
|
|
||||||
summaryData.add(
|
|
||||||
AVG_RIGHT_POWER_PHASE_PEAK,
|
|
||||||
context.getString(
|
|
||||||
R.string.range_degrees,
|
|
||||||
Math.round(startAngle.floatValue() / 0.7111111),
|
|
||||||
Math.round(endAngle.floatValue() / 0.7111111)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] avgPowerPosition = session.getAvgPowerPosition();
|
|
||||||
if (avgPowerPosition != null && avgPowerPosition.length == 2) {
|
|
||||||
summaryData.add(AVG_POWER_SEATING, avgPowerPosition[0], UNIT_WATT);
|
|
||||||
summaryData.add(AVG_POWER_STANDING, avgPowerPosition[1], UNIT_WATT);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] maxPowerPosition = session.getMaxPowerPosition();
|
|
||||||
if (maxPowerPosition != null && maxPowerPosition.length == 2) {
|
|
||||||
summaryData.add(MAX_POWER_SEATING, maxPowerPosition[0], UNIT_WATT);
|
|
||||||
summaryData.add(MAX_POWER_STANDING, maxPowerPosition[1], UNIT_WATT);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] avgCadencePosition = session.getAvgCadencePosition();
|
|
||||||
if (avgCadencePosition != null && avgCadencePosition.length == 2) {
|
|
||||||
summaryData.add(AVG_CADENCE_SEATING, avgCadencePosition[0], UNIT_RPM);
|
|
||||||
summaryData.add(AVG_CADENCE_STANDING, avgCadencePosition[1], UNIT_RPM);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Number[] maxCadencePosition = session.getMaxCadencePosition();
|
|
||||||
if (maxCadencePosition != null && maxCadencePosition.length == 2) {
|
|
||||||
summaryData.add(MAX_CADENCE_SEATING, maxCadencePosition[0], UNIT_RPM);
|
|
||||||
summaryData.add(MAX_CADENCE_STANDING, maxCadencePosition[1], UNIT_RPM);
|
|
||||||
}
|
|
||||||
|
|
||||||
summaryData.add(FRONT_GEAR_SHIFTS, session.getFrontShifts(), UNIT_NONE);
|
|
||||||
summaryData.add(REAR_GEAR_SHIFTS, session.getRearShifts(), UNIT_NONE);
|
|
||||||
|
|
||||||
final Integer balance = session.getLeftRightBalance();
|
|
||||||
if (balance != null) {
|
|
||||||
final float balancePercentage = (balance & 0x3fff) / 100f;
|
|
||||||
final boolean isRight = (balance & 0x8000) != 0;
|
|
||||||
final float balanceL;
|
|
||||||
final float balanceR;
|
|
||||||
if (isRight) {
|
|
||||||
balanceL = 100f - balancePercentage;
|
|
||||||
balanceR = balancePercentage;
|
|
||||||
} else {
|
|
||||||
balanceL = balancePercentage;
|
|
||||||
balanceR = 100f - balancePercentage;
|
|
||||||
}
|
|
||||||
summaryData.add(
|
|
||||||
LEFT_RIGHT_BALANCE,
|
|
||||||
context.getString(
|
|
||||||
R.string.range_percentage_float,
|
|
||||||
balanceL,
|
|
||||||
balanceR
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Float avgLeftPedalSmoothness = session.getAvgLeftPedalSmoothness();
|
|
||||||
final Float avgRightPedalSmoothness = session.getAvgRightPedalSmoothness();
|
|
||||||
if (avgLeftPedalSmoothness != null && avgRightPedalSmoothness != null) {
|
|
||||||
summaryData.add(
|
|
||||||
AVG_PEDAL_SMOOTHNESS,
|
|
||||||
context.getString(R.string.range_percentage, Math.round(avgLeftPedalSmoothness), Math.round(avgRightPedalSmoothness))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Float avgLeftTorqueEffectiveness = session.getAvgLeftTorqueEffectiveness();
|
|
||||||
final Float avgRightTorqueEffectiveness = session.getAvgRightTorqueEffectiveness();
|
|
||||||
if (avgLeftTorqueEffectiveness != null && avgRightTorqueEffectiveness != null) {
|
|
||||||
summaryData.add(
|
|
||||||
AVG_TORQUE_EFFECTIVENESS,
|
|
||||||
context.getString(R.string.range_percentage, Math.round(avgLeftTorqueEffectiveness), Math.round(avgRightTorqueEffectiveness))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final FitTimeInZone fitTimeInZone : timesInZone) {
|
|
||||||
// Find the first time in zone for the session (assumes single-session)
|
|
||||||
if (fitTimeInZone.getReferenceMessage() != null && fitTimeInZone.getReferenceMessage() == 18) {
|
|
||||||
final Double[] timeInZones = fitTimeInZone.getTimeInZone();
|
|
||||||
if (timeInZones == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final double totalTime = Arrays.stream(timeInZones).mapToDouble(Number::doubleValue).sum();
|
|
||||||
if (totalTime == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (timeInZones[0] != null && timeInZones[0] == totalTime) {
|
|
||||||
// The total time is N/A, so do not add the section
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final List<String> zoneOrder = Arrays.asList(HR_ZONE_NA, HR_ZONE_WARM_UP, HR_ZONE_EASY, HR_ZONE_AEROBIC, HR_ZONE_THRESHOLD, HR_ZONE_MAXIMUM);
|
|
||||||
final int[] zoneColors = new int[]{
|
|
||||||
0,
|
|
||||||
context.getResources().getColor(R.color.hr_zone_warm_up_color),
|
|
||||||
context.getResources().getColor(R.color.hr_zone_easy_color),
|
|
||||||
context.getResources().getColor(R.color.hr_zone_aerobic_color),
|
|
||||||
context.getResources().getColor(R.color.hr_zone_threshold_color),
|
|
||||||
context.getResources().getColor(R.color.hr_zone_maximum_color),
|
|
||||||
};
|
|
||||||
for (int i = 0; i < zoneOrder.size(); i++) {
|
|
||||||
double timeInZone = timeInZones[i] != null ? Math.rint(timeInZones[i]) : 0;
|
|
||||||
summaryData.add(
|
|
||||||
zoneOrder.get(i),
|
|
||||||
new ActivitySummaryProgressEntry(
|
|
||||||
timeInZone,
|
|
||||||
UNIT_SECONDS,
|
|
||||||
(int) (100 * timeInZone / totalTime),
|
|
||||||
zoneColors[i]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (physiologicalMetrics.getRecoveryTime() != null) {
|
|
||||||
summaryData.add(RECOVERY_TIME, physiologicalMetrics.getRecoveryTime() * 60, UNIT_SECONDS);
|
|
||||||
}
|
|
||||||
if (physiologicalMetrics.getLactateThresholdHeartRate() != null) {
|
|
||||||
summaryData.add(LACTATE_THRESHOLD_HR, physiologicalMetrics.getLactateThresholdHeartRate(), UNIT_BPM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diveSummary != null) {
|
|
||||||
if (diveSummary.getAvgDepth() != null) {
|
|
||||||
summaryData.add(AVG_DEPTH, diveSummary.getAvgDepth(), UNIT_METERS);
|
|
||||||
}
|
|
||||||
if (diveSummary.getMaxDepth() != null) {
|
|
||||||
summaryData.add(MAX_DEPTH, diveSummary.getMaxDepth(), UNIT_METERS);
|
|
||||||
}
|
|
||||||
if (diveSummary.getStartCns() != null) {
|
|
||||||
summaryData.add(START_CNS, diveSummary.getStartCns(), UNIT_PERCENTAGE);
|
|
||||||
}
|
|
||||||
if (diveSummary.getEndCns() != null) {
|
|
||||||
summaryData.add(END_CNS, diveSummary.getEndCns(), UNIT_PERCENTAGE);
|
|
||||||
}
|
|
||||||
if (diveSummary.getStartN2() != null) {
|
|
||||||
summaryData.add(START_N2, diveSummary.getStartN2(), UNIT_PERCENTAGE);
|
|
||||||
}
|
|
||||||
if (diveSummary.getEndN2() != null) {
|
|
||||||
summaryData.add(END_N2, diveSummary.getEndN2(), UNIT_PERCENTAGE);
|
|
||||||
}
|
|
||||||
if (diveSummary.getDiveNumber() != null) {
|
|
||||||
summaryData.add(DIVE_NUMBER, diveSummary.getDiveNumber(), UNIT_NONE);
|
|
||||||
}
|
|
||||||
if (diveSummary.getBottomTime() != null) {
|
|
||||||
summaryData.add(BOTTOM_TIME, diveSummary.getBottomTime(), UNIT_SECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
summaryData.add(TRAINING_LOAD, safeRound(session.getTrainingLoadPeak()), UNIT_NONE);
|
|
||||||
summaryData.add(INTENSITY_FACTOR, session.getIntensityFactor(), UNIT_NONE);
|
|
||||||
summaryData.add(TRAINING_STRESS_SCORE, session.getTrainingStressScore(), UNIT_NONE);
|
|
||||||
|
|
||||||
if (!diveGases.isEmpty()) {
|
|
||||||
final ActivitySummaryTableBuilder tableBuilder = new ActivitySummaryTableBuilder(GAS, "gases_header", Arrays.asList(
|
|
||||||
"diving_gas",
|
|
||||||
"helium_content",
|
|
||||||
"oxygen_content",
|
|
||||||
"nitrogen_content"
|
|
||||||
));
|
|
||||||
|
|
||||||
int i = 1;
|
|
||||||
for (final FitDiveGas gas : diveGases) {
|
|
||||||
int helium = gas.getHeliumContent() != null ? gas.getHeliumContent(): 0;
|
|
||||||
int oxygen = gas.getOxygenContent() != null ? gas.getOxygenContent(): 0;
|
|
||||||
int nitrogen = 100 - helium - oxygen;
|
|
||||||
tableBuilder.addRow(
|
|
||||||
"gas_" + i,
|
|
||||||
Arrays.asList(
|
|
||||||
new ActivitySummaryValue(i, UNIT_NONE),
|
|
||||||
new ActivitySummaryValue(helium, UNIT_PERCENTAGE),
|
|
||||||
new ActivitySummaryValue(oxygen, UNIT_PERCENTAGE),
|
|
||||||
new ActivitySummaryValue(nitrogen, UNIT_PERCENTAGE)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
tableBuilder.addToSummaryData(summaryData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sets.isEmpty()) {
|
|
||||||
final ActivitySummaryTableBuilder tableBuilder = new ActivitySummaryTableBuilder(SETS, "sets_header", Arrays.asList(
|
|
||||||
"set",
|
|
||||||
"category",
|
|
||||||
"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) {
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tableBuilder.addToSummaryData(summaryData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(
|
|
||||||
INTERNAL_HAS_GPS,
|
|
||||||
String.valueOf(activityPoints.stream().anyMatch(p -> p.getLocation() != null))
|
|
||||||
);
|
|
||||||
|
|
||||||
summary.setSummaryData(summaryData.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Number safeRound(final Number number) {
|
|
||||||
if (number == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(number.doubleValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ActivityKind getActivityKind(final Integer sport, final Integer subsport) {
|
|
||||||
final Optional<GarminSport> garminSport = GarminSport.fromCodes(sport, subsport);
|
|
||||||
if (garminSport.isPresent()) {
|
|
||||||
return garminSport.get().getActivityKind();
|
|
||||||
} else {
|
|
||||||
LOG.warn("Unknown garmin sport {}/{}", sport, subsport);
|
|
||||||
|
|
||||||
final Optional<GarminSport> optGarminSportFallback = GarminSport.fromCodes(sport, 0);
|
|
||||||
if (!optGarminSportFallback.isEmpty()) {
|
|
||||||
return optGarminSportFallback.get().getActivityKind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ActivityKind.UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user