mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-08-25 00:30:37 +02:00
Initial live heartrate measurement in the live activity tab #178
This commit is contained in:
parent
58d90c2a66
commit
f15a97d994
@ -0,0 +1,6 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
public class HeartRateUtils {
|
||||
public static final int MAX_HEART_RATE_VALUE = 250;
|
||||
public static final int MIN_HEART_RATE_VALUE = 0;
|
||||
}
|
@ -39,6 +39,7 @@ import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
@ -116,6 +117,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
protected int CHART_TEXT_COLOR;
|
||||
protected int LEGEND_TEXT_COLOR;
|
||||
protected int HEARTRATE_COLOR;
|
||||
protected int HEARTRATE_FILL_COLOR;
|
||||
protected int AK_ACTIVITY_COLOR;
|
||||
protected int AK_DEEP_SLEEP_COLOR;
|
||||
protected int AK_LIGHT_SLEEP_COLOR;
|
||||
@ -152,6 +154,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
CHART_TEXT_COLOR = getResources().getColor(R.color.secondarytext);
|
||||
LEGEND_TEXT_COLOR = getResources().getColor(R.color.primarytext);
|
||||
HEARTRATE_COLOR = getResources().getColor(R.color.chart_heartrate);
|
||||
HEARTRATE_FILL_COLOR = getResources().getColor(R.color.chart_heartrate_fill);
|
||||
AK_ACTIVITY_COLOR = getResources().getColor(R.color.chart_activity_light);
|
||||
AK_DEEP_SLEEP_COLOR = getResources().getColor(R.color.chart_light_sleep_light);
|
||||
AK_LIGHT_SLEEP_COLOR = getResources().getColor(R.color.chart_deep_sleep_light);
|
||||
@ -518,7 +521,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
}
|
||||
|
||||
protected boolean isValidHeartRateValue(int value) {
|
||||
return value > 0 && value < 255;
|
||||
return value > HeartRateUtils.MIN_HEART_RATE_VALUE && value < HeartRateUtils.MAX_HEART_RATE_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
@ -83,8 +84,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
||||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(true);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaxValue(250);
|
||||
yAxisRight.setAxisMinValue(0);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
|
||||
// refresh immediately instead of use refreshIfVisible(), for perceived performance
|
||||
refresh();
|
||||
|
@ -23,6 +23,7 @@ import com.github.mikephil.charting.components.YAxis;
|
||||
import com.github.mikephil.charting.data.BarData;
|
||||
import com.github.mikephil.charting.data.BarDataSet;
|
||||
import com.github.mikephil.charting.data.BarEntry;
|
||||
import com.github.mikephil.charting.data.ChartData;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.data.LineData;
|
||||
import com.github.mikephil.charting.data.LineDataSet;
|
||||
@ -39,10 +40,12 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Measurement;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class LiveActivityFragment extends AbstractChartFragment {
|
||||
@ -63,6 +66,9 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
private final Steps mSteps = new Steps();
|
||||
private ScheduledExecutorService pulseScheduler;
|
||||
private int maxStepsResetCounter;
|
||||
private List<Measurement> heartRateValues;
|
||||
private LineDataSet mHeartRateSet;
|
||||
private int mHeartRate;
|
||||
|
||||
private class Steps {
|
||||
private int initialSteps;
|
||||
@ -145,16 +151,36 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
switch (action) {
|
||||
case DeviceService.ACTION_REALTIME_STEPS:
|
||||
case DeviceService.ACTION_REALTIME_STEPS: {
|
||||
int steps = intent.getIntExtra(DeviceService.EXTRA_REALTIME_STEPS, 0);
|
||||
long timestamp = intent.getLongExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
refreshCurrentSteps(steps, timestamp);
|
||||
addEntries(steps, timestamp);
|
||||
break;
|
||||
}
|
||||
case DeviceService.ACTION_HEARTRATE_MEASUREMENT: {
|
||||
int heartRate = intent.getIntExtra(DeviceService.EXTRA_HEART_RATE_VALUE, 0);
|
||||
long timestamp = intent.getLongExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
if (isValidHeartRateValue(heartRate)) {
|
||||
setCurrentHeartRate(heartRate, timestamp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void refreshCurrentSteps(int steps, long timestamp) {
|
||||
private void setCurrentHeartRate(int heartRate, long timestamp) {
|
||||
addHistoryDataSet(true);
|
||||
mHeartRate = heartRate;
|
||||
}
|
||||
|
||||
private int getCurrentHeartRate() {
|
||||
int result = mHeartRate;
|
||||
mHeartRate = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addEntries(int steps, long timestamp) {
|
||||
mSteps.updateCurrentSteps(steps, timestamp);
|
||||
if (++maxStepsResetCounter > RESET_COUNT) {
|
||||
maxStepsResetCounter = 0;
|
||||
@ -163,10 +189,10 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
// Or: count down the steps until goal reached? And then flash GOAL REACHED -> Set stretch goal
|
||||
LOG.info("Steps: " + steps + ", total: " + mSteps.getTotalSteps() + ", current: " + mSteps.getStepsPerMinute(false));
|
||||
|
||||
// refreshCurrentSteps();
|
||||
// addEntries();
|
||||
}
|
||||
|
||||
private void refreshCurrentSteps() {
|
||||
private void addEntries() {
|
||||
mTotalStepsChart.setSingleEntryYValue(mSteps.getTotalSteps());
|
||||
YAxis stepsPerMinuteCurrentYAxis = mStepsPerMinuteCurrentChart.getAxisLeft();
|
||||
int maxStepsPerMinute = mSteps.getMaxStepsPerMinute();
|
||||
@ -180,24 +206,36 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
int stepsPerMinute = mSteps.getStepsPerMinute(true);
|
||||
mStepsPerMinuteCurrentChart.setSingleEntryYValue(stepsPerMinute);
|
||||
|
||||
if (!addHistoryDataSet(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChartData data = mStepsPerMinuteHistoryChart.getData();
|
||||
data.addXValue("");
|
||||
if (stepsPerMinute < 0) {
|
||||
stepsPerMinute = 0;
|
||||
}
|
||||
mHistorySet.addEntry(new Entry(stepsPerMinute, data.getXValCount() - 1));
|
||||
int hr = getCurrentHeartRate();
|
||||
if (hr < 0) {
|
||||
hr = 0;
|
||||
}
|
||||
mHeartRateSet.addEntry(new Entry(hr, data.getXValCount() - 1));
|
||||
}
|
||||
|
||||
private boolean addHistoryDataSet(boolean force) {
|
||||
if (mStepsPerMinuteHistoryChart.getData() == null) {
|
||||
if (mSteps.getTotalSteps() == 0) {
|
||||
return; // ignore the first default value to keep the "no-data-description" visible
|
||||
}
|
||||
// ignore the first default value to keep the "no-data-description" visible
|
||||
if (force || mSteps.getTotalSteps() > 0) {
|
||||
LineData data = new LineData();
|
||||
mStepsPerMinuteHistoryChart.setData(data);
|
||||
data.addDataSet(mHistorySet);
|
||||
data.addDataSet(mHeartRateSet);
|
||||
mStepsPerMinuteHistoryChart.setData(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
LineData historyData = (LineData) mStepsPerMinuteHistoryChart.getData();
|
||||
historyData.addXValue("");
|
||||
historyData.addEntry(new Entry(stepsPerMinute, mHistorySet.getEntryCount()), 0);
|
||||
|
||||
mTotalStepsData.notifyDataSetChanged();
|
||||
mStepsPerMinuteData.notifyDataSetChanged();
|
||||
mStepsPerMinuteHistoryChart.notifyDataSetChanged();
|
||||
|
||||
renderCharts();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -205,6 +243,8 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
IntentFilter filterLocal = new IntentFilter();
|
||||
filterLocal.addAction(DeviceService.ACTION_REALTIME_STEPS);
|
||||
filterLocal.addAction(DeviceService.ACTION_HEARTRATE_MEASUREMENT);
|
||||
heartRateValues = new ArrayList<>();
|
||||
|
||||
View rootView = inflater.inflate(R.layout.fragment_live_activity, container, false);
|
||||
|
||||
@ -262,7 +302,22 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
* Called in the UI thread.
|
||||
*/
|
||||
private void pulse() {
|
||||
refreshCurrentSteps();
|
||||
addEntries();
|
||||
|
||||
LineData historyData = (LineData) mStepsPerMinuteHistoryChart.getData();
|
||||
if (historyData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
historyData.notifyDataChanged();
|
||||
mTotalStepsData.notifyDataSetChanged();
|
||||
mStepsPerMinuteData.notifyDataSetChanged();
|
||||
mStepsPerMinuteHistoryChart.notifyDataSetChanged();
|
||||
|
||||
renderCharts();
|
||||
|
||||
// have to enable it again and again to keep it measureing
|
||||
GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(true);
|
||||
}
|
||||
|
||||
private long getPulseIntervalMillis() {
|
||||
@ -272,6 +327,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
@Override
|
||||
protected void onMadeVisibleInActivity() {
|
||||
GBApplication.deviceService().onEnableRealtimeSteps(true);
|
||||
GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(true);
|
||||
super.onMadeVisibleInActivity();
|
||||
if (getActivity() != null) {
|
||||
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
@ -281,6 +337,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
@Override
|
||||
protected void onMadeInvisibleInActivity() {
|
||||
GBApplication.deviceService().onEnableRealtimeSteps(false);
|
||||
GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(false);
|
||||
if (getActivity() != null) {
|
||||
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
@ -346,6 +403,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
private void setupHistoryChart(BarLineChartBase chart) {
|
||||
configureBarLineChartDefaults(chart);
|
||||
|
||||
chart.setTouchEnabled(false); // no zooming or anything, because it's updated all the time
|
||||
chart.setBackgroundColor(BACKGROUND_COLOR);
|
||||
chart.setDescriptionColor(DESCRIPTION_COLOR);
|
||||
chart.setDescription(getString(R.string.live_activity_steps_per_minute_history));
|
||||
@ -367,22 +425,34 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
y.setDrawGridLines(false);
|
||||
y.setDrawTopYLabelEntry(false);
|
||||
y.setTextColor(CHART_TEXT_COLOR);
|
||||
|
||||
y.setEnabled(true);
|
||||
y.setAxisMinValue(0);
|
||||
|
||||
YAxis yAxisRight = chart.getAxisRight();
|
||||
yAxisRight.setDrawGridLines(false);
|
||||
yAxisRight.setEnabled(false);
|
||||
yAxisRight.setDrawLabels(false);
|
||||
yAxisRight.setEnabled(true);
|
||||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(false);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
|
||||
mHistorySet = new LineDataSet(new ArrayList<Entry>(), getString(R.string.live_activity_steps_history));
|
||||
mHistorySet.setAxisDependency(YAxis.AxisDependency.LEFT);
|
||||
mHistorySet.setColor(akActivity.color);
|
||||
mHistorySet.setDrawCircles(false);
|
||||
mHistorySet.setDrawCubic(true);
|
||||
mHistorySet.setDrawFilled(true);
|
||||
mHistorySet.setDrawValues(false);
|
||||
|
||||
mHeartRateSet = new LineDataSet(new ArrayList<Entry>(), getString(R.string.live_activity_heart_rate));
|
||||
mHeartRateSet.setAxisDependency(YAxis.AxisDependency.RIGHT);
|
||||
mHeartRateSet.setColor(HEARTRATE_COLOR);
|
||||
mHeartRateSet.setDrawCircles(false);
|
||||
mHeartRateSet.setDrawCubic(true);
|
||||
mHeartRateSet.setDrawFilled(false);
|
||||
// mHeartRateSet.setFillColor(HEARTRATE_FILL_COLOR);
|
||||
mHeartRateSet.setDrawValues(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,6 +27,7 @@ import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount;
|
||||
@ -174,8 +175,8 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(true);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaxValue(250);
|
||||
yAxisRight.setAxisMinValue(0);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
}
|
||||
|
||||
protected void setupLegend(Chart chart) {
|
||||
|
@ -44,6 +44,8 @@ public interface EventHandler {
|
||||
|
||||
void onHeartRateTest();
|
||||
|
||||
void onEnableRealtimeHeartRateMeasurement(boolean enable);
|
||||
|
||||
void onFindDevice(boolean start);
|
||||
|
||||
void onScreenshotReq();
|
||||
|
@ -216,4 +216,11 @@ public class GBDeviceService implements DeviceService {
|
||||
.putExtra(EXTRA_BOOLEAN_ENABLE, enable);
|
||||
invokeService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
Intent intent = createIntent().setAction(ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT)
|
||||
.putExtra(EXTRA_BOOLEAN_ENABLE, enable);
|
||||
invokeService(intent);
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,9 @@ public interface DeviceService extends EventHandler {
|
||||
String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms";
|
||||
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
|
||||
String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps";
|
||||
String ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT = PREFIX + ".action.realtime_hr_measurement";
|
||||
String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support";
|
||||
String ACTION_HEARTRATE_MEASUREMENT = PREFIX + ".action.hr_measurement";
|
||||
String EXTRA_DEVICE_ADDRESS = "device_address";
|
||||
String EXTRA_NOTIFICATION_BODY = "notification_body";
|
||||
String EXTRA_NOTIFICATION_FLAGS = "notification_flags";
|
||||
@ -62,6 +64,7 @@ public interface DeviceService extends EventHandler {
|
||||
String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps";
|
||||
String EXTRA_REALTIME_STEPS = "realtime_steps";
|
||||
String EXTRA_TIMESTAMP = "timestamp";
|
||||
String EXTRA_HEART_RATE_VALUE = "hr_value";
|
||||
|
||||
void start();
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
public class Measurement {
|
||||
private final int value;
|
||||
private final long timestamp;
|
||||
|
||||
public Measurement(int value, long timestamp) {
|
||||
this.value = value;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (71 ^ value ^ timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof Measurement) {
|
||||
Measurement m = (Measurement) o;
|
||||
return timestamp == m.timestamp && value == m.value;
|
||||
}
|
||||
return super.equals(o);
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CO
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE;
|
||||
@ -349,6 +350,11 @@ public class DeviceCommunicationService extends Service {
|
||||
mDeviceSupport.onEnableHeartRateSleepSupport(enable);
|
||||
break;
|
||||
}
|
||||
case ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT: {
|
||||
boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false);
|
||||
mDeviceSupport.onEnableRealtimeHeartRateMeasurement(enable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return START_STICKY;
|
||||
|
@ -250,4 +250,12 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
||||
}
|
||||
delegate.onEnableHeartRateSleepSupport(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
if (checkBusy("enable realtime heart rate measurement: " + enable)) {
|
||||
return;
|
||||
}
|
||||
delegate.onEnableRealtimeHeartRateMeasurement(enable);
|
||||
}
|
||||
}
|
||||
|
@ -603,7 +603,6 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
TransactionBuilder builder = performInitialized("HeartRateTest");
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual);
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
@ -614,6 +613,25 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
if (supportsHeartRate()) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement");
|
||||
if (enable) {
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
}
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsHeartRate() {
|
||||
return getDeviceInfo() != null && getDeviceInfo().supportsHeartrate();
|
||||
}
|
||||
@ -745,7 +763,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
handleRealtimeSteps(characteristic.getValue());
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
logHeartrate(characteristic.getValue());
|
||||
handleHeartrate(characteristic.getValue());
|
||||
} else {
|
||||
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
|
||||
logMessageContent(characteristic.getValue());
|
||||
@ -814,6 +832,15 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleHeartrate(byte[] value) {
|
||||
if (value.length == 2 && value[0] == 6) {
|
||||
int hrValue = (value[1] & 0xff);
|
||||
Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT)
|
||||
.putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, hrValue)
|
||||
.putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRealtimeSteps(byte[] value) {
|
||||
int steps = 0xff & value[0] | (0xff & value[1]) << 8;
|
||||
|
@ -181,4 +181,10 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
|
||||
byte[] bytes = gbDeviceProtocol.encodeEnableHeartRateSleepSupport(enable);
|
||||
sendToDevice(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeHeartRateMeasurement(enable);
|
||||
sendToDevice(bytes);
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ public abstract class GBDeviceProtocol {
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] encodeEnableRealtimeHeartRateMeasurement(boolean enable) { return null; }
|
||||
|
||||
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
|
||||
return null;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
<color name="divider" type="color">#1f000000</color>
|
||||
|
||||
<color name="chart_heartrate" type="color">#ffab40</color>
|
||||
<color name="chart_heartrate_fill" type="color">#fadab1</color>
|
||||
<color name="chart_deep_sleep_light" type="color">#0071b7</color>
|
||||
<color name="chart_deep_sleep_dark" type="color">#4c5aff</color>
|
||||
|
||||
|
@ -240,5 +240,6 @@
|
||||
<string name="updatefirmwareoperation_update_in_progress">Firmware update in progress</string>
|
||||
<string name="updatefirmwareoperation_firmware_not_sent">Firmware not sent</string>
|
||||
<string name="charts_legend_heartrate">Heart Rate</string>
|
||||
<string name="live_activity_heart_rate">Heart Rate</string>
|
||||
|
||||
</resources>
|
||||
|
@ -129,4 +129,9 @@ public class TestDeviceSupport extends AbstractDeviceSupport {
|
||||
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user