Workout details: disable elevation chart if all points are zero

This commit is contained in:
José Rebelo
2025-08-29 14:39:12 +01:00
parent 2d47120ecd
commit 84dd8bf3fc
@@ -10,6 +10,8 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.
import android.content.Context;
import androidx.core.content.ContextCompat;
import com.github.mikephil.charting.charts.ScatterChart;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
@@ -47,21 +49,34 @@ public class DefaultWorkoutCharts {
final List<Entry> elevationDataPoints = new ArrayList<>();
boolean hasSpeedValues = false;
boolean hasCadenceValues = false;
boolean hasElevationValues = false;
final Accumulator cadenceAccumulator = new Accumulator();
for (int i = 0; i <= activityPoints.size() - 1; i++) {
final ActivityPoint point = activityPoints.get(i);
final long tsShorten = tsTranslation.shorten((int) point.getTime().getTime());
// HR
if (point.getHeartRate() > 0) {
heartRateDataPoints.add(new Entry(tsShorten, point.getHeartRate()));
}
// Elevation
if (point.getLocation() != null && point.getLocation().getAltitude() != GPSCoordinate.UNKNOWN_ALTITUDE) {
elevationDataPoints.add(new Entry(tsShorten, (float) point.getLocation().getAltitude()));
if (point.getLocation().getAltitude() != 0) {
// Some devices provide all points at zero
hasElevationValues = true;
}
}
// Speed
speedDataPoints.add(new Entry(tsShorten, point.getSpeed()));
if (!hasSpeedValues && point.getSpeed() > 0) {
hasSpeedValues = true;
}
// Cadence
cadenceDataPoints.add(new Entry(tsShorten, point.getCadence()));
if (!hasCadenceValues && point.getCadence() > 0) {
cadenceAccumulator.add(point.getCadence());
@@ -70,68 +85,122 @@ public class DefaultWorkoutCharts {
}
if (!heartRateDataPoints.isEmpty()) {
final String label = String.format("%s(%s)", context.getString(R.string.heart_rate), getUnitString(context, UNIT_BPM));
final LineDataSet dataset = createLineDataSet(context, heartRateDataPoints, label, context.getResources().getColor(R.color.chart_line_heart_rate));
ValueFormatter integerFormatter = new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return String.valueOf((int) value);
}
};
charts.add(new WorkoutChart("heart_rate", context.getString(R.string.heart_rate), ActivitySummaryEntries.GROUP_HEART_RATE, new LineData(dataset), integerFormatter, getUnitString(context, UNIT_BPM)));
charts.add(createHeartRateChart(context, heartRateDataPoints));
}
if (hasSpeedValues && !speedDataPoints.isEmpty()) {
if (ActivityKind.isPaceActivity(activityKind)) {
final String label = String.format("%s (%s)", context.getString(R.string.Pace), getUnitString(context, UNIT_MINUTES_PER_KM));
final LineDataSet dataset = createLineDataSet(context, speedDataPoints, label, context.getResources().getColor(R.color.chart_line_speed));
charts.add(new WorkoutChart("pace", context.getString(R.string.Pace), ActivitySummaryEntries.GROUP_SPEED, new LineData(dataset), new SpeedYLabelFormatter(UNIT_SECONDS_PER_KM), getUnitString(context, UNIT_MINUTES_PER_KM)));
} else {
final String label = String.format("%s (%s)", context.getString(R.string.Speed), getUnitString(context, UNIT_KMPH));
final LineDataSet dataset = createLineDataSet(context, speedDataPoints, label, context.getResources().getColor(R.color.chart_line_speed));
charts.add(new WorkoutChart("speed", context.getString(R.string.Speed), ActivitySummaryEntries.GROUP_SPEED, new LineData(dataset), new SpeedYLabelFormatter(UNIT_METERS_PER_SECOND), getUnitString(context, UNIT_KMPH)));
}
charts.add(createSpeedChart(context, activityKind, speedDataPoints));
}
if (hasCadenceValues && !cadenceDataPoints.isEmpty()) {
final String label = String.format("%s (%s)", context.getString(R.string.workout_cadence), getUnitString(context, getCadenceUnit(cycleUnit)));
final ScatterDataSet dataset = createScatterDataSet(context, cadenceDataPoints, label, context.getResources().getColor(R.color.chart_cadence_circle));
final ValueFormatter integerFormatter = new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return String.valueOf((int) value);
}
};
float xAxisMaximum = Math.max((float) (cadenceAccumulator.getMax() + 30), (float) cadenceAccumulator.getAverage() * 2);
charts.add(new WorkoutChart(
"cadence",
context.getString(R.string.workout_cadence),
ActivitySummaryEntries.GROUP_CADENCE,
new ScatterData(dataset),
integerFormatter,
getUnitString(context, UNIT_SPM),
lineChart -> {
YAxis yAxisLeft = lineChart.getAxisLeft();
yAxisLeft.setAxisMinimum(0);
yAxisLeft.setAxisMaximum(xAxisMaximum);
YAxis yAxisRight = lineChart.getAxisRight();
yAxisRight.setAxisMinimum(0);
yAxisRight.setAxisMaximum(xAxisMaximum);
return kotlin.Unit.INSTANCE;
}
)
);
charts.add(createCadenceChart(context, cycleUnit, cadenceDataPoints, cadenceAccumulator));
}
if (!elevationDataPoints.isEmpty()) {
final String label = String.format("%s (%s)", context.getString(R.string.Elevation), getUnitString(context, UNIT_METERS));
LineDataSet dataset = createLineDataSet(context, elevationDataPoints, label, context.getResources().getColor(R.color.chart_line_elevation));
charts.add(new WorkoutChart("elevation", context.getString(R.string.Elevation), ActivitySummaryEntries.GROUP_ELEVATION, new LineData(dataset), null, getUnitString(context, UNIT_METERS)));
if (hasElevationValues && !elevationDataPoints.isEmpty()) {
charts.add(createElevationChart(context, elevationDataPoints));
}
return charts;
}
private static WorkoutChart createElevationChart(final Context context,
final List<Entry> elevationDataPoints) {
final String label = String.format("%s (%s)", context.getString(R.string.Elevation), getUnitString(context, UNIT_METERS));
final LineDataSet dataset = createLineDataSet(context, elevationDataPoints, label, ContextCompat.getColor(context, R.color.chart_line_elevation));
return new WorkoutChart(
"elevation",
context.getString(R.string.Elevation),
ActivitySummaryEntries.GROUP_ELEVATION,
new LineData(dataset),
null,
getUnitString(context, UNIT_METERS)
);
}
private static WorkoutChart createHeartRateChart(final Context context,
final List<Entry> heartRateDataPoints) {
final String label = String.format("%s(%s)", context.getString(R.string.heart_rate), getUnitString(context, UNIT_BPM));
final LineDataSet dataset = createLineDataSet(context, heartRateDataPoints, label, ContextCompat.getColor(context, R.color.chart_line_heart_rate));
final ValueFormatter integerFormatter = new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return String.valueOf((int) value);
}
};
return new WorkoutChart(
"heart_rate",
context.getString(R.string.heart_rate),
ActivitySummaryEntries.GROUP_HEART_RATE,
new LineData(dataset),
integerFormatter,
getUnitString(context, UNIT_BPM)
);
}
private static WorkoutChart createSpeedChart(final Context context,
final ActivityKind activityKind,
final List<Entry> speedDataPoints) {
if (ActivityKind.isPaceActivity(activityKind)) {
final String label = String.format("%s (%s)", context.getString(R.string.Pace), getUnitString(context, UNIT_MINUTES_PER_KM));
final LineDataSet dataset = createLineDataSet(context, speedDataPoints, label, ContextCompat.getColor(context, R.color.chart_line_speed));
return new WorkoutChart(
"pace",
context.getString(R.string.Pace),
ActivitySummaryEntries.GROUP_SPEED,
new LineData(dataset),
new SpeedYLabelFormatter(UNIT_SECONDS_PER_KM),
getUnitString(context, UNIT_MINUTES_PER_KM)
);
} else {
final String label = String.format("%s (%s)", context.getString(R.string.Speed), getUnitString(context, UNIT_KMPH));
final LineDataSet dataset = createLineDataSet(context, speedDataPoints, label, ContextCompat.getColor(context, R.color.chart_line_speed));
return new WorkoutChart(
"speed",
context.getString(R.string.Speed),
ActivitySummaryEntries.GROUP_SPEED,
new LineData(dataset),
new SpeedYLabelFormatter(UNIT_METERS_PER_SECOND),
getUnitString(context, UNIT_KMPH)
);
}
}
private static WorkoutChart createCadenceChart(final Context context,
final ActivityKind.CycleUnit cycleUnit,
final List<Entry> cadenceDataPoints,
final Accumulator cadenceAccumulator) {
final String label = String.format("%s (%s)", context.getString(R.string.workout_cadence), getUnitString(context, getCadenceUnit(cycleUnit)));
final ScatterDataSet dataset = createScatterDataSet(context, cadenceDataPoints, label, ContextCompat.getColor(context, R.color.chart_cadence_circle));
final ValueFormatter integerFormatter = new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return String.valueOf((int) value);
}
};
float xAxisMaximum = Math.max(
(float) (cadenceAccumulator.getMax() + 30),
(float) cadenceAccumulator.getAverage() * 2
);
return new WorkoutChart(
"cadence",
context.getString(R.string.workout_cadence),
ActivitySummaryEntries.GROUP_CADENCE,
new ScatterData(dataset),
integerFormatter,
getUnitString(context, UNIT_SPM),
lineChart -> {
YAxis yAxisLeft = lineChart.getAxisLeft();
yAxisLeft.setAxisMinimum(0);
yAxisLeft.setAxisMaximum(xAxisMaximum);
YAxis yAxisRight = lineChart.getAxisRight();
yAxisRight.setAxisMinimum(0);
yAxisRight.setAxisMaximum(xAxisMaximum);
return kotlin.Unit.INSTANCE;
}
);
}
public static String getUnitString(final Context context, final String unit) {
final int resId = context.getResources().getIdentifier(unit, "string", context.getPackageName());
if (resId != 0) {
@@ -141,9 +210,9 @@ public class DefaultWorkoutCharts {
}
public static LineDataSet createLineDataSet(final Context context,
final List<Entry> entities,
final String label,
final int color) {
final List<Entry> entities,
final String label,
final int color) {
final LineDataSet dataSet = new LineDataSet(entities, label);
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
dataSet.setCubicIntensity(0.05f);
@@ -159,9 +228,9 @@ public class DefaultWorkoutCharts {
}
public static ScatterDataSet createScatterDataSet(final Context context,
final List<Entry> entities,
final String label,
final int color) {
final List<Entry> entities,
final String label,
final int color) {
final ScatterDataSet dataSet = new ScatterDataSet(entities, label);
dataSet.setAxisDependency(YAxis.AxisDependency.RIGHT);
dataSet.setColor(color);