package com.jsbogang.womp.health; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.jsbogang.womp.ble.AlarmPrefs; import com.jsbogang.womp.ble.DevicePrefs; import com.jsbogang.womp.ble.VeepooBleManager; import com.veepoo.protocol.VPOperateManager; import com.veepoo.protocol.listener.base.IBleWriteResponse; import com.veepoo.protocol.listener.data.AbsBloodGlucoseChangeListener; import com.veepoo.protocol.listener.data.IBPDetectDataListener; import com.veepoo.protocol.listener.data.IHeartDataListener; import com.veepoo.protocol.listener.data.ISpo2hDataListener; import com.veepoo.protocol.model.datas.BpData; import com.veepoo.protocol.model.datas.HeartData; import com.veepoo.protocol.model.datas.Spo2hData; import com.veepoo.protocol.model.enums.EBPDetectModel; import com.veepoo.protocol.model.enums.EBPDetectStatus; import com.veepoo.protocol.model.enums.EBloodGlucoseRiskLevel; import com.veepoo.protocol.model.enums.EBloodGlucoseStatus; import com.veepoo.protocol.model.enums.EHeartStatus; /** * 每 30 分钟主动触发心率/血压/血氧/血糖测量。手表端用户自行测量时设备会 busy,此时跳过本轮。 */ public final class ActiveMeasureRunner { private static final String TAG = "ActiveMeasure"; private static final ActiveMeasureRunner INSTANCE = new ActiveMeasureRunner(); private static final IBleWriteResponse WR = code -> {}; private Context app; private final Handler handler = new Handler(Looper.getMainLooper()); private volatile boolean measuring; private int lastHr; private int lastBpHigh; private int lastBpLow; private float lastSpo2; private float lastGlucose; private Runnable timeoutTask; private ActiveMeasureRunner() {} public static ActiveMeasureRunner get() { return INSTANCE; } public void attach(Context context) { app = context.getApplicationContext(); } public boolean isMeasuring() { return measuring; } public void startCycle() { if (app == null || measuring || !VeepooBleManager.get().isReady()) { return; } if (HealthSyncer.get().isSyncing()) { handler.postDelayed(this::startCycle, 15_000); return; } measuring = true; lastHr = 0; lastBpHigh = 0; lastBpLow = 0; lastSpo2 = 0; lastGlucose = 0; startHeart(); } private void startHeart() { HealthSyncer.get().notifyUi("正在测量心率…"); try { VPOperateManager.getInstance().startDetectHeart(WR, new IHeartDataListener() { @Override public void onDataChange(HeartData heart) { if (heart == null) { return; } if (heart.getHeartStatus() == EHeartStatus.STATE_HEART_BUSY) { abortBusy(); return; } int v = heart.getData(); if (v >= 30 && v <= 220) { lastHr = v; } } }); } catch (Exception e) { Log.w(TAG, "start heart fail", e); handler.postDelayed(this::startBp, 800); return; } armTimeout(() -> { stopHeart(); if (lastHr > 0) { saveMetric(lastHr, 0, 0, 0, 0); } startBp(); }, 25_000); } private void stopHeart() { try { VPOperateManager.getInstance().stopDetectHeart(WR); } catch (Exception ignored) { } } private void startBp() { if (!stillOn()) { return; } HealthSyncer.get().notifyUi("正在测量血压…"); try { VPOperateManager.getInstance().startDetectBP(WR, new IBPDetectDataListener() { @Override public void onDataChange(BpData bpData) { if (bpData == null) { return; } if (bpData.getStatus() == EBPDetectStatus.STATE_BP_BUSY) { abortBusy(); return; } if (bpData.getHighPressure() > 0 && bpData.getLowPressure() > 0) { lastBpHigh = bpData.getHighPressure(); lastBpLow = bpData.getLowPressure(); } if (bpData.getProgress() >= 100 && lastBpHigh > 0) { finishBp(); } } }, EBPDetectModel.DETECT_MODEL_PUBLIC); } catch (Exception e) { Log.w(TAG, "start bp fail", e); handler.postDelayed(this::startSpo2, 800); return; } armTimeout(this::finishBp, 55_000); } private void finishBp() { clearTimeout(); try { VPOperateManager.getInstance().stopDetectBP(WR, EBPDetectModel.DETECT_MODEL_PUBLIC); } catch (Exception ignored) { } if (lastBpHigh > 0) { saveMetric(0, lastBpHigh, lastBpLow, 0, 0); } handler.postDelayed(this::startSpo2, 1200); } private void startSpo2() { if (!stillOn()) { return; } HealthSyncer.get().notifyUi("正在测量血氧…"); try { VPOperateManager.getInstance().startDetectSPO2H(WR, new ISpo2hDataListener() { @Override public void onSpO2HADataChange(Spo2hData data) { if (data == null) { return; } int v = data.getValue(); if (v >= 70 && v <= 100) { lastSpo2 = v; } if (data.getCheckingProgress() >= 100 && lastSpo2 > 0) { finishSpo2(); } } }); } catch (Exception e) { Log.w(TAG, "start spo2 fail", e); handler.postDelayed(this::startGlucose, 800); return; } armTimeout(this::finishSpo2, 50_000); } private void finishSpo2() { clearTimeout(); try { VPOperateManager.getInstance().stopDetectSPO2H(WR, data -> {}); } catch (Exception ignored) { } if (lastSpo2 > 0) { saveMetric(0, 0, 0, lastSpo2, 0); } handler.postDelayed(this::startGlucose, 1200); } private void startGlucose() { if (!stillOn()) { return; } HealthSyncer.get().notifyUi("正在测量血糖…"); try { VPOperateManager.getInstance().startBloodGlucoseDetect(WR, new AbsBloodGlucoseChangeListener() { @Override public void onDetectError(int opt, EBloodGlucoseStatus status) { finishGlucose(); } @Override public void onBloodGlucoseDetect(int progress, float bloodGlucose, EBloodGlucoseRiskLevel level) { if (bloodGlucose > 0) { lastGlucose = bloodGlucose; } if (progress >= 100) { finishGlucose(); } } @Override public void onBloodGlucoseStopDetect() { finishGlucose(); } }); } catch (Exception e) { Log.w(TAG, "start glucose fail", e); done(); return; } armTimeout(this::finishGlucose, 45_000); } private void finishGlucose() { clearTimeout(); try { VPOperateManager.getInstance().stopBloodGlucoseDetect(WR, new AbsBloodGlucoseChangeListener() {}); } catch (Exception ignored) { } if (lastGlucose > 0) { saveMetric(0, 0, 0, 0, lastGlucose); } done(); } private void abortBusy() { clearTimeout(); stopHeart(); try { VPOperateManager.getInstance().stopDetectBP(WR, EBPDetectModel.DETECT_MODEL_PUBLIC); } catch (Exception ignored) { } try { VPOperateManager.getInstance().stopDetectSPO2H(WR, data -> {}); } catch (Exception ignored) { } measuring = false; HealthSyncer.get().syncSleep(); HealthSyncer.get().notifyUi("手表正忙,本次测量已跳过"); } private void done() { clearTimeout(); measuring = false; HealthUploader.get().flush(); HealthSyncer.get().syncSleep(); HealthSyncer.get().notifyUi("测量完成"); } private boolean stillOn() { if (!measuring) { return false; } if (!VeepooBleManager.get().isReady()) { measuring = false; return false; } return true; } private void armTimeout(Runnable r, long ms) { clearTimeout(); timeoutTask = r; handler.postDelayed(timeoutTask, ms); } private void clearTimeout() { if (timeoutTask != null) { handler.removeCallbacks(timeoutTask); timeoutTask = null; } } private void saveMetric(int hr, int bpHigh, int bpLow, float spo2, float glucose) { if (app == null || AppDatabase.get() == null || !DevicePrefs.isBound(app)) { return; } HealthSample s = new HealthSample(); s.timestamp = System.currentTimeMillis(); s.deviceMac = DevicePrefs.getMac(app); s.dataType = "周期"; s.uploaded = 0; s.stepValue = 0; s.heartRate = hr; s.bpHigh = bpHigh; s.bpLow = bpLow; s.spo2 = spo2; s.glucose = glucose; if (AlarmPrefs.isAbnormal(app, hr, bpHigh, bpLow, spo2)) { s.dataType = "异常"; } AppDatabase.dao().upsertSample(s); HealthSyncer.get().notifyUi("测量数据已更新"); } }