From a9624f72e7e4a1c925ee99e49138c649277a7b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20Mart=C3=ADnez?= Date: Sun, 31 Jan 2021 17:23:47 +0100 Subject: [PATCH] Track recorded stats shows sensor data from trackpoints table. Heart rate: maximum and average. Cadence: maximum and average. Power: average. Fixes #525 and #502. --- .../content/sensor/SensorDataCyclingTest.java | 16 ++-- .../opentracks/adapters/SensorsAdapter.java | 95 +++---------------- .../provider/ContentProviderUtils.java | 29 ++++++ .../opentracks/content/sensor/SensorData.java | 15 ++- .../content/sensor/SensorDataCycling.java | 47 +++------ .../sensor/SensorDataCyclingPower.java | 17 +--- .../content/sensor/SensorDataHeartRate.java | 17 +--- .../content/sensor/SensorDataSet.java | 14 +-- .../fragments/StatisticsRecordedFragment.java | 56 +++++++++-- .../StatisticsRecordingFragment.java | 13 +-- .../opentracks/stats/SensorStatistics.java | 49 ++++++++++ .../viewmodels/SensorDataModel.java | 56 +++++++++++ src/main/res/layout/statistics_recorded.xml | 9 ++ src/main/res/values/strings.xml | 5 + 14 files changed, 267 insertions(+), 171 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/stats/SensorStatistics.java create mode 100644 src/main/java/de/dennisguse/opentracks/viewmodels/SensorDataModel.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingTest.java b/src/androidTest/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingTest.java index 2ba8e9a7e..8f8dd01d9 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingTest.java @@ -24,7 +24,7 @@ public class SensorDataCyclingTest { current.compute(previous); // then - assertEquals(60, current.getCadence_rpm(), 0.01); + assertEquals(60, current.getValue(), 0.01); } @Test @@ -37,7 +37,7 @@ public class SensorDataCyclingTest { current.compute(previous); // then - assertEquals(33.53, current.getCadence_rpm(), 0.01); + assertEquals(33.53, current.getValue(), 0.01); } @Test @@ -50,7 +50,7 @@ public class SensorDataCyclingTest { current.compute(previous); // then - assertEquals(0, current.getCadence_rpm(), 0.01); + assertEquals(0, current.getValue(), 0.01); } @@ -64,7 +64,7 @@ public class SensorDataCyclingTest { current.compute(previous); // then - assertFalse(current.hasCadence_rpm()); + assertFalse(current.hasValue()); } @Test @@ -77,7 +77,7 @@ public class SensorDataCyclingTest { current.compute(previous); // then - assertEquals(60, current.getCadence_rpm(), 0.01); + assertEquals(60, current.getValue(), 0.01); } @Test @@ -90,7 +90,7 @@ public class SensorDataCyclingTest { current.compute(previous); // then - assertEquals(60, current.getCadence_rpm(), 0.01); + assertEquals(60, current.getValue(), 0.01); } @Test @@ -103,7 +103,7 @@ public class SensorDataCyclingTest { current.compute(previous, 2150); // then - assertEquals(1.20, current.getSpeed_mps(), 0.01); + assertEquals(1.20, current.getValue(), 0.01); } @Test @@ -116,7 +116,7 @@ public class SensorDataCyclingTest { current.compute(previous, 2000); // then - assertEquals(2, current.getSpeed_mps(), 0.01); + assertEquals(2, current.getValue(), 0.01); } @Test diff --git a/src/main/java/de/dennisguse/opentracks/adapters/SensorsAdapter.java b/src/main/java/de/dennisguse/opentracks/adapters/SensorsAdapter.java index 77c460c05..d5b5a439f 100644 --- a/src/main/java/de/dennisguse/opentracks/adapters/SensorsAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/adapters/SensorsAdapter.java @@ -1,7 +1,6 @@ package de.dennisguse.opentracks.adapters; import android.content.Context; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -13,19 +12,11 @@ import androidx.recyclerview.widget.RecyclerView; import java.util.List; import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.content.sensor.SensorData; -import de.dennisguse.opentracks.content.sensor.SensorDataCycling; -import de.dennisguse.opentracks.content.sensor.SensorDataCyclingPower; -import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate; -import de.dennisguse.opentracks.util.StringUtils; +import de.dennisguse.opentracks.viewmodels.SensorDataModel; public class SensorsAdapter extends RecyclerView.Adapter { - public static final int HEART_RATE_TYPE = 0; - public static final int CADENCE_TYPE = 1; - public static final int POWER_TYPE = 2; - - private List> sensorDataList; + private List sensorDataList; private final Context context; public SensorsAdapter(Context context) { @@ -42,9 +33,8 @@ public class SensorsAdapter extends RecyclerView.Adapter> swapData(List> data) { + public List swapData(List data) { if (sensorDataList == data) { return null; } @@ -89,70 +74,18 @@ public class SensorsAdapter extends RecyclerView.Adapter { + + protected T value; private final String sensorAddress; private final String sensorName; @@ -44,6 +46,17 @@ public class SensorData { return sensorName != null ? sensorName : sensorAddress; } + public boolean hasValue() { + return value != null; + } + + public T getValue() { + return value; + } + + /** + * Is the data recent considering the current time. + */ public boolean isRecent() { return Instant.now().isBefore(timestamp_ms.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS)); } diff --git a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCycling.java b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCycling.java index 4ee0e63cc..2fc466567 100644 --- a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCycling.java +++ b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCycling.java @@ -4,6 +4,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.util.Pair; import de.dennisguse.opentracks.util.UintUtils; import de.dennisguse.opentracks.util.UnitConversions; @@ -20,11 +21,10 @@ public final class SensorDataCycling { private SensorDataCycling() { } - public static class Cadence extends SensorData { + public static class Cadence extends SensorData { private final Long crankRevolutionsCount; // UINT32 private final Integer crankRevolutionsTime; // UINT16; 1/1024s - private Float cadence_rpm; public Cadence(String sensorAddress) { super(sensorAddress); @@ -57,24 +57,16 @@ public final class SensorDataCycling { return crankRevolutionsTime; } - public boolean hasCadence_rpm() { - return cadence_rpm != null; - } - - public float getCadence_rpm() { - return cadence_rpm; - } - public void compute(Cadence previous) { if (hasData() && previous != null && previous.hasData()) { float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS; if (timeDiff_ms <= 0) { Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence."); - cadence_rpm = null; + value = null; } else { long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX); float cadence_ms = crankDiff / timeDiff_ms; - cadence_rpm = (float) (cadence_ms / UnitConversions.MS_TO_S / UnitConversions.S_TO_MIN); + value = (float) (cadence_ms / UnitConversions.MS_TO_S / UnitConversions.S_TO_MIN); } } } @@ -82,7 +74,7 @@ public final class SensorDataCycling { @NonNull @Override public String toString() { - return "cadence=" + cadence_rpm + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount; + return "cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount; } @Override @@ -98,11 +90,10 @@ public final class SensorDataCycling { } } - public static class Speed extends SensorData { + public static class Speed extends SensorData { private final Integer wheelRevolutionsCount; // UINT16 private final Integer wheelRevolutionsTime; // UINT16; 1/1024s - private Float speed_mps; public Speed(String sensorAddress) { super(sensorAddress); @@ -128,24 +119,16 @@ public final class SensorDataCycling { return wheelRevolutionsTime; } - public boolean hasSpeed_mps() { - return speed_mps != null; - } - - public float getSpeed_mps() { - return speed_mps; - } - public void compute(Speed previous, int wheel_circumference_mm) { if (hasData() && previous != null && previous.hasData()) { float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS; if (timeDiff_ms <= 0) { Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence."); - speed_mps = null; + value = null; } else { long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT16_MAX); double timeDiff_s = timeDiff_ms * UnitConversions.MS_TO_S; - speed_mps = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M / timeDiff_s); + value = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M / timeDiff_s); } } } @@ -153,7 +136,7 @@ public final class SensorDataCycling { @NonNull @Override public String toString() { - return "speed=" + speed_mps + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount; + return "speed=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount; } @Override @@ -169,23 +152,19 @@ public final class SensorDataCycling { } } - public static class CadenceAndSpeed extends SensorData { - - private final Cadence cadence; - private final Speed speed; + public static class CadenceAndSpeed extends SensorData> { public CadenceAndSpeed(String sensorAddress, String sensorName, @NonNull Cadence cadence, @NonNull Speed speed) { super(sensorAddress, sensorName); - this.cadence = cadence; - this.speed = speed; + this.value = new Pair<>(cadence, speed); } public Cadence getCadence() { - return cadence; + return this.value != null ? this.value.first : null; } public Speed getSpeed() { - return speed; + return this.value != null ? this.value.second : null; } } } diff --git a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingPower.java b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingPower.java index 705f58695..2fa263a1f 100644 --- a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataCyclingPower.java @@ -2,31 +2,20 @@ package de.dennisguse.opentracks.content.sensor; import androidx.annotation.NonNull; -public class SensorDataCyclingPower extends SensorData { - - private final Float power_w; +public class SensorDataCyclingPower extends SensorData { public SensorDataCyclingPower(String address) { super(address); - this.power_w = null; } public SensorDataCyclingPower(String name, String address, float power_w) { super(name, address); - this.power_w = power_w; - } - - public boolean hasPower_w() { - return power_w != null; - } - - public float getPower_w() { - return power_w; + this.value = power_w; } @NonNull @Override public String toString() { - return "power=" + power_w; + return "power=" + value; } } diff --git a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataHeartRate.java b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataHeartRate.java index 8f3f145c0..b1b4db565 100644 --- a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataHeartRate.java @@ -2,31 +2,20 @@ package de.dennisguse.opentracks.content.sensor; import androidx.annotation.NonNull; -public class SensorDataHeartRate extends SensorData { - - private final Float heartRate_bpm; +public class SensorDataHeartRate extends SensorData { public SensorDataHeartRate(String address) { super(address); - heartRate_bpm = null; } public SensorDataHeartRate(String name, String address, float heartRate_bpm) { super(name, address); - this.heartRate_bpm = heartRate_bpm; - } - - public boolean hasHeartRate_bpm() { - return heartRate_bpm != null; - } - - public Float getHeartRate_bpm() { - return heartRate_bpm; + this.value = heartRate_bpm; } @NonNull @Override public String toString() { - return "heart=" + heartRate_bpm; + return "heart=" + value; } } diff --git a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataSet.java index 7d9bd402c..414ede43f 100644 --- a/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/content/sensor/SensorDataSet.java @@ -50,19 +50,19 @@ public final class SensorDataSet { public void fillTrackPoint(TrackPoint trackPoint) { if (heartRate != null) { - trackPoint.setHeartRate_bpm(heartRate.getHeartRate_bpm()); + trackPoint.setHeartRate_bpm(heartRate.getValue()); } - if (cyclingCadence != null && cyclingCadence.hasCadence_rpm()) { - trackPoint.setCyclingCadence_rpm(cyclingCadence.getCadence_rpm()); + if (cyclingCadence != null && cyclingCadence.hasValue()) { + trackPoint.setCyclingCadence_rpm(cyclingCadence.getValue()); } - if (cyclingSpeed != null && cyclingSpeed.hasSpeed_mps()) { - trackPoint.setSpeed(cyclingSpeed.getSpeed_mps()); + if (cyclingSpeed != null && cyclingSpeed.hasValue()) { + trackPoint.setSpeed(cyclingSpeed.getValue()); } - if (cyclingPower != null && cyclingPower.hasPower_w()) { - trackPoint.setPower(cyclingPower.getPower_w()); + if (cyclingPower != null && cyclingPower.hasValue()) { + trackPoint.setPower(cyclingPower.getValue()); } } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java index 32571318b..253cbc5fb 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java @@ -27,15 +27,23 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.adapters.SensorsAdapter; import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.databinding.StatisticsRecordedBinding; +import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.TrackIconUtils; +import de.dennisguse.opentracks.viewmodels.SensorDataModel; /** * A fragment to display track statistics to the user for a recorded {@link Track}. @@ -49,6 +57,8 @@ public class StatisticsRecordedFragment extends Fragment { private static final String TRACK_ID_KEY = "trackId"; private TrackStatistics trackStatistics; + private SensorStatistics sensorStatistics; + private SensorsAdapter sensorsAdapter; private String category = ""; private Track.Id trackId; private ContentProviderUtils contentProviderUtils; @@ -67,12 +77,6 @@ public class StatisticsRecordedFragment extends Fragment { } }; - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - viewBinding = StatisticsRecordedBinding.inflate(inflater, container, false); - return viewBinding.getRoot(); - } - public static StatisticsRecordedFragment newInstance(Track.Id trackId) { Bundle bundle = new Bundle(); bundle.putParcelable(TRACK_ID_KEY, trackId); @@ -82,6 +86,22 @@ public class StatisticsRecordedFragment extends Fragment { return fragment; } + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + viewBinding = StatisticsRecordedBinding.inflate(inflater, container, false); + return viewBinding.getRoot(); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + sensorsAdapter = new SensorsAdapter(getContext()); + RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView; + sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); + sensorsRecyclerView.setAdapter(sensorsAdapter); + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -112,8 +132,10 @@ public class StatisticsRecordedFragment extends Fragment { if (isResumed()) { Track track = contentProviderUtils.getTrack(trackId); trackStatistics = track != null ? track.getTrackStatistics() : null; + sensorStatistics = contentProviderUtils.getSensorStats(trackId); category = track != null ? track.getCategory() : ""; updateUI(); + updateSensorUI(); } }); } @@ -196,4 +218,26 @@ public class StatisticsRecordedFragment extends Fragment { viewBinding.statsElevationLossUnit.setText(parts.second); } } + + private void updateSensorUI() { + if (sensorStatistics == null) { + return; + } + + List sensorDataList = new ArrayList<>(); + if (sensorStatistics.hasHeartRate()) { + sensorDataList.add(new SensorDataModel(R.string.sensor_state_heart_rate_max, R.string.sensor_unit_beats_per_minute, sensorStatistics.getMaxHeartRate())); + sensorDataList.add(new SensorDataModel(R.string.sensor_state_heart_rate_avg, R.string.sensor_unit_beats_per_minute, sensorStatistics.getAvgHeartRate())); + } + if (sensorStatistics.hasCadence()) { + sensorDataList.add(new SensorDataModel(R.string.sensor_state_cadence_max, R.string.sensor_unit_rounds_per_minute, sensorStatistics.getMaxCadence())); + sensorDataList.add(new SensorDataModel(R.string.sensor_state_cadence_avg, R.string.sensor_unit_rounds_per_minute, sensorStatistics.getAvgCadence())); + } + if (sensorStatistics.hasPower()) { + sensorDataList.add(new SensorDataModel(R.string.sensor_state_power_avg, R.string.sensor_unit_power, sensorStatistics.getAvgPower())); + } + if (sensorDataList.size() > 0) { + sensorsAdapter.swapData(sensorDataList); + } + } } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java index 1e0e295dd..9140f4980 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java @@ -38,6 +38,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.UnitConversions; +import de.dennisguse.opentracks.viewmodels.SensorDataModel; /** * A fragment to display track statistics to the user for a currently recording {@link Track}. @@ -272,15 +273,15 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi } else { SensorDataSet sensorDataSet = trackRecordingService.getSensorData(); if (sensorDataSet != null) { - List> sensorDataList = new ArrayList<>(); + List sensorDataList = new ArrayList<>(); if (sensorDataSet.getHeartRate() != null) { - sensorDataList.add(new Pair<>(SensorsAdapter.HEART_RATE_TYPE, sensorDataSet.getHeartRate())); + sensorDataList.add(new SensorDataModel(sensorDataSet.getHeartRate())); } if (sensorDataSet.getCyclingCadence() != null) { - sensorDataList.add(new Pair<>(SensorsAdapter.CADENCE_TYPE, sensorDataSet.getCyclingCadence())); + sensorDataList.add(new SensorDataModel(sensorDataSet.getCyclingCadence())); } if(sensorDataSet.getCyclingPower() != null) { - sensorDataList.add(new Pair<>(SensorsAdapter.POWER_TYPE, sensorDataSet.getCyclingPower())); + sensorDataList.add(new SensorDataModel(sensorDataSet.getCyclingPower())); } sensorsAdapter.swapData(sensorDataList); setSpeedSensorData(sensorDataSet); @@ -333,8 +334,8 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi if (sensorDataSet != null && sensorDataSet.getCyclingSpeed() != null) { SensorDataCycling.Speed data = sensorDataSet.getCyclingSpeed(); - if (data.hasSpeed_mps() && data.isRecent()) { - setSpeed(data.getSpeed_mps()); + if (data.hasValue() && data.isRecent()) { + setSpeed(data.getValue()); } } } diff --git a/src/main/java/de/dennisguse/opentracks/stats/SensorStatistics.java b/src/main/java/de/dennisguse/opentracks/stats/SensorStatistics.java new file mode 100644 index 000000000..44cff4860 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/stats/SensorStatistics.java @@ -0,0 +1,49 @@ +package de.dennisguse.opentracks.stats; + +public class SensorStatistics { + private Float maxHr; + private Float avgHr; + private Float maxCadence; + private Float avgCadence; + private Float avgPower; + + public SensorStatistics(Float maxHr, Float avgHr, Float maxCadence, Float avgCadence, Float avgPower) { + this.maxHr = maxHr; + this.avgHr = avgHr; + this.maxCadence = maxCadence; + this.avgCadence = avgCadence; + this.avgPower = avgPower; + } + + public boolean hasHeartRate() { + return maxHr != null; + } + + public float getMaxHeartRate() { + return maxHr; + } + + public float getAvgHeartRate() { + return avgHr; + } + + public boolean hasCadence() { + return maxCadence != null; + } + + public float getMaxCadence() { + return maxCadence; + } + + public float getAvgCadence() { + return avgCadence; + } + + public boolean hasPower() { + return avgPower != null; + } + + public float getAvgPower() { + return avgPower; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/SensorDataModel.java b/src/main/java/de/dennisguse/opentracks/viewmodels/SensorDataModel.java new file mode 100644 index 000000000..3aa16b31e --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/SensorDataModel.java @@ -0,0 +1,56 @@ +package de.dennisguse.opentracks.viewmodels; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.content.sensor.SensorData; +import de.dennisguse.opentracks.content.sensor.SensorDataCycling; +import de.dennisguse.opentracks.content.sensor.SensorDataCyclingPower; +import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate; +import de.dennisguse.opentracks.util.StringUtils; + +public class SensorDataModel { + private int labelId; + private String sensorValue; + private String sensorName; + private int unitId; + + public SensorDataModel(int labelId, int unitId, float sensorValue) { + this.labelId = labelId; + this.unitId = unitId; + this.sensorValue = StringUtils.formatDecimal(sensorValue, 0); + } + + public SensorDataModel(SensorData sensorData) { + this.sensorName = sensorData.getSensorNameOrAddress(); + this.sensorValue = sensorData.hasValue() && sensorData.isRecent() ? StringUtils.formatDecimal((float) sensorData.getValue(), 0) : null; + if (sensorData instanceof SensorDataHeartRate) { + this.labelId = R.string.sensor_state_heart_rate; + this.unitId = R.string.sensor_unit_beats_per_minute; + } else if (sensorData instanceof SensorDataCycling.Cadence) { + this.labelId = R.string.sensor_state_cadence; + this.unitId = R.string.sensor_unit_rounds_per_minute; + } else if (sensorData instanceof SensorDataCyclingPower) { + this.labelId = R.string.sensor_state_power; + this.unitId = R.string.sensor_unit_power; + } + } + + public int getLabelId() { + return labelId; + } + + public int getUnitId() { + return unitId; + } + + public String getSensorName() { + return sensorName; + } + + public String getSensorValue() { + return sensorValue; + } + + public boolean hasValue() { + return sensorValue != null; + } +} diff --git a/src/main/res/layout/statistics_recorded.xml b/src/main/res/layout/statistics_recorded.xml index 808e710f3..7514af11f 100644 --- a/src/main/res/layout/statistics_recorded.xml +++ b/src/main/res/layout/statistics_recorded.xml @@ -313,5 +313,14 @@ app:layout_constraintEnd_toStartOf="@+id/guideline3" app:layout_constraintTop_toBottomOf="@+id/stats_elevation_barrier" /> + + + \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index a883c243d..7fb5ad9af 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -360,11 +360,16 @@ limitations under the License. Battery level Cadence + Avg Cadence + Max Cadence %1$d rpm Disconnected Heart rate + Avg Heart rate + Max Heart rate %1$d bpm Power + Avg Power %1$d W Sensor %1$s is unknown. Please check the settings. Could not scan for Bluetooth devices (error %1$i).