Garmin/Zepp OS: Display resting HR

This commit is contained in:
José Rebelo 2024-10-12 13:21:20 +01:00
parent fb1d0a92cc
commit bd3b7467a4
6 changed files with 48 additions and 14 deletions

View File

@ -12,7 +12,6 @@ import androidx.core.content.ContextCompat;
import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.LegendEntry; import com.github.mikephil.charting.components.LegendEntry;
import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis;
@ -23,6 +22,7 @@ import com.github.mikephil.charting.data.LineDataSet;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -35,6 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDailyFragment.HeartRateData> { public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDailyFragment.HeartRateData> {
@ -50,7 +51,6 @@ public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDaily
private TextView hrAverage; private TextView hrAverage;
private TextView hrMinimum; private TextView hrMinimum;
private TextView hrMaximum; private TextView hrMaximum;
private LinearLayout heartRateRestingWrapper;
private LineChart hrLineChart; private LineChart hrLineChart;
@ -70,21 +70,22 @@ public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDaily
hrAverage = rootView.findViewById(R.id.hr_average); hrAverage = rootView.findViewById(R.id.hr_average);
hrMinimum = rootView.findViewById(R.id.hr_minimum); hrMinimum = rootView.findViewById(R.id.hr_minimum);
hrMaximum = rootView.findViewById(R.id.hr_maximum); hrMaximum = rootView.findViewById(R.id.hr_maximum);
heartRateRestingWrapper = rootView.findViewById(R.id.hr_resting_wrapper); final LinearLayout heartRateRestingWrapper = rootView.findViewById(R.id.hr_resting_wrapper);
setupChart(); setupChart();
refresh(); refresh();
setupLegend(hrLineChart); setupLegend(hrLineChart);
if (!supportHeartRateRestingMeasurement()) { if (!supportsHeartRateRestingMeasurement()) {
heartRateRestingWrapper.setVisibility(View.GONE); heartRateRestingWrapper.setVisibility(View.GONE);
} }
return rootView; return rootView;
} }
public boolean supportHeartRateRestingMeasurement() { public boolean supportsHeartRateRestingMeasurement() {
return false; final GBDevice device = getChartsHost().getDevice();
return device.getDeviceCoordinator().supportsHeartRateRestingMeasurement(device);
} }
protected List<? extends AbstractActivitySample> getActivitySamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) { protected List<? extends AbstractActivitySample> getActivitySamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
@ -124,7 +125,19 @@ public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDaily
String formattedDate = new SimpleDateFormat("E, MMM dd").format(date); String formattedDate = new SimpleDateFormat("E, MMM dd").format(date);
mDateView.setText(formattedDate); mDateView.setText(formattedDate);
List<? extends ActivitySample> samples = getActivitySamples(db, device, startTs, endTs); List<? extends ActivitySample> samples = getActivitySamples(db, device, startTs, endTs);
return new HeartRateData(samples);
int restingHeartRate = -1;
if (supportsHeartRateRestingMeasurement()) {
restingHeartRate = device.getDeviceCoordinator()
.getHeartRateRestingSampleProvider(device, db.getDaoSession())
.getAllSamples(startTs * 1000L, endTs * 1000L)
.stream()
.max(Comparator.comparingLong(HeartRateSample::getTimestamp))
.map(HeartRateSample::getHeartRate)
.orElse(-1);
}
return new HeartRateData(samples, restingHeartRate);
} }
@Override @Override
@ -186,7 +199,6 @@ public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDaily
final List<Entry> lineEntries = new ArrayList<>(); final List<Entry> lineEntries = new ArrayList<>();
List<? extends ActivitySample> samples = data.samples; List<? extends ActivitySample> samples = data.samples;
int average = 0; int average = 0;
int resting = 0;
int minimum = 0; int minimum = 0;
int maximum = 0; int maximum = 0;
int sum = 0; int sum = 0;
@ -233,7 +245,7 @@ public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDaily
hrAverage.setText(average > 0 ? getString(R.string.bpm_value_unit, average) : "-"); hrAverage.setText(average > 0 ? getString(R.string.bpm_value_unit, average) : "-");
hrMinimum.setText(minimum > 0 ? getString(R.string.bpm_value_unit, minimum) : "-"); hrMinimum.setText(minimum > 0 ? getString(R.string.bpm_value_unit, minimum) : "-");
hrMaximum.setText(maximum > 0 ? getString(R.string.bpm_value_unit, maximum) : "-"); hrMaximum.setText(maximum > 0 ? getString(R.string.bpm_value_unit, maximum) : "-");
hrResting.setText(resting > 0 ? getString(R.string.bpm_value_unit, resting) : "-"); hrResting.setText(data.restingHeartRate > 0 ? getString(R.string.bpm_value_unit, data.restingHeartRate) : "-");
if (minimum > 0) { if (minimum > 0) {
@ -251,9 +263,11 @@ public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDaily
protected static class HeartRateData extends ChartsData { protected static class HeartRateData extends ChartsData {
public List<? extends ActivitySample> samples; public List<? extends ActivitySample> samples;
public int restingHeartRate;
protected HeartRateData(List<? extends ActivitySample> samples) { protected HeartRateData(List<? extends ActivitySample> samples, int restingHeartRate) {
this.samples = samples; this.samples = samples;
this.restingHeartRate = restingHeartRate;
} }
} }
} }

View File

@ -643,6 +643,11 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return false; return false;
} }
@Override
public boolean supportsHeartRateRestingMeasurement(final GBDevice device) {
return false;
}
@Override @Override
public boolean supportsManualHeartRateMeasurement(final GBDevice device) { public boolean supportsManualHeartRateMeasurement(final GBDevice device) {
return supportsHeartRateMeasurement(device); return supportsHeartRateMeasurement(device);

View File

@ -462,6 +462,11 @@ public interface DeviceCoordinator {
*/ */
boolean supportsHeartRateMeasurement(GBDevice device); boolean supportsHeartRateMeasurement(GBDevice device);
/**
* Returns true if the given device supports resting heart rate measurements.
*/
boolean supportsHeartRateRestingMeasurement(GBDevice device);
/** /**
* Returns true if the device supports triggering manual one-shot heart rate measurements. * Returns true if the device supports triggering manual one-shot heart rate measurements.
*/ */

View File

@ -259,6 +259,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
return true; return true;
} }
@Override
public boolean supportsHeartRateRestingMeasurement(final GBDevice device) {
return true;
}
@Override @Override
public boolean supportsRealtimeData() { public boolean supportsRealtimeData() {
return true; return true;

View File

@ -120,6 +120,11 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
return true; return true;
} }
@Override
public boolean supportsHeartRateRestingMeasurement(final GBDevice device) {
return true;
}
@Override @Override
public int[] getSupportedDeviceSpecificAuthenticationSettings() { public int[] getSupportedDeviceSpecificAuthenticationSettings() {
return new int[]{R.xml.devicesettings_pairingkey}; return new int[]{R.xml.devicesettings_pairingkey};

View File

@ -45,7 +45,7 @@
android:id="@+id/hr_minimum" android:id="@+id/hr_minimum"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="0" android:text="@string/stats_empty_value"
android:textSize="20sp" /> android:textSize="20sp" />
<TextView <TextView
@ -65,7 +65,7 @@
android:id="@+id/hr_maximum" android:id="@+id/hr_maximum"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="0" android:text="@string/stats_empty_value"
android:textSize="20sp" /> android:textSize="20sp" />
<TextView <TextView
@ -83,7 +83,7 @@
android:id="@+id/hr_average" android:id="@+id/hr_average"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="0" android:text="@string/stats_empty_value"
android:textSize="20sp" /> android:textSize="20sp" />
<TextView <TextView
@ -107,7 +107,7 @@
android:id="@+id/hr_resting" android:id="@+id/hr_resting"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="0" android:text="@string/stats_empty_value"
android:textSize="20sp" /> android:textSize="20sp" />
<TextView <TextView