Extract hardcoded activity summary entries to constants

This commit is contained in:
José Rebelo 2024-01-06 13:54:06 +00:00
parent 4e54f8137d
commit ab894ae433
7 changed files with 381 additions and 200 deletions

View File

@ -17,6 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.*;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Context;
@ -86,7 +88,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryJsonSummary;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.SwipeEvents;
@ -464,13 +465,13 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
if (!show_raw_data) {
//special casing here + imperial units handling
switch (unit) {
case "cm":
case UNIT_CM:
if (units.equals(UNIT_IMPERIAL)) {
value = value * 0.0328084;
unit = "ft";
}
break;
case "meters_second":
case UNIT_METERS_PER_SECOND:
if (units.equals(UNIT_IMPERIAL)) {
value = value * 2.236936D;
unit = "mi_h";
@ -479,7 +480,7 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
unit = "km_h";
}
break;
case "seconds_m":
case UNIT_SECONDS_PER_M:
if (units.equals(UNIT_IMPERIAL)) {
value = value * (1609.344 / 60D);
unit = "minutes_mi";
@ -488,7 +489,7 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
unit = "minutes_km";
}
break;
case "seconds_km":
case UNIT_SECONDS_PER_KM:
if (units.equals(UNIT_IMPERIAL)) {
value = value / 60D * 1.609344;
unit = "minutes_mi";
@ -497,7 +498,7 @@ public class ActivitySummaryDetail extends AbstractGBActivity {
unit = "minutes_km";
}
break;
case "meters":
case UNIT_METERS:
if (units.equals(UNIT_IMPERIAL)) {
value = value * 3.28084D;
unit = "ft";

View File

@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.*;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.commons.lang3.ArrayUtils;
@ -73,7 +75,7 @@ public class Huami2021ActivitySummaryParser extends HuamiActivitySummaryParser {
if (summaryProto.hasTime()) {
int totalDuration = summaryProto.getTime().getTotalDuration();
summary.setEndTime(new Date(startTime.getTime() + totalDuration * 1000L));
addSummaryData("activeSeconds", summaryProto.getTime().getWorkoutDuration(), "seconds");
addSummaryData(ACTIVE_SECONDS, summaryProto.getTime().getWorkoutDuration(), UNIT_SECONDS);
// TODO pause durations
}
@ -82,93 +84,94 @@ public class Huami2021ActivitySummaryParser extends HuamiActivitySummaryParser {
summary.setBaseLatitude(summaryProto.getLocation().getBaseLatitude());
summary.setBaseAltitude(summaryProto.getLocation().getBaseAltitude() / 2);
// TODO: Min/Max Latitude/Longitude
addSummaryData("baseAltitude", summaryProto.getLocation().getBaseAltitude() / 2, "meters");
addSummaryData(ALTITUDE_BASE, summaryProto.getLocation().getBaseAltitude() / 2, UNIT_METERS);
}
if (summaryProto.hasHeartRate()) {
addSummaryData("averageHR", summaryProto.getHeartRate().getAvg(), "bpm");
addSummaryData("maxHR", summaryProto.getHeartRate().getMax(), "bpm");
addSummaryData("minHR", summaryProto.getHeartRate().getMin(), "bpm");
addSummaryData(HR_AVG, summaryProto.getHeartRate().getAvg(), UNIT_BPM);
addSummaryData(HR_MAX, summaryProto.getHeartRate().getMax(), UNIT_BPM);
addSummaryData(HR_MIN, summaryProto.getHeartRate().getMin(), UNIT_BPM);
}
if (summaryProto.hasSteps()) {
addSummaryData("maxCadence", summaryProto.getSteps().getMaxCadence() * 60, "spm");
addSummaryData("averageCadence", summaryProto.getSteps().getAvgCadence() * 60, "spm");
addSummaryData("averageStride", summaryProto.getSteps().getAvgStride(), "cm");
addSummaryData("steps", summaryProto.getSteps().getSteps(), "steps_unit");
addSummaryData(CADENCE_MAX, summaryProto.getSteps().getMaxCadence() * 60, UNIT_SPM);
addSummaryData(CADENCE_AVG, summaryProto.getSteps().getAvgCadence() * 60, UNIT_SPM);
addSummaryData(STRIDE_AVG, summaryProto.getSteps().getAvgStride(), UNIT_CM);
addSummaryData(STEPS, summaryProto.getSteps().getSteps(), UNIT_STEPS);
}
if (summaryProto.hasDistance()) {
addSummaryData("distanceMeters", summaryProto.getDistance().getDistance(), "meters");
addSummaryData(DISTANCE_METERS, summaryProto.getDistance().getDistance(), UNIT_METERS);
}
if (summaryProto.hasPace()) {
addSummaryData("maxPace", summaryProto.getPace().getBest(), "seconds_m");
addSummaryData("averageKMPaceSeconds", summaryProto.getPace().getAvg() * 1000, "seconds_km");
addSummaryData(PACE_MAX, summaryProto.getPace().getBest(), UNIT_SECONDS_PER_M);
addSummaryData(PACE_AVG_SECONDS_KM, summaryProto.getPace().getAvg() * 1000, UNIT_SECONDS_PER_KM);
}
if (summaryProto.hasCalories()) {
addSummaryData("caloriesBurnt", summaryProto.getCalories().getCalories(), "calories_unit");
addSummaryData(CALORIES_BURNT, summaryProto.getCalories().getCalories(), UNIT_KCAL);
}
if (summaryProto.hasHeartRateZones()) {
// TODO hr zones bpm?
if (summaryProto.getHeartRateZones().getZoneTimeCount() == 6) {
addSummaryData("hrZoneNa", summaryProto.getHeartRateZones().getZoneTime(0), "seconds");
addSummaryData("hrZoneWarmUp", summaryProto.getHeartRateZones().getZoneTime(1), "seconds");
addSummaryData("hrZoneFatBurn", summaryProto.getHeartRateZones().getZoneTime(2), "seconds");
addSummaryData("hrZoneAerobic", summaryProto.getHeartRateZones().getZoneTime(3), "seconds");
addSummaryData("hrZoneAnaerobic", summaryProto.getHeartRateZones().getZoneTime(4), "seconds");
addSummaryData("hrZoneExtreme", summaryProto.getHeartRateZones().getZoneTime(5), "seconds");
addSummaryData(HR_ZONE_NA, summaryProto.getHeartRateZones().getZoneTime(0), UNIT_SECONDS);
addSummaryData(HR_ZONE_WARM_UP, summaryProto.getHeartRateZones().getZoneTime(1), UNIT_SECONDS);
addSummaryData(HR_ZONE_FAT_BURN, summaryProto.getHeartRateZones().getZoneTime(2), UNIT_SECONDS);
addSummaryData(HR_ZONE_AEROBIC, summaryProto.getHeartRateZones().getZoneTime(3), UNIT_SECONDS);
addSummaryData(HR_ZONE_ANAEROBIC, summaryProto.getHeartRateZones().getZoneTime(4), UNIT_SECONDS);
addSummaryData(HR_ZONE_EXTREME, summaryProto.getHeartRateZones().getZoneTime(5), UNIT_SECONDS);
} else {
LOG.warn("Unexpected number of HR zones {}", summaryProto.getHeartRateZones().getZoneTimeCount());
}
}
if (summaryProto.hasTrainingEffect()) {
addSummaryData("aerobicTrainingEffect", summaryProto.getTrainingEffect().getAerobicTrainingEffect(), "");
addSummaryData("anaerobicTrainingEffect", summaryProto.getTrainingEffect().getAnaerobicTrainingEffect(), "");
addSummaryData("currentWorkoutLoad", summaryProto.getTrainingEffect().getCurrentWorkoutLoad(), "");
addSummaryData("maximumOxygenUptake", summaryProto.getTrainingEffect().getMaximumOxygenUptake(), "ml/kg/min");
addSummaryData(TRAINING_EFFECT_AEROBIC, summaryProto.getTrainingEffect().getAerobicTrainingEffect(), UNIT_NONE);
addSummaryData(TRAINING_EFFECT_ANAEROBIC, summaryProto.getTrainingEffect().getAnaerobicTrainingEffect(), UNIT_NONE);
addSummaryData(WORKOUT_LOAD, summaryProto.getTrainingEffect().getCurrentWorkoutLoad(), UNIT_NONE);
addSummaryData(MAXIMUM_OXYGEN_UPTAKE, summaryProto.getTrainingEffect().getMaximumOxygenUptake(), UNIT_ML_KG_MIN);
}
if (summaryProto.hasAltitude()) {
addSummaryData("maxAltitude", summaryProto.getAltitude().getMaxAltitude() / 200, "meters");
addSummaryData("minAltitude", summaryProto.getAltitude().getMinAltitude() / 200, "meters");
addSummaryData("averageAltitude", summaryProto.getAltitude().getAvgAltitude() / 200, "meters");
addSummaryData(ALTITUDE_MAX, summaryProto.getAltitude().getMaxAltitude() / 200, UNIT_METERS);
addSummaryData(ALTITUDE_MIN, summaryProto.getAltitude().getMinAltitude() / 200, UNIT_METERS);
addSummaryData(ALTITUDE_AVG, summaryProto.getAltitude().getAvgAltitude() / 200, UNIT_METERS);
// TODO totalClimbing
addSummaryData("elevationGain", summaryProto.getAltitude().getElevationGain() / 100, "meters");
addSummaryData("elevationLoss", summaryProto.getAltitude().getElevationLoss() / 100, "meters");
addSummaryData(ELEVATION_GAIN, summaryProto.getAltitude().getElevationGain() / 100, UNIT_METERS);
addSummaryData(ELEVATION_LOSS, summaryProto.getAltitude().getElevationLoss() / 100, UNIT_METERS);
}
if (summaryProto.hasElevation()) {
addSummaryData("ascentSeconds", summaryProto.getElevation().getUphillTime(), "seconds");
addSummaryData("descentSeconds", summaryProto.getElevation().getDownhillTime(), "seconds");
addSummaryData(ASCENT_SECONDS, summaryProto.getElevation().getUphillTime(), UNIT_SECONDS);
addSummaryData(DESCENT_SECONDS, summaryProto.getElevation().getDownhillTime(), UNIT_SECONDS);
}
if (summaryProto.hasSwimmingData()) {
addSummaryData("laps", summaryProto.getSwimmingData().getLaps(), "laps_unit");
addSummaryData(LAPS, summaryProto.getSwimmingData().getLaps(), UNIT_LAPS);
switch (summaryProto.getSwimmingData().getLaneLengthUnit()) {
case 0:
addSummaryData("laneLength", summaryProto.getSwimmingData().getLaneLength(), "meters");
addSummaryData(LANE_LENGTH, summaryProto.getSwimmingData().getLaneLength(), UNIT_METERS);
break;
case 1:
addSummaryData("laneLength", summaryProto.getSwimmingData().getLaneLength(), "yard");
addSummaryData(LANE_LENGTH, summaryProto.getSwimmingData().getLaneLength(), UNIT_YARD);
break;
}
switch (summaryProto.getSwimmingData().getStyle()) {
// TODO i18n these
case 1:
addSummaryData("swimStyle", "breaststroke");
addSummaryData(SWIM_STYLE, "breaststroke");
break;
case 2:
addSummaryData("swimStyle", "freestyle");
addSummaryData(SWIM_STYLE, "freestyle");
break;
}
addSummaryData("strokes", summaryProto.getSwimmingData().getStrokes(), "strokes_unit");
addSummaryData("avgStrokeRate", summaryProto.getSwimmingData().getAvgStrokeRate(), "strokes_minute");
addSummaryData("maxStrokeRate", summaryProto.getSwimmingData().getMaxStrokeRate(), "strokes_minute");
addSummaryData("averageStrokeDistance", summaryProto.getSwimmingData().getAvgDps(), "cm");
addSummaryData("swolfIndex", summaryProto.getSwimmingData().getSwolf(), "");
addSummaryData(STROKES, summaryProto.getSwimmingData().getStrokes(), UNIT_STROKES);
addSummaryData(STROKE_RATE_AVG, summaryProto.getSwimmingData().getAvgStrokeRate(), UNIT_STROKES_PER_MINUTE);
addSummaryData(STROKE_RATE_MAX, summaryProto.getSwimmingData().getMaxStrokeRate(), UNIT_STROKES_PER_MINUTE);
addSummaryData(STROKE_DISTANCE_AVG, summaryProto.getSwimmingData().getAvgDps(), UNIT_CM);
addSummaryData(SWOLF_INDEX, summaryProto.getSwimmingData().getSwolf(), UNIT_NONE);
}
}
}

View File

@ -17,6 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.*;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
@ -273,9 +275,9 @@ public class HuamiActivitySummaryParser implements ActivitySummaryParser {
buffer.getInt(); // unknown probably flatDistance = buffer.getFloat();
flatSeconds = buffer.getInt() / 1000; // ms?
addSummaryData("ascentSeconds", ascentSeconds, "seconds");
addSummaryData("descentSeconds", descentSeconds, "seconds");
addSummaryData("flatSeconds", flatSeconds, "seconds");
addSummaryData(ASCENT_SECONDS, ascentSeconds, UNIT_SECONDS);
addSummaryData(DESCENT_SECONDS, descentSeconds, UNIT_SECONDS);
addSummaryData(FLAT_SECONDS, flatSeconds, UNIT_SECONDS);
}
averageHR = buffer.getShort();
@ -308,62 +310,62 @@ public class HuamiActivitySummaryParser implements ActivitySummaryParser {
// summary.setAveragePace(BLETypeConversions.toUnsigned(averagePace);
// summary.setAverageStride(BLETypeConversions.toUnsigned(averageStride);
addSummaryData("ascentSeconds", ascentSeconds, "seconds");
addSummaryData("descentSeconds", descentSeconds, "seconds");
addSummaryData("flatSeconds", flatSeconds, "seconds");
addSummaryData("ascentDistance", ascentDistance, "meters");
addSummaryData("descentDistance", descentDistance, "meters");
addSummaryData("flatDistance", flatDistance, "meters");
addSummaryData(ASCENT_SECONDS, ascentSeconds, UNIT_SECONDS);
addSummaryData(DESCENT_SECONDS, descentSeconds, UNIT_SECONDS);
addSummaryData(FLAT_SECONDS, flatSeconds, UNIT_SECONDS);
addSummaryData(ASCENT_DISTANCE, ascentDistance, UNIT_METERS);
addSummaryData(DESCENT_DISTANCE, descentDistance, UNIT_METERS);
addSummaryData(FLAT_DISTANCE, flatDistance, UNIT_METERS);
addSummaryData("distanceMeters", distanceMeters, "meters");
// addSummaryData("distanceMeters2", distanceMeters2, "meters");
addSummaryData("ascentMeters", ascentMeters, "meters");
addSummaryData("descentMeters", descentMeters, "meters");
addSummaryData(DISTANCE_METERS, distanceMeters, UNIT_METERS);
// addSummaryData("distanceMeters2", distanceMeters2, UNIT_METERS);
addSummaryData(ASCENT_METERS, ascentMeters, UNIT_METERS);
addSummaryData(DESCENT_METERS, descentMeters, UNIT_METERS);
if (maxAltitude != -100000) {
addSummaryData("maxAltitude", maxAltitude, "meters");
addSummaryData(ALTITUDE_MAX, maxAltitude, UNIT_METERS);
}
if (minAltitude != 100000) {
addSummaryData("minAltitude", minAltitude, "meters");
addSummaryData(ALTITUDE_MIN, minAltitude, UNIT_METERS);
}
if (minAltitude != 100000) {
addSummaryData("averageAltitude", averageAltitude, "meters");
addSummaryData(ALTITUDE_AVG, averageAltitude, UNIT_METERS);
}
addSummaryData("steps", steps, "steps_unit");
addSummaryData("activeSeconds", activeSeconds, "seconds");
addSummaryData("caloriesBurnt", caloriesBurnt, "calories_unit");
addSummaryData("maxSpeed", maxSpeed, "meters_second");
addSummaryData("minSpeed", minSpeed, "meters_second");
addSummaryData("averageSpeed", averageSpeed, "meters_second");
addSummaryData("maxCadence", maxCadence, "spm");
addSummaryData("minCadence", minCadence, "spm");
addSummaryData("averageCadence", averageCadence, "spm");
addSummaryData(STEPS, steps, UNIT_STEPS);
addSummaryData(ACTIVE_SECONDS, activeSeconds, UNIT_SECONDS);
addSummaryData(CALORIES_BURNT, caloriesBurnt, UNIT_KCAL);
addSummaryData(SPEED_MAX, maxSpeed, UNIT_METERS_PER_SECOND);
addSummaryData(SPEED_MIN, minSpeed, UNIT_METERS_PER_SECOND);
addSummaryData(SPEED_AVG, averageSpeed, UNIT_METERS_PER_SECOND);
addSummaryData(CADENCE_MAX, maxCadence, UNIT_SPM);
addSummaryData(CADENCE_MIN, minCadence, UNIT_SPM);
addSummaryData(CADENCE_AVG, averageCadence, UNIT_SPM);
if (!(activityKind == ActivityKind.TYPE_ELLIPTICAL_TRAINER ||
activityKind == ActivityKind.TYPE_JUMP_ROPING ||
activityKind == ActivityKind.TYPE_EXERCISE ||
activityKind == ActivityKind.TYPE_YOGA ||
activityKind == ActivityKind.TYPE_INDOOR_CYCLING)) {
addSummaryData("minPace", minPace, "seconds_m");
addSummaryData("maxPace", maxPace, "seconds_m");
// addSummaryData("averagePace", averagePace, "seconds_m");
addSummaryData(PACE_MIN, minPace, UNIT_SECONDS_PER_M);
addSummaryData(PACE_MAX, maxPace, UNIT_SECONDS_PER_M);
// addSummaryData("averagePace", averagePace, UNIT_SECONDS_PER_M);
}
addSummaryData("totalStride", totalStride, "meters");
addSummaryData("averageHR", averageHR, "bpm");
addSummaryData("maxHR", maxHR, "bpm");
addSummaryData("minHR", minHR, "bpm");
addSummaryData("averageKMPaceSeconds", averageKMPaceSeconds, "seconds_km");
addSummaryData("averageStride", averageStride, "cm");
addSummaryData("maxStride", maxStride, "cm");
addSummaryData("minStride", minStride, "cm");
// addSummaryData("averageStride2", averageStride2, "cm");
addSummaryData(STRIDE_TOTAL, totalStride, UNIT_METERS);
addSummaryData(HR_AVG, averageHR, UNIT_BPM);
addSummaryData(HR_MAX, maxHR, UNIT_BPM);
addSummaryData(HR_MIN, minHR, UNIT_BPM);
addSummaryData(PACE_AVG_SECONDS_KM, averageKMPaceSeconds, UNIT_SECONDS_PER_KM);
addSummaryData(STRIDE_AVG, averageStride, UNIT_CM);
addSummaryData(STRIDE_MAX, maxStride, UNIT_CM);
addSummaryData(STRIDE_MIN, minStride, UNIT_CM);
// addSummaryData("averageStride2", averageStride2, UNIT_CM);
if (activityKind == ActivityKind.TYPE_SWIMMING || activityKind == ActivityKind.TYPE_SWIMMING_OPENWATER) {
addSummaryData("averageStrokeDistance", averageStrokeDistance, "meters");
addSummaryData("averageStrokesPerSecond", averageStrokesPerSecond, "strokes_second");
addSummaryData("averageLapPace", averageLapPace, "second");
addSummaryData("strokes", strokes, "strokes");
addSummaryData("swolfIndex", swolfIndex, "swolf_index");
addSummaryData(STROKE_DISTANCE_AVG, averageStrokeDistance, UNIT_METERS);
addSummaryData(STROKE_AVG_PER_SECOND, averageStrokesPerSecond, UNIT_STROKES_PER_SECOND);
addSummaryData(LAP_PACE_AVERAGE, averageLapPace, "second");
addSummaryData(STROKES, strokes, "strokes");
addSummaryData(SWOLF_INDEX, swolfIndex, "swolf_index");
String swimStyleName = "unknown"; // TODO: translate here or keep as string identifier here?
switch (swimStyle) {
case 1:
@ -379,8 +381,8 @@ public class HuamiActivitySummaryParser implements ActivitySummaryParser {
swimStyleName = "medley";
break;
}
addSummaryData("swimStyle", swimStyleName);
addSummaryData("laps", laps, "laps");
addSummaryData(SWIM_STYLE, swimStyleName);
addSummaryData(LAPS, laps, "laps");
}
}

View File

@ -0,0 +1,109 @@
/* Copyright (C) 2023 José Rebelo
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 <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.model;
public class ActivitySummaryEntries {
public static final String TIME_START = "startTime";
public static final String TIME_END = "endTime";
public static final String ACTIVE_SECONDS = "activeSeconds";
public static final String ALTITUDE_AVG = "averageAltitude";
public static final String ALTITUDE_BASE = "baseAltitude";
public static final String ALTITUDE_MAX = "maxAltitude";
public static final String ALTITUDE_MIN = "minAltitude";
public static final String ASCENT_DISTANCE = "ascentDistance";
public static final String ASCENT_METERS = "ascentMeters";
public static final String ASCENT_SECONDS = "ascentSeconds";
public static final String DESCENT_DISTANCE = "descentDistance";
public static final String DESCENT_METERS = "descentMeters";
public static final String DESCENT_SECONDS = "descentSeconds";
public static final String FLAT_DISTANCE = "flatDistance";
public static final String FLAT_SECONDS = "flatSeconds";
public static final String CADENCE_AVG = "averageCadence";
public static final String CADENCE_MAX = "maxCadence";
public static final String CADENCE_MIN = "minCadence";
public static final String SPEED_AVG = "averageSpeed";
public static final String SPEED_MAX = "maxSpeed";
public static final String SPEED_MIN = "minSpeed";
public static final String DISTANCE_METERS = "distanceMeters";
public static final String ELEVATION_GAIN = "elevationGain";
public static final String ELEVATION_LOSS = "elevationLoss";
public static final String HR_AVG = "averageHR";
public static final String HR_MAX = "maxHR";
public static final String HR_MIN = "minHR";
public static final String HR_ZONE_NA = "hrZoneNa";
public static final String HR_ZONE_WARM_UP = "hrZoneWarmUp";
public static final String HR_ZONE_FAT_BURN = "hrZoneFatBurn";
public static final String HR_ZONE_AEROBIC = "hrZoneAerobic";
public static final String HR_ZONE_ANAEROBIC = "hrZoneAnaerobic";
public static final String HR_ZONE_EXTREME = "hrZoneExtreme";
public static final String LANE_LENGTH = "laneLength";
public static final String LAPS = "laps";
public static final String LAP_PACE_AVERAGE = "averageLapPace";
public static final String PACE_AVG_SECONDS_KM = "averageKMPaceSeconds";
public static final String PACE_MAX = "maxPace";
public static final String PACE_MIN = "minPace";
public static final String STEPS = "steps";
public static final String STRIDE_AVG = "averageStride";
public static final String STRIDE_MAX = "maxStride";
public static final String STRIDE_MIN = "minStride";
public static final String STRIDE_TOTAL = "totalStride";
public static final String STROKE_DISTANCE_AVG = "averageStrokeDistance";
public static final String STROKE_AVG_PER_SECOND = "averageStrokesPerSecond";
public static final String STROKE_RATE_AVG = "avgStrokeRate";
public static final String STROKE_RATE_MAX = "maxStrokeRate";
public static final String STROKES = "strokes";
public static final String SWIM_STYLE = "swimStyle";
public static final String SWOLF_INDEX = "swolfIndex";
public static final String CALORIES_BURNT = "caloriesBurnt";
public static final String TRAINING_EFFECT_AEROBIC = "aerobicTrainingEffect";
public static final String TRAINING_EFFECT_ANAEROBIC = "anaerobicTrainingEffect";
public static final String WORKOUT_LOAD = "currentWorkoutLoad";
public static final String MAXIMUM_OXYGEN_UPTAKE = "maximumOxygenUptake";
public static final String RECOVERY_TIME = "recoveryTime";
public static final String UNIT_BPM = "bpm";
public static final String UNIT_CM = "cm";
public static final String UNIT_UNIX_EPOCH_SECONDS = "unix_epoch_seconds";
public static final String UNIT_KCAL = "calories_unit";
public static final String UNIT_LAPS = "laps_unit";
public static final String UNIT_METERS = "meters";
public static final String UNIT_ML_KG_MIN = "ml/kg/min";
public static final String UNIT_NONE = "";
public static final String UNIT_HOURS = "hours";
public static final String UNIT_SECONDS = "seconds";
public static final String UNIT_SECONDS_PER_KM = "seconds_km";
public static final String UNIT_SECONDS_PER_M = "seconds_m";
public static final String UNIT_METERS_PER_SECOND = "meters_second";
public static final String UNIT_KMPH = "km_h";
public static final String UNIT_SPM = "spm";
public static final String UNIT_STEPS = "steps_unit";
public static final String UNIT_STROKES = "strokes_unit";
public static final String UNIT_STROKES_PER_MINUTE = "strokes_minute";
public static final String UNIT_STROKES_PER_SECOND = "strokes_second";
public static final String UNIT_YARD = "yard";
}

View File

@ -1,5 +1,7 @@
package nodomain.freeyourgadget.gadgetbridge.model;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -153,35 +155,35 @@ public class ActivitySummaryJsonSummary {
private JSONObject createActivitySummaryGroups(){
final Map<String, List<String>> groupDefinitions = new HashMap<String, List<String>>() {{
put("Strokes", Arrays.asList(
"averageStrokeDistance", "averageStrokesPerSecond", "strokes",
"avgStrokeRate", "maxStrokeRate"
STROKE_DISTANCE_AVG, STROKE_AVG_PER_SECOND, STROKES,
STROKE_RATE_AVG, STROKE_RATE_MAX
));
put("Swimming", Arrays.asList(
"swolfIndex", "swimStyle"
SWOLF_INDEX, SWIM_STYLE
));
put("Elevation", Arrays.asList(
"ascentMeters", "descentMeters", "maxAltitude", "minAltitude", "averageAltitude",
"baseAltitude", "ascentSeconds", "descentSeconds", "flatSeconds", "ascentDistance",
"descentDistance", "flatDistance", "elevationGain", "elevationLoss"
ASCENT_METERS, DESCENT_METERS, ALTITUDE_MAX, ALTITUDE_MIN, ALTITUDE_AVG,
ALTITUDE_BASE, ASCENT_SECONDS, DESCENT_SECONDS, FLAT_SECONDS, ASCENT_DISTANCE,
DESCENT_DISTANCE, FLAT_DISTANCE, ELEVATION_GAIN, ELEVATION_LOSS
));
put("Speed", Arrays.asList(
"averageSpeed", "maxSpeed", "minSpeed", "averageKMPaceSeconds", "minPace",
"maxPace", "averageSpeed2", "averageCadence", "maxCadence", "minCadence"
SPEED_AVG, SPEED_MAX, SPEED_MIN, PACE_AVG_SECONDS_KM, PACE_MIN,
PACE_MAX, "averageSpeed2", CADENCE_AVG, CADENCE_MAX, CADENCE_MIN
));
put("Activity", Arrays.asList(
"distanceMeters", "steps", "activeSeconds", "caloriesBurnt", "totalStride",
"averageHR", "maxHR", "minHR", "averageStride", "maxStride", "minStride"
DISTANCE_METERS, STEPS, ACTIVE_SECONDS, CALORIES_BURNT, STRIDE_TOTAL,
HR_AVG, HR_MAX, HR_MIN, STRIDE_AVG, STRIDE_MAX, STRIDE_MIN
));
put("HeartRateZones", Arrays.asList(
"hrZoneNa", "hrZoneWarmUp", "hrZoneFatBurn", "hrZoneAerobic", "hrZoneAnaerobic",
"hrZoneExtreme"
HR_ZONE_NA, HR_ZONE_WARM_UP, HR_ZONE_FAT_BURN, HR_ZONE_AEROBIC, HR_ZONE_ANAEROBIC,
HR_ZONE_EXTREME
));
put("TrainingEffect", Arrays.asList(
"aerobicTrainingEffect", "anaerobicTrainingEffect", "currentWorkoutLoad",
"maximumOxygenUptake"
TRAINING_EFFECT_AEROBIC, TRAINING_EFFECT_ANAEROBIC, WORKOUT_LOAD,
MAXIMUM_OXYGEN_UPTAKE
));
put("laps", Arrays.asList(
"averageLapPace", "laps", "laneLength"
LAP_PACE_AVERAGE, LAPS, LANE_LENGTH
));
}};

View File

@ -16,13 +16,46 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.ACTIVE_SECONDS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.CADENCE_AVG;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.CALORIES_BURNT;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.DISTANCE_METERS;
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_MIN;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_AEROBIC;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_ANAEROBIC;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_EXTREME;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_FAT_BURN;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_WARM_UP;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.PACE_MAX;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.PACE_MIN;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.RECOVERY_TIME;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.SPEED_MAX;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.STEPS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TIME_END;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TIME_START;
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.UNIT_BPM;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_HOURS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KCAL;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KMPH;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_NONE;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS_PER_M;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SPM;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_STEPS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_UNIX_EPOCH_SECONDS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.WORKOUT_LOAD;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl.XiaomiSimpleActivityParser.XIAOMI_WORKOUT_TYPE;
import android.widget.Toast;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.ArrayUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -152,50 +185,52 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
builder.setHeaderSize(headerSize);
builder.addInt("startTime", "seconds");
builder.addInt("endTime", "seconds");
builder.addInt("activeSeconds", "seconds");
builder.addInt(TIME_START, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(TIME_END, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(ACTIVE_SECONDS, UNIT_SECONDS);
builder.addUnknown(4);
builder.addShort("caloriesBurnt", "calories_unit");
builder.addShort(CALORIES_BURNT, UNIT_KCAL);
builder.addUnknown(4);
builder.addByte("averageHR", "bpm");
builder.addByte("maxHR", "bpm");
builder.addByte("minHR", "bpm");
builder.addFloat("aerobicTrainingEffect", "");
builder.addByte(HR_AVG, UNIT_BPM);
builder.addByte(HR_MAX, UNIT_BPM);
builder.addByte(HR_MIN, UNIT_BPM);
builder.addFloat(TRAINING_EFFECT_AEROBIC, UNIT_NONE);
builder.addUnknown(1);
builder.addUnknown(1);
builder.addShort(RECOVERY_TIME, UNIT_HOURS);
builder.addInt(HR_ZONE_EXTREME, UNIT_SECONDS);
builder.addInt(HR_ZONE_ANAEROBIC, UNIT_SECONDS);
builder.addInt(HR_ZONE_AEROBIC, UNIT_SECONDS);
builder.addInt(HR_ZONE_FAT_BURN, UNIT_SECONDS);
builder.addInt(HR_ZONE_WARM_UP, UNIT_SECONDS);
builder.addUnknown(2);
builder.addShort("recoveryTime", "hours");
builder.addInt("hrZoneExtreme", "seconds");
builder.addInt("hrZoneAnaerobic", "seconds");
builder.addInt("hrZoneAerobic", "seconds");
builder.addInt("hrZoneFatBurn", "seconds");
builder.addInt("hrZoneWarmUp", "seconds");
builder.addUnknown(6);
builder.addFloat("anaerobicTrainingEffect", "");
builder.addUnknown(4);
builder.addFloat(TRAINING_EFFECT_ANAEROBIC, UNIT_NONE);
// FIXME identify field lengths to align with the header
builder.addUnknown(3);
builder.addInt("configuredTimeGoal", "seconds");
builder.addShort("configuredCaloriesGoal", "calories_unit");
builder.addShort("maximumCaloriesGoal", "calories_unit"); // TODO: mhm?
builder.addInt("configuredTimeGoal", UNIT_SECONDS);
builder.addShort("configuredCaloriesGoal", UNIT_KCAL);
builder.addShort("maximumCaloriesGoal", UNIT_KCAL); // TODO: mhm?
builder.addUnknown(28);
builder.addShort("trainingLoad", "");
builder.addShort(WORKOUT_LOAD, UNIT_NONE); // training load
builder.addUnknown(24);
builder.addByte("configuredSets", "");
builder.addByte("configuredSets", UNIT_NONE);
builder.addUnknown(13);
builder.addInt("startTime2", "seconds");
builder.addInt("endTime2", "seconds");
builder.addInt("goal", ""); // TODO match against goalType
builder.addInt("duration2", "seconds");
builder.addInt("intervalTime", "seconds");
builder.addInt("startTime2", UNIT_SECONDS);
builder.addInt("endTime2", UNIT_SECONDS);
builder.addInt("goal", UNIT_NONE); // TODO match against goalType
builder.addInt("duration2", UNIT_SECONDS);
builder.addInt("intervalTime", UNIT_SECONDS);
builder.addUnknown(56);
builder.addInt("hrZoneExtreme2", "seconds");
builder.addInt("hrZoneAnaerobic2", "seconds");
builder.addInt("hrZoneAerobic2", "seconds");
builder.addInt("hrZoneFatBurn2", "seconds");
builder.addInt("hrZoneWarmUp2", "seconds");
builder.addInt("hrZoneExtreme2", UNIT_SECONDS);
builder.addInt("hrZoneAnaerobic2", UNIT_SECONDS);
builder.addInt("hrZoneAerobic2", UNIT_SECONDS);
builder.addInt("hrZoneFatBurn2", UNIT_SECONDS);
builder.addInt("hrZoneWarmUp2", UNIT_SECONDS);
builder.addUnknown(16);
builder.addShort("vitality_gain", "");
builder.addShort("training_load2", "");
builder.addShort("recovery_time2", "hours");
builder.addShort("vitality_gain", UNIT_NONE);
builder.addShort("training_load2", UNIT_NONE);
builder.addShort("recovery_time2", UNIT_HOURS);
return builder.build();
}
@ -215,30 +250,31 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
builder.setHeaderSize(headerSize);
builder.addInt("startTime", "seconds");
builder.addInt("endTime", "seconds");
builder.addInt("activeSeconds", "seconds");
builder.addInt("distanceMeters", "meters");
builder.addInt("caloriesBurnt", "calories_unit");
builder.addInt("maxPace", "seconds_m");
builder.addInt("minPace", "seconds_m");
builder.addInt(TIME_START, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(TIME_END, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(ACTIVE_SECONDS, UNIT_SECONDS);
builder.addInt(DISTANCE_METERS, UNIT_METERS);
builder.addInt(CALORIES_BURNT, UNIT_KCAL);
builder.addInt(PACE_MAX, UNIT_SECONDS_PER_M);
builder.addInt(PACE_MIN, UNIT_SECONDS_PER_M);
builder.addUnknown(4);
builder.addInt("steps", "steps_unit");
builder.addInt(STEPS, UNIT_STEPS);
builder.addUnknown(2); // pace?
builder.addByte("averageHR", "bpm");
builder.addByte("maxHR", "bpm");
builder.addByte("minHR", "bpm");
builder.addByte(HR_AVG, UNIT_BPM);
builder.addByte(HR_MAX, UNIT_BPM);
builder.addByte(HR_MIN, UNIT_BPM);
// FIXME identify field lengths to align with the header
builder.addUnknown(20);
builder.addFloat("recoveryValue", "recoveryValue");
builder.addUnknown(9);
builder.addByte("recoveryTime", "seconds");
builder.addByte(RECOVERY_TIME, UNIT_SECONDS);
builder.addUnknown(2);
builder.addInt("hrZoneExtreme", "seconds");
builder.addInt("hrZoneAnaerobic", "seconds");
builder.addInt("hrZoneAerobic", "seconds");
builder.addInt("hrZoneFatBurn", "seconds");
builder.addInt("hrZoneWarmUp", "seconds");
builder.addInt("configured_time_goal", "seconds");
builder.addInt(HR_ZONE_EXTREME, UNIT_SECONDS);
builder.addInt(HR_ZONE_ANAEROBIC, UNIT_SECONDS);
builder.addInt(HR_ZONE_AEROBIC, UNIT_SECONDS);
builder.addInt(HR_ZONE_FAT_BURN, UNIT_SECONDS);
builder.addInt(HR_ZONE_WARM_UP, UNIT_SECONDS);
builder.addInt("configured_time_goal", UNIT_SECONDS);
return builder.build();
}
@ -258,41 +294,41 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
builder.setHeaderSize(headerSize);
builder.addShort("xiaomiActivityType", "xiaomiActivityType");
builder.addInt("startTime", "seconds");
builder.addInt("endTime", "seconds");
builder.addInt("activeSeconds", "seconds");
builder.addShort(XIAOMI_WORKOUT_TYPE, XIAOMI_WORKOUT_TYPE);
builder.addInt(TIME_START, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(TIME_END, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(ACTIVE_SECONDS, UNIT_SECONDS);
builder.addUnknown(4);
builder.addInt("distanceMeters", "meters");
builder.addInt(DISTANCE_METERS, UNIT_METERS);
builder.addUnknown(2);
builder.addShort("caloriesBurnt", "calories_unit");
builder.addShort(CALORIES_BURNT, UNIT_KCAL);
builder.addUnknown(12);
builder.addInt("steps", "steps_unit");
builder.addInt(STEPS, UNIT_STEPS);
builder.addUnknown(2);
builder.addByte("averageHR", "bpm");
builder.addByte("maxHR", "bpm");
builder.addByte("minHR", "bpm");
builder.addByte(HR_AVG, UNIT_BPM);
builder.addByte(HR_MAX, UNIT_BPM);
builder.addByte(HR_MIN, UNIT_BPM);
builder.addUnknown(20);
builder.addFloat("recoveryValue", "?");
builder.addUnknown(9);
builder.addByte("recoveryTime", "hours");
builder.addByte(RECOVERY_TIME, UNIT_HOURS);
builder.addUnknown(2);
builder.addInt("hrZoneExtreme", "seconds");
builder.addInt("hrZoneAnaerobic", "seconds");
builder.addInt("hrZoneAerobic", "seconds");
builder.addInt("hrZoneFatBurn", "seconds");
builder.addInt("hrZoneWarmUp", "seconds");
builder.addInt("configuredTimeGoal", "seconds");
builder.addShort("configuredCaloriesGoal", "calories_unit");
builder.addInt("configuredDistanceGoal", "meters");
builder.addInt(HR_ZONE_EXTREME, UNIT_SECONDS);
builder.addInt(HR_ZONE_ANAEROBIC, UNIT_SECONDS);
builder.addInt(HR_ZONE_AEROBIC, UNIT_SECONDS);
builder.addInt(HR_ZONE_FAT_BURN, UNIT_SECONDS);
builder.addInt(HR_ZONE_WARM_UP, UNIT_SECONDS);
builder.addInt("configuredTimeGoal", UNIT_SECONDS);
builder.addShort("configuredCaloriesGoal", UNIT_KCAL);
builder.addInt("configuredDistanceGoal", UNIT_METERS);
builder.addUnknown(11);
builder.addShort("trainingLoad", "");
builder.addShort(WORKOUT_LOAD, UNIT_NONE); // training load
builder.addUnknown(24);
builder.addByte("averageHR2", "bpm");
builder.addByte("maxHR2", "bpm");
builder.addByte("minHR2", "bpm");
builder.addByte("averageHR2", UNIT_BPM);
builder.addByte("maxHR2", UNIT_BPM);
builder.addByte("minHR2", UNIT_BPM);
builder.addUnknown(2);
builder.addByte("averageCadence", "spm");
builder.addByte(CADENCE_AVG, UNIT_SPM);
return builder.build();
}
@ -312,19 +348,20 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
builder.setHeaderSize(headerSize);
builder.addShort("xiaomiWorkoutType", "xiaomiWorkoutType");
builder.addInt("startTime", "seconds");
builder.addInt("endTime", "seconds");
builder.addInt("activeSeconds", "seconds");
builder.addShort(XIAOMI_WORKOUT_TYPE, XIAOMI_WORKOUT_TYPE);
builder.addInt(TIME_START, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(TIME_END, UNIT_UNIX_EPOCH_SECONDS);
builder.addInt(ACTIVE_SECONDS, UNIT_SECONDS);
builder.addUnknown(4);
builder.addInt("distanceMeters", "meters");
builder.addInt(DISTANCE_METERS, UNIT_METERS);
builder.addUnknown(2);
builder.addShort("caloriesBurnt", "calories_unit");
builder.addUnknown(8);
builder.addFloat("maxSpeed", "km_h");
builder.addByte("averageHR", "bpm");
builder.addByte("maxHR", "bpm");
builder.addByte("minHR", "bpm");
builder.addShort(CALORIES_BURNT, UNIT_KCAL);
builder.addUnknown(4);
builder.addUnknown(4);
builder.addFloat(SPEED_MAX, UNIT_KMPH);
builder.addByte(HR_AVG, UNIT_BPM);
builder.addByte(HR_MAX, UNIT_BPM);
builder.addByte(HR_MIN, UNIT_BPM);
return builder.build();
}

View File

@ -16,8 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TIME_END;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TIME_START;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_UNIX_EPOCH_SECONDS;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@ -26,8 +32,13 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class XiaomiSimpleActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(XiaomiSimpleActivityParser.class);
public static final String XIAOMI_WORKOUT_TYPE = "xiaomiWorkoutType";
private final int headerSize;
private final List<XiaomiSimpleDataEntry> dataEntries;
@ -42,19 +53,35 @@ public class XiaomiSimpleActivityParser {
final byte[] header = new byte[headerSize];
buf.get(header);
for (final XiaomiSimpleDataEntry dataEntry : dataEntries) {
LOG.debug("Header: {}", GB.hexdump(header));
for (int i = 0; i < dataEntries.size(); i++) {
final XiaomiSimpleDataEntry dataEntry = dataEntries.get(i);
final Number value = dataEntry.get(buf);
if (value == null) {
LOG.debug("Skipping unknown field {}", i);
continue;
}
if (dataEntry.getKey().equals("endTime")) {
if (dataEntry.getUnit().equals("seconds")) {
// Each bit in the header marks whether the data is valid or not, in order of the fields
final boolean validData = (header[i / 8] & (1 << (7 - (i % 8)))) != 0;
// FIXME: We can't use the header before identifying the correct field lenggths for unknown fields
// or parsing gets out of sync with the header and we will potentially ignore valid data
//if (!validData) {
// LOG.debug("Ignoring non-valid data {}", i);
// continue;
//}
if (dataEntry.getKey().equals(TIME_END)) {
if (dataEntry.getUnit().equals(UNIT_UNIX_EPOCH_SECONDS)) {
summary.setEndTime(new Date(value.intValue() * 1000L));
} else {
throw new IllegalArgumentException("endTime should be in seconds");
throw new IllegalArgumentException("endTime should be an unix epoch");
}
} if (dataEntry.getKey().equals("xiaomiWorkoutType")) {
} else if (dataEntry.getKey().equals(TIME_START)) {
// ignored
} else if (dataEntry.getKey().equals(XIAOMI_WORKOUT_TYPE)) {
// TODO use XiaomiWorkoutType
switch (value.intValue()) {
case 2: