Track recorded stats shows sensor data from trackpoints table.

Heart rate: maximum and average.
Cadence: maximum and average.
Power: average.
Fixes #525 and #502.
This commit is contained in:
Román Martínez
2021-01-31 17:23:47 +01:00
parent bb38d274fb
commit a9624f72e7
14 changed files with 267 additions and 171 deletions
@@ -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<RecyclerView.ViewHolder> {
public static final int HEART_RATE_TYPE = 0;
public static final int CADENCE_TYPE = 1;
public static final int POWER_TYPE = 2;
private List<Pair<Integer, SensorData>> sensorDataList;
private List<SensorDataModel> sensorDataList;
private final Context context;
public SensorsAdapter(Context context) {
@@ -42,9 +33,8 @@ public class SensorsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
SensorsAdapter.ViewHolder viewHolder = (SensorsAdapter.ViewHolder) holder;
int type = sensorDataList.get(position).first;
SensorData sensorData = sensorDataList.get(position).second;
viewHolder.setData(sensorData, type);
SensorDataModel sensorDataModel = sensorDataList.get(position);
viewHolder.setData(sensorDataModel);
}
@Override
@@ -56,12 +46,7 @@ public class SensorsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
}
}
@Override
public int getItemViewType(int position) {
return sensorDataList.get(position).first;
}
public List<Pair<Integer, SensorData>> swapData(List<Pair<Integer, SensorData>> data) {
public List<SensorDataModel> swapData(List<SensorDataModel> data) {
if (sensorDataList == data) {
return null;
}
@@ -89,70 +74,18 @@ public class SensorsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
unit = itemView.findViewById(R.id.stats_sensor_unit);
}
public void setData(SensorData sensorData, int type) {
switch (type) {
case HEART_RATE_TYPE:
setHeartRateSensorData((SensorDataHeartRate) sensorData);
break;
case CADENCE_TYPE:
setCadenceSensorData((SensorDataCycling.Cadence) sensorData);
break;
case POWER_TYPE:
setPowerSensorData((SensorDataCyclingPower) sensorData);
break;
default:
throw new RuntimeException("Unknown sensor type");
}
}
public void setData(SensorDataModel sensorDataModel) {
String sensorName = sensorDataModel.getSensorName();
String sensorValue = sensorDataModel.hasValue() ? sensorDataModel.getSensorValue() : context.getString(R.string.value_unknown);
private void setHeartRateSensorData(SensorDataHeartRate data) {
String sensorValue = context.getString(R.string.value_unknown);
String sensorName = context.getString(R.string.value_unknown);
if (data != null) {
sensorName = data.getSensorNameOrAddress();
if (data.hasHeartRate_bpm() && data.isRecent()) {
sensorValue = StringUtils.formatDecimal(data.getHeartRate_bpm(), 0);
}
this.label.setText(context.getString(sensorDataModel.getLabelId()));
if (sensorName == null) {
this.sensorValue.setVisibility(View.GONE);
} else {
this.sensorValue.setText(sensorName);
}
this.label.setText(context.getString(R.string.sensor_state_heart_rate));
this.sensorValue.setText(sensorName);
this.value.setText(sensorValue);
this.unit.setText(R.string.sensor_unit_beats_per_minute);
}
private void setCadenceSensorData(SensorDataCycling.Cadence data) {
String sensorValue = context.getString(R.string.value_unknown);
String sensorName = context.getString(R.string.value_unknown);
if (data != null) {
sensorName = data.getSensorNameOrAddress();
if (data.hasCadence_rpm() && data.isRecent()) {
sensorValue = StringUtils.formatDecimal(data.getCadence_rpm(), 0);
}
}
this.label.setText(context.getString(R.string.sensor_state_cadence));
this.sensorValue.setText(sensorName);
this.value.setText(sensorValue);
this.unit.setText(R.string.sensor_unit_rounds_per_minute);
}
private void setPowerSensorData(SensorDataCyclingPower data) {
String sensorValue = context.getString(R.string.value_unknown);
String sensorName = context.getString(R.string.value_unknown);
if (data != null) {
sensorName = data.getSensorNameOrAddress();
if (data.hasPower_w() && data.isRecent()) {
sensorValue = StringUtils.formatDecimal(data.getPower_w(), 0);
}
}
this.label.setText(context.getString(R.string.sensor_state_power));
this.sensorValue.setText(sensorName);
this.value.setText(sensorValue);
this.unit.setText(R.string.sensor_unit_power);
this.unit.setText(context.getString(sensorDataModel.getUnitId()));
}
}
}
@@ -44,6 +44,7 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.UUIDUtils;
@@ -835,4 +836,32 @@ public class ContentProviderUtils {
public static String[] parseTrackIdsFromUri(Uri url) {
return TextUtils.split(url.getLastPathSegment(), ID_SEPARATOR);
}
public SensorStatistics getSensorStats(Track.Id trackId) {
if (trackId == null) {
return null;
}
String[] projection = {
"MAX(" + TrackPointsColumns.SENSOR_HEARTRATE + ") max_hr",
"AVG(" + TrackPointsColumns.SENSOR_HEARTRATE + ") avg_hr",
"MAX(" + TrackPointsColumns.SENSOR_CADENCE + ") max_cadence",
"AVG(" + TrackPointsColumns.SENSOR_CADENCE + ") avg_cadence",
"AVG(" + TrackPointsColumns.SENSOR_POWER + ") avg_power"
};
String selection = TrackPointsColumns.TRACKID + "=?";
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
SensorStatistics sensorStatistics = null;
try (Cursor cursor = getTrackPointCursor(projection, selection, selectionArgs, null)) {
if (cursor != null && cursor.moveToFirst()) {
sensorStatistics = new SensorStatistics(
!cursor.isNull(cursor.getColumnIndexOrThrow("max_hr")) ? cursor.getFloat(cursor.getColumnIndexOrThrow("max_hr")) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("avg_hr")) ? cursor.getFloat(cursor.getColumnIndexOrThrow("avg_hr")) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("max_cadence")) ? cursor.getFloat(cursor.getColumnIndexOrThrow("max_cadence")) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("avg_cadence")) ? cursor.getFloat(cursor.getColumnIndexOrThrow("avg_cadence")) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("avg_power")) ? cursor.getFloat(cursor.getColumnIndexOrThrow("avg_power")) : null
);
}
}
return sensorStatistics;
}
}
@@ -8,7 +8,9 @@ import java.time.Instant;
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
public class SensorData {
public class SensorData<T> {
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));
}
@@ -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<Float> {
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<Float> {
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<Pair<Cadence, Speed>> {
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;
}
}
}
@@ -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<Float> {
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;
}
}
@@ -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<Float> {
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;
}
}
@@ -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());
}
}
@@ -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<SensorDataModel> 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);
}
}
}
@@ -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<Pair<Integer, SensorData>> sensorDataList = new ArrayList<>();
List<SensorDataModel> 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());
}
}
}
@@ -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;
}
}
@@ -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;
}
}