#ifdef SUPPORT_AT_JSINSTALL\r
#include "ohosfwk_at_process.h"\r
#endif\r
+#include "bloodpressure/include/BPAtCmd.h"\r
#ifdef SUPPORT_AT_APP\r
#include "app_cmd_manager.h"\r
#endif\r
at_cmd_table_t *ohosfwk_table = get_ohosfwk_cmd_table(&ohosfwk_cmd_count);\r
at_cmd_add_moudle(ohosfwk_table, ohosfwk_cmd_count);\r
#endif\r
+ uint32_t bp_ui_cmd_count;\r
+ at_cmd_table_t *bp_ui_table = get_bp_ui_at_cmd_table(&bp_ui_cmd_count);\r
+ at_cmd_add_moudle(bp_ui_table, bp_ui_cmd_count);\r
#ifdef SUPPORT_AT_APP\r
uint32_t app_cmd_count;\r
at_cmd_table_t *app_table = get_app_cmd_table(&app_cmd_count);\r
VIEW_MAIN_ALIPAY, // 支付宝
#endif
VIEW_HEART_CHART, // 心率曲线图检测
+ VIEW_BLOOD_PRESSURE, // 血压(主入口/历史/说明)
+ VIEW_ECG, // 心电(主入口/历史/测量)
VIEW_ADD_NEW_ID = 250,
/* -------------------------------------------------------------------------- */
VIEW_RECENT_APP,
--- /dev/null
+#ifndef BP_ABNORMAL_VIEW_H
+#define BP_ABNORMAL_VIEW_H
+
+#include "components/ui_label_button.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_image_view.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+typedef enum : uint8_t {
+ BP_ABNORMAL_TYPE_NOT_CALIBRATED = 0,//未校准类型
+ BP_ABNORMAL_TYPE_ARM_MOVE,//手臂移动类型
+ BP_ABNORMAL_TYPE_MEASURE_TIMEOUT,//测量超时类型
+ BP_ABNORMAL_TYPE_CALIBRATION_DIFFERENCE_TOO_LARGE,//校准差异过大类型
+ BP_ABNORMAL_TYPE_DISCLAIMER,//免责提醒类型
+ MAX_BP_ABNORMAL_TYPE,//最大界面类型
+} BPAbnormalType;
+
+class BPAbnormalView {
+public:
+ BPAbnormalView();
+ ~BPAbnormalView();
+ static BPAbnormalView *GetInstance();
+ UIViewGroup *InitView(BPAbnormalType type);
+ void UpdateForType(BPAbnormalType type);
+
+private:
+ void ApplyAbnormalType(BPAbnormalType type);
+ UIViewGroup *group_{nullptr};
+ UILabelExt *titleLabel_{nullptr};
+ UILabelExt *noteLabel_{nullptr};
+ UIImageView *imageView_{nullptr};
+ UILabelButton *CancelButton_{nullptr};
+ UILabelButton *RetryButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) CompanyNameMagicTag. All rights reserved.
+ * Description: 血压 UI 子界面 AT 调试命令
+ */
+#ifndef BP_AT_CMD_H
+#define BP_AT_CMD_H
+
+#include <stdint.h>
+#include "at_cmd_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+at_cmd_table_t *get_bp_ui_at_cmd_table(uint32_t *cmd_count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef BP_CALIB_FIRST_DONE_VIEW_H
+#define BP_CALIB_FIRST_DONE_VIEW_H
+
+#include "components/ui_box_progress.h"
+#include "components/ui_image_view.h"
+#include "components/ui_label_button.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+#define BLOOD_PATH_FIRST_DONE "/user/res/bloodpressure/vectorgrreen_s.bin"
+
+class BPCalibFirstDoneView {
+public:
+ BPCalibFirstDoneView();
+ ~BPCalibFirstDoneView();
+ static BPCalibFirstDoneView *GetInstance();
+ UIViewGroup *InitView();
+
+private:
+ void InitProgressBar();
+
+ UIViewGroup *group_{nullptr};
+ UILabelExt *titleLabel_{nullptr};
+ UILabelExt *noteLabel_{nullptr};
+ UIImageView *imageView_{nullptr};
+ UIBoxProgress *progressBar_{nullptr};
+ UILabelButton *RetryButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_CALIB_SECOND_DONE_VIEW_H
+#define BP_CALIB_SECOND_DONE_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label_button.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class BPCalibSecondDoneView {
+public:
+ BPCalibSecondDoneView();
+ ~BPCalibSecondDoneView();
+ static BPCalibSecondDoneView *GetInstance();
+ UIViewGroup *InitView();
+
+private:
+ UIViewGroup *group_{nullptr};
+ UILabelExt *titleLabel_{nullptr};
+ UILabelExt *timeLabel_{nullptr};
+ UILabelExt *noteLabel_{nullptr};
+ UILabelButton *confirmButton_{nullptr};
+ UIImageView *imageView_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_CALIB_STOP_VIEW_H
+#define BP_CALIB_STOP_VIEW_H
+
+#include "components/ui_label.h"
+#include "components/ui_label_button_ext.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class BPCalibStopView {
+public:
+ BPCalibStopView();
+ ~BPCalibStopView();
+ static BPCalibStopView *GetInstance();
+ UIViewGroup *InitView();
+ void UpdateForMode(bool isMeasure);
+
+private:
+ void InitRoundIconButton(UILabelButtonExt *&button, int16_t x, int16_t y, const char *viewId,
+ const char *imagePath);
+
+ UIViewGroup *group_{nullptr};
+ UILabelExt *titleLabel_{nullptr};
+ UILabelExt *noteLabel_{nullptr};
+ UILabelButtonExt *cancelButton_{nullptr};
+ UILabelButtonExt *confirmButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_CALIBRATING_VIEW_H
+#define BP_CALIBRATING_VIEW_H
+
+#include "components/ui_circle_progress.h"
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class BPCalibratingView {
+public:
+ BPCalibratingView();
+ ~BPCalibratingView();
+ static BPCalibratingView *GetInstance();
+ UIViewGroup *InitView();
+ /** @param seconds 兼容旧接口,按 90s 映射为百分比 */
+ void UpdateTimeText(int seconds);
+ void UpdateProgress(int percent);
+ void UpdateRound(uint8_t round);
+
+private:
+ void InitCircleProgress();
+
+ UIViewGroup *group_{nullptr};
+ UIImageView *progressBgImage_{nullptr};
+ UICircleProgress *circleProgress_{nullptr};
+ UILabelExt *statusLabel_{nullptr};
+ UILabel *percentValue_{nullptr};
+ UILabel *percentSign_{nullptr};
+ UILabelExt *hintLabel_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_ENTRY_VIEW_H
+#define BP_ENTRY_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_label_button.h"
+#include "components/ui_view.h"
+#include "components/ui_view_group.h"
+#include "UiConfig.h"
+
+namespace OHOS {
+
+#include "bloodpressure/include/BPViewClickIds.h"
+
+#define BLOOD_PATH_SUCC PNG_SUCC_IMAGE
+#define BLOOD_PATH_FAIL PNG_ATTENTION_IMAGE
+#define BLOOD_PATH_CALIBRATION_EXPIRED BLOOD_PATH_FAIL
+#define BLOOD_PATH_NOTE BLOOD_PATH_FAIL
+#define BLOOD_PATH_MEASURE_TIMEOUT "user/res/bloodpressure/nonet.bin"
+#define BLOOD_PATH_WARNING_S "/user/res/bloodpressure/warning_s.bin"
+
+class BPEntryView {
+public:
+ BPEntryView();
+ ~BPEntryView();
+ static BPEntryView *GetInstance();
+ UIViewGroup *InitView();
+ void InitCalibratedView();
+ void InitCalibrationExpiredView();
+ void InitNotCalibratedView();
+ void InitPageIndicator();
+
+private:
+ void InitCalibExpiringSoonBar();
+ void InitNoCalibratedBar();
+
+ UIViewGroup *group_{nullptr};
+ UILabel *titleLabel_{nullptr};
+ UILabel *instructionLabel_{nullptr};
+ UIImageView *imageView_{nullptr};
+ UILabelButton *instructionButton_{nullptr};
+ UILabelButton *measureButton_{nullptr};
+ UIView *calibWarnBg_{nullptr};
+ UIImageView *calibWarnIcon_{nullptr};
+ UILabelExt *calibWarnLabel_{nullptr};
+ UIView *dotEntry_{nullptr};
+ UIView *dotHistory_{nullptr};
+ int8_t currentState_{0};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_HISTORY_VIEW_H
+#define BP_HISTORY_VIEW_H
+
+#include "components/ui_image_animator.h"
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_swipe_view.h"
+#include "components/ui_view_group.h"
+#include <vector>
+
+namespace OHOS {
+
+#include "bloodpressure/include/BPViewClickIds.h"
+
+class BPHistoryView {
+public:
+ BPHistoryView();
+ ~BPHistoryView();
+ static BPHistoryView *GetInstance();
+ UIViewGroup *InitView();
+ void RefreshView();
+ void OnRecordPageChanged(uint16_t pageIndex);
+ void HideArrowWhileDragging();
+ void StartArrowAnimation();
+ void StopArrowAnimation();
+
+private:
+ UIViewGroup *CreateRecordPage(const struct BPRecord &record, size_t index);
+ void ClearRecordPages();
+ void RebuildRecordPages();
+ void InitEmptyPage();
+ void InitPageIndicator();
+ void InitPullDownAnimator();
+ void UnloadPullDownAnimator();
+ void UpdatePageVisibility();
+ void UpdateArrowVisibility();
+ static bool IsHighBloodPressure(uint8_t sbp, uint8_t dbp);
+ static std::string FormatRecordTime(uint32_t timestamp);
+
+ UIViewGroup *group_{nullptr};
+ UISwipeView *recordSwipeView_{nullptr};
+ UIViewGroup *emptyPage_{nullptr};
+ std::vector<UIViewGroup *> recordPages_;
+ UIImageAnimatorView *pullDownAnimator_{nullptr};
+ ImageAnimatorInfo pullDownFrameInfo_[70]{};
+ const ImageInfo **pullUpImageInfo_{nullptr};
+ bool pullDownInited_{false};
+
+ UILabel *emptyTitleLabel_{nullptr};
+ UIImageView *emptyImage_{nullptr};
+ UILabel *emptyHintLabel_{nullptr};
+ UILabelButton *startMeasureButton_{nullptr};
+ UIView *dotEntry_{nullptr};
+ UIView *dotHistory_{nullptr};
+
+ size_t currentRecordIndex_{0};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_INSTRUCTION_VIEW_H
+#define BP_INSTRUCTION_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+#include "bloodpressure/include/BPViewClickIds.h"
+
+#define BLOOD_PATH_SEAT "/user/res/bloodpressure/seat.bin"
+#define BLOOD_PATH_WEAR "/user/res/bloodpressure/wear.bin"
+#define BLOOD_PATH_FIRST "/user/res/bloodpressure/first.bin"
+#define BLOOD_PATH_SECOND "/user/res/bloodpressure/second.bin"
+#define BLOOD_PATH_BELT_LOOSE "/user/res/bloodpressure/belt_loose.bin"
+typedef enum : uint8_t {
+ BP_INSTRUCTION_TYPE_SEAT = 0,//坐姿引导界面类型
+ BP_INSTRUCTION_TYPE_WEAR,//佩戴引导界面类型
+ BP_INSTRUCTION_TYPE_BELT_LOOSE,//佩戴过松引导界面类型
+ BP_INSTRUCTION_TYPE_FIRST,//校准第一轮说明界面类型
+ BP_INSTRUCTION_TYPE_SECOND,//校准第二轮说明界面类型
+ MAX_BP_INSTRUCTION_TYPE,//最大界面类型
+} BPInstructionType;
+
+class BPInstructionView {
+public:
+ BPInstructionView();
+ ~BPInstructionView();
+ static BPInstructionView *GetInstance();
+ UIViewGroup *InitView(BPInstructionType type);
+ void UpdateForType(BPInstructionType type);
+
+private:
+ void ApplyInstructionType(BPInstructionType type);
+ UIViewGroup *group_{nullptr};
+ UIImageView *imageView_{nullptr};
+ UILabel *instructionLabel_{nullptr};
+ UILabelButton *backButton_{nullptr};
+ UILabelButton *measureButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_MAIN_PRESENTER_H
+#define BP_MAIN_PRESENTER_H
+
+#include "Presenter.h"
+#include "bloodpressure/include/BPMainView.h"
+#include "bloodpressure/include/BPInstructionView.h"
+#include "components/ui_swipe_view.h"
+#include "common/key_code.h"
+#include "key/include/KeyInputListener.h"
+
+namespace OHOS {
+
+class GraphicTimer;
+
+class BPMainPresenter : public Presenter<BPMainView>,
+ public UIView::OnClickListener,
+ public UIView::OnDragListener,
+ public UISwipeView::OnSwipeListener,
+ public RootView::OnKeyActListener {
+public:
+ enum class BPProgressMode : uint8_t { None = 0, Measure, Calibrate };
+
+ BPMainPresenter();
+ virtual ~BPMainPresenter();
+ static BPMainPresenter *GetInstance();
+
+ void OnStart() override;
+ void OnPause() override;
+ void OnResume() override;
+ void OnStop() override;
+
+ bool OnDragStart(UIView &view, const DragEvent &event) override;
+ bool OnDrag(UIView &view, const DragEvent &event) override;
+ bool OnDragEnd(UIView &view, const DragEvent &event) override;
+ void OnSwipe(UISwipeView &view) override;
+ bool OnClick(UIView &view, const ClickEvent &event) override;
+ bool OnKeyAct(UIView &view, const KeyEvent &event) override;
+
+ void NotifyCalibrationComplete(uint8_t sbp, uint8_t dbp);
+ void NotifyMeasureComplete(uint8_t sbp, uint8_t dbp);
+
+private:
+ void NavigateBack();
+ bool HandleSwipeBack();
+ bool IsFlowSubPage(int16_t viewId) const;
+ bool NeedsStopConfirm(int16_t viewId) const;
+ void RequestStopConfirm(int16_t fromView);
+ void StartMeasureFlow(BPMainView *mainView);
+ void StartCalibrationFlow(BPMainView *mainView, uint8_t round);
+ void StartProgressSession(BPProgressMode mode);
+ void PauseProgressSession();
+ void ResumeProgressSession();
+ void StopProgressSession();
+ void OnProgressTick();
+ void OnProgressTimeout();
+ void UpdateProgressUi(int percent);
+ void CompleteCalibrationFlow(uint8_t sbp, uint8_t dbp);
+ void CompleteMeasureFlow(uint8_t sbp, uint8_t dbp);
+ static void ProgressTimerThunk(void *arg);
+
+ static constexpr uint32_t kProgressTickMs = 150U;
+ static constexpr int kProgressMaxPercent = 100;
+
+ GraphicTimer *progressTimer_{nullptr};
+ BPProgressMode progressMode_{BPProgressMode::None};
+ int progressPercent_{0};
+ bool progressDataReceived_{false};
+ bool progressPaused_{false};
+ int16_t stopReturnView_{MAX_BPVIEW};
+ bool stopIsMeasure_{false};
+ BPProgressMode lastTimeoutMode_{BPProgressMode::None};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_MAIN_VIEW_H
+#define BP_MAIN_VIEW_H
+
+#include "View.h"
+#include "bloodpressure/include/BPInstructionView.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+
+typedef enum : uint8_t {
+ BP_ENTRY_VIEW = 0,//主界面
+ BP_HISTORY_VIEW,//历史记录界面
+ BP_INSTRUCTION_VIEW,//指导界面
+ BP_ABNORMAL_VIEW,//异常界面
+ BP_MEASURING_VIEW,//测量界面
+ BP_MEASURE_STOP_VIEW,//测量停止界面
+ BP_MEASURE_RESULT_VIEW,//测量结果界面
+ BP_CALIBRATING_VIEW,//校准界面
+ BP_CALIB_STOP_VIEW,//校准停止界面
+ BP_VALUE_INPUT_VIEW,//值输入界面
+ BP_VALUE_CONFIRM_VIEW,//值确认界面
+ BP_CALIB_FIRST_DONE_VIEW,//校准第一轮完成界面
+ BP_CALIB_SECOND_DONE_VIEW,//校准第二轮完成界面
+ MAX_BPVIEW,//最大界面
+} BPVIEWID;
+
+class BPMainPresenter;
+class BPMainView : public View<BPMainPresenter> {
+public:
+ BPMainView();
+ virtual ~BPMainView();
+ static BPMainView *GetInstance();
+ void OnStart() override;
+ void OnStop() override;
+
+ bool ChangeBPEntryView(int16_t thisView, int16_t type);
+ bool ChangeBPHistoryView(int16_t thisView, int16_t type);
+ bool ChangeBPInstructionView(int16_t thisView, int16_t type);
+ bool ChangeBPAbnormalView(int16_t thisView, int16_t type);
+ bool ChangeBPMeasuringView(int16_t thisView, int16_t type);
+ bool ChangeBPMeasureResultView(int16_t thisView, int16_t type);
+ bool ChangeBPCalibratingView(int16_t thisView, int16_t type);
+ bool ChangeBPCalibStopView(int16_t thisView, int16_t type);
+ bool ChangeBPValueInputView(int16_t thisView, int16_t type);
+ bool ChangeBPValueConfirmView(int16_t thisView, int16_t type);
+ bool ChangeBPCalibFirstDoneView(int16_t thisView, int16_t type);
+ bool ChangeBPCalibSecondDoneView(int16_t thisView, int16_t type);
+
+ bool ClickFunc(int16_t interface, int16_t type);
+ void ChangeView(int16_t hideView, int16_t showView, int16_t type = 0);
+ int16_t GetCurrentViewId() const;
+ int16_t GetInstructionSubType() const { return instructionSubType_; }
+ void RefreshCurrentView();
+ /** AT^BPUIVIEW 调试用:切换到指定子界面,subType 用于说明页/异常页子类型 */
+ void ShowAtTestPage(int16_t viewId, int16_t subType = 0);
+
+private:
+ void DeInit();
+
+ class BPEntryView *entryView_{nullptr};
+ class BPHistoryView *historyView_{nullptr};
+ class BPInstructionView *instructionView_{nullptr};
+ class BPAbnormalView *abnormalView_{nullptr};
+ class BPMeasuringView *measuringView_{nullptr};
+ class BPMeasureResultView *measureResultView_{nullptr};
+ class BPCalibratingView *calibratingView_{nullptr};
+ class BPCalibStopView *calibStopView_{nullptr};
+ class BPValueInputView *valueInputView_{nullptr};
+ class BPValueConfirmView *valueConfirmView_{nullptr};
+ class BPCalibFirstDoneView *calibFirstDoneView_{nullptr};
+ class BPCalibSecondDoneView *calibSecondDoneView_{nullptr};
+ UIViewGroup *container_{nullptr};
+ UIViewGroup *group_[MAX_BPVIEW]{nullptr};
+ int16_t currentViewId_{BP_ENTRY_VIEW};
+ int16_t instructionSubType_{0};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_MEASURE_RESULT_VIEW_H
+#define BP_MEASURE_RESULT_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class BPMeasureResultView {
+public:
+ BPMeasureResultView();
+ ~BPMeasureResultView();
+ static BPMeasureResultView *GetInstance();
+ UIViewGroup *InitView();
+ void UpdateText();
+
+private:
+ void InitDividerLines();
+ void ApplyNormalLayout();
+ void ApplyHighLayout();
+ void ApplyCalibExpiredLayout();
+ static bool IsHighBloodPressure(uint8_t sbp, uint8_t dbp);
+ static std::string FormatMeasureTime();
+
+ UIViewGroup *group_{nullptr};
+ UILabel *titleLabel_{nullptr};
+ UILabel *timeLabel_{nullptr};
+ UILabelExt *statusLabel_{nullptr};
+ UIImageView *hlineTop_{nullptr};
+ UIImageView *hlineBottom_{nullptr};
+ UIImageView *vlineMid_{nullptr};
+ UILabelExt *sbpTitle_{nullptr};
+ UILabelExt *dbpTitle_{nullptr};
+ UILabel *sbpValue_{nullptr};
+ UILabel *dbpValue_{nullptr};
+ UILabel *sbpUnit_{nullptr};
+ UILabel *dbpUnit_{nullptr};
+ UILabelButton *confirmButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_MEASURING_VIEW_H
+#define BP_MEASURING_VIEW_H
+
+#include "components/ui_image_animator.h"
+#include "components/ui_label.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class BPMeasuringView {
+public:
+ BPMeasuringView();
+ ~BPMeasuringView();
+ static BPMeasuringView *GetInstance();
+ UIViewGroup *InitView();
+ void StartMeasuringAnimation();
+ void StopMeasuringAnimation();
+ void UpdateProgressPercent(int percent);
+
+private:
+ bool InitMeasuringAnimator();
+ void UnloadAnimatorFrames();
+
+ UIViewGroup *group_{nullptr};
+ UILabel *noteLabel_{nullptr};
+ UIImageAnimatorView *animatorView_{nullptr};
+ ImageAnimatorInfo frameInfo_[40]{};
+ bool animatorInited_{false};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_MODEL_H
+#define BP_MODEL_H
+
+#include <cstdint>
+#include <deque>
+#include <string>
+
+namespace OHOS {
+
+struct BPRecord {
+ uint32_t timestamp;
+ uint8_t sbp;
+ uint8_t dbp;
+};
+
+/** 校准状态:GetCurrentstate() 返回值 */
+static constexpr int8_t BP_CALIB_STATE_VALID = 0; // 已校准且未过期
+static constexpr int8_t BP_CALIB_STATE_EXPIRED = 1; // 校准已过期
+static constexpr int8_t BP_CALIB_STATE_NOT_CALIBRATED = -1; // 从未校准
+
+/** 校准差异阈值,超过该值认为校准差异过大 */
+static constexpr uint8_t BP_CALIB_DIFF_THRESHOLD = 15;
+
+class BPModel {
+public:
+ BPModel();
+ ~BPModel();
+ static BPModel *GetInstance();
+
+ void LoadData();
+ BPRecord GetLastRecord() const;
+ const std::deque<BPRecord> &GetHistoryRecords() const;
+ std::string FormatTimestamp(uint32_t timestamp) const;
+ /** MM-DD HH:mm,历史记录卡片用 */
+ std::string FormatShortTimestamp(uint32_t timestamp) const;
+
+ /** 0:已校准 1:校准已过期 -1:未校准 */
+ int8_t GetCurrentstate();
+ /** 校准完成后写入当前时间戳与校准值 */
+ bool SaveCalibrationResult();
+ int32_t GetCalibrationTimestamp() const;
+
+ /** 校准有效期(秒),默认 30 天,与 fd230 needMeasure 一致 */
+ void SetCalibrationTimeoutSec(uint32_t seconds);
+ uint32_t GetCalibrationTimeoutSec() const;
+ /** 校准剩余天数,-1 表示未校准或已过期 */
+ int32_t GetCalibrationRemainingDays() const;
+ /** 校准即将过期(剩余天数 <= warnDays) */
+ bool IsCalibrationExpiringSoon(uint32_t warnDays = 3) const;
+
+ /** 首次使用免责声明 */
+ bool IsFirstUse() const;
+ bool MarkDisclaimerAccepted();
+
+ enum class BPValueType : uint8_t { SBP = 0, DBP };
+ void ResetInputValues();
+ void AdjustInputValue(BPValueType type, bool increase);
+ void SetInputSbp(uint8_t sbp);
+ void SetInputDbp(uint8_t dbp);
+ uint8_t GetInputSbp() const;
+ uint8_t GetInputDbp() const;
+ void SetMeasureResult(uint8_t sbp, uint8_t dbp);
+ uint8_t GetResultSbp() const;
+ uint8_t GetResultDbp() const;
+ void SetCalibrationRound(uint8_t round);
+ uint8_t GetCalibrationRound() const;
+
+ void SaveFirstRoundInput();
+ void SetSecondRoundInput(uint8_t sbp, uint8_t dbp);
+ bool IsCalibrationDifferenceTooLarge() const;
+ bool SaveMeasureRecord(uint8_t sbp, uint8_t dbp);
+
+private:
+ bool CreateBPDataDirectory();
+ bool LoadCalibrationFromStorage();
+ bool SaveCalibrationToStorage();
+ bool LoadHistoryFromStorage();
+ bool SaveHistoryToStorage();
+
+ BPRecord lastRecord_{0, 0, 0};
+ std::deque<BPRecord> historyRecords_;
+ int32_t calibrationTimestamp_{-1};
+ uint32_t calibrationTimeoutSec_{30U * 24U * 3600U};
+ uint8_t inputSbp_{120};
+ uint8_t inputDbp_{80};
+ uint8_t resultSbp_{0};
+ uint8_t resultDbp_{0};
+ uint8_t calibrationRound_{1};
+ uint8_t firstRoundSbp_{120};
+ uint8_t firstRoundDbp_{80};
+ uint8_t secondRoundSbp_{120};
+ uint8_t secondRoundDbp_{80};
+ bool firstRoundSaved_{false};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_VALUE_CONFIRM_VIEW_H
+#define BP_VALUE_CONFIRM_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class BPValueConfirmView {
+public:
+ BPValueConfirmView();
+ ~BPValueConfirmView();
+ static BPValueConfirmView *GetInstance();
+ UIViewGroup *InitView();
+ void UpdateView();
+
+private:
+ void InitDividerLines();
+ void InitActionButtons();
+ void ApplyValidLayout();
+ void ApplyInvalidLayout(uint8_t sbp, uint8_t dbp);
+
+ UIViewGroup *group_{nullptr};
+ UILabelExt *titleLabel_{nullptr};
+ UILabelExt *errorLabel_{nullptr};
+ UIImageView *hlineTop_{nullptr};
+ UIImageView *vlineMid_{nullptr};
+ UILabelExt *sbpTitle_{nullptr};
+ UILabelExt *dbpTitle_{nullptr};
+ UILabel *sbpValue_{nullptr};
+ UILabel *dbpValue_{nullptr};
+ UILabel *sbpUnit_{nullptr};
+ UILabel *dbpUnit_{nullptr};
+ UILabelButton *modifyButton_{nullptr};
+ UILabelButton *startCalibButton_{nullptr};
+ UILabelButton *errorOkButton_{nullptr};
+ bool isValid_{true};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_VALUE_INPUT_VIEW_H
+#define BP_VALUE_INPUT_VIEW_H
+
+#include "components/ui_label_ext.h"
+#include "components/ui_label_button_ext.h"
+#include "components/ui_picker.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class BPValueInputView {
+public:
+ static constexpr int16_t SBP_MIN = 60;
+ static constexpr int16_t SBP_MAX = 250;
+ static constexpr int16_t DBP_MIN = 40;
+ static constexpr int16_t DBP_MAX = 150;
+
+ BPValueInputView();
+ ~BPValueInputView();
+ static BPValueInputView *GetInstance();
+ UIViewGroup *InitView();
+ void SyncPickerFromModel();
+ UIPicker *GetSbpPicker() const { return sbpPicker_; }
+ UIPicker *GetDbpPicker() const { return dbpPicker_; }
+
+private:
+ void InitBpPicker(UIPicker *&picker, int16_t x, int16_t y, int16_t width, int16_t height,
+ const char *viewId, int16_t start, int16_t end);
+
+ UIViewGroup *group_{nullptr};
+ UILabelExt *titleLabel_{nullptr};
+ UILabelExt *sbpLabel_{nullptr};
+ UILabelExt *dbpLabel_{nullptr};
+ UIPicker *sbpPicker_{nullptr};
+ UIPicker *dbpPicker_{nullptr};
+ UILabelButtonExt *okButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef BP_VIEW_CLICK_IDS_H
+#define BP_VIEW_CLICK_IDS_H
+
+#define BP_CLICK_HISTORY "bp_click_history"
+#define BP_CLICK_INSTRUCTION "bp_click_instruction"
+#define BP_CLICK_MEASURE "bp_click_measure"
+#define BP_CLICK_RECALIBRATE "bp_click_recalibrate"
+#define BP_CLICK_HISTORY_BACK "bp_click_history_back"
+#define BP_CLICK_INSTRUCTION_BACK "bp_click_instruction_back"
+
+#define BP_CLICK_STOP "bp_click_stop"
+#define BP_CLICK_RETRY "bp_click_retry"
+#define BP_CLICK_EXIT "bp_click_exit"
+#define BP_CLICK_CONFIRM "bp_click_confirm"
+#define BP_CLICK_VALUE_NEXT "bp_value_next"
+#define BP_CLICK_VALUE_CONFIRM "bp_value_confirm"
+#define BP_CLICK_VALUE_ERROR_OK "bp_value_error_ok"
+#define BP_CLICK_SBP_LEFT "SBPLeft"
+#define BP_CLICK_SBP_RIGHT "SBPRight"
+#define BP_CLICK_DBP_LEFT "DBPLeft"
+#define BP_CLICK_DBP_RIGHT "DBPRight"
+#define BP_CLICK_MEASURE_STOP "bp_measure_stop"
+#define BP_CLICK_CALIB_STOP "bp_calib_stop"
+#define BP_CLICK_CALIB_STOP_CANCEL "bp_calib_stop_cancel"
+#define BP_CLICK_CALIB_STOP_CONFIRM "bp_calib_stop_confirm"
+#define BP_CLICK_FIRST_CALIB_DONE "bp_first_calib_done"
+#define BP_CLICK_SECOND_CALIB_DONE "bp_second_calib_done"
+#define BP_CLICK_DISCLAIMER_ACCEPT "bp_disclaimer_accept"
+
+#endif
--- /dev/null
+#include "bloodpressure/include/BPAbnormalView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "bloodpressure/include/BPEntryView.h"
+#include "ui_resource_string.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+static BPAbnormalView *g_instance = nullptr;
+
+BPAbnormalView::BPAbnormalView() { g_instance = this; }
+
+BPAbnormalView::~BPAbnormalView() {
+ if (group_) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete noteLabel_;
+ delete imageView_;
+ delete CancelButton_;
+ delete RetryButton_;
+ delete group_;
+ titleLabel_ = nullptr;
+ noteLabel_ = nullptr;
+ imageView_ = nullptr;
+ CancelButton_ = nullptr;
+ RetryButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPAbnormalView *BPAbnormalView::GetInstance() { return g_instance; }
+
+void BPAbnormalView::ApplyAbnormalType(BPAbnormalType type) {
+ if (titleLabel_ == nullptr || noteLabel_ == nullptr || CancelButton_ == nullptr ||
+ RetryButton_ == nullptr) {
+ return;
+ }
+ titleLabel_->SetText("请根据提示进行操作");
+ noteLabel_->SetText("请根据提示进行操作");
+ if (imageView_ != nullptr) {
+ imageView_->SetSrc(BLOOD_PATH_NOTE);
+ imageView_->SetVisible(true);
+ }
+ CancelButton_->SetVisible(true);
+ RetryButton_->SetVisible(true);
+ CancelButton_->Resize(170, 90);
+ CancelButton_->SetPosition(58, 320);
+ RetryButton_->SetPosition(238, 320);
+ CancelButton_->SetText("取消");
+ RetryButton_->SetText("重新校准");
+ CancelButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff191919);
+ RetryButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffff5a36);
+
+ switch (type) {
+ case BP_ABNORMAL_TYPE_NOT_CALIBRATED:
+ titleLabel_->SetText("需先完成校准");
+ noteLabel_->SetText("请准备传统血压计\n约需5分钟");
+ CancelButton_->SetText("稍后");
+ RetryButton_->SetText("开始校准");
+ break;
+ case BP_ABNORMAL_TYPE_ARM_MOVE:
+ titleLabel_->SetText("提示");
+ noteLabel_->SetText("检测到手臂移动\n请保持静止后重试");
+ RetryButton_->SetViewId(BP_CLICK_RETRY);
+ RetryButton_->SetText("重试");
+ break;
+ case BP_ABNORMAL_TYPE_MEASURE_TIMEOUT:
+ titleLabel_->SetText("提示");
+ if (imageView_ != nullptr) {
+ imageView_->SetSrc(BLOOD_PATH_MEASURE_TIMEOUT);
+ }
+ noteLabel_->SetText("未能获取有效信号\n请调整佩戴后重试");
+ RetryButton_->SetViewId(BP_CLICK_RETRY);
+ RetryButton_->SetText("重试");
+ break;
+ case BP_ABNORMAL_TYPE_CALIBRATION_DIFFERENCE_TOO_LARGE:
+ titleLabel_->SetText("两次偏差过大");
+ noteLabel_->SetText("检测到两次偏差较大\n建议重新校准");
+ break;
+ case BP_ABNORMAL_TYPE_DISCLAIMER:
+ titleLabel_->SetText("测量结果仅供参考");
+ noteLabel_->SetText("不可替代专业医疗诊断\n如有健康疑虑,请咨询医生");
+ RetryButton_->SetVisible(false);
+ CancelButton_->SetViewId(BP_CLICK_DISCLAIMER_ACCEPT);
+ CancelButton_->SetText("同意并继续");
+ CancelButton_->Resize(280, 90);
+ CancelButton_->SetPosition(93, 320);
+ CancelButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffff5a36);
+ break;
+ default:
+ break;
+ }
+}
+
+void BPAbnormalView::UpdateForType(BPAbnormalType type) { ApplyAbnormalType(type); }
+
+UIViewGroup *BPAbnormalView::InitView(BPAbnormalType type) {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ InitLabel(titleLabel_, 83, 50, 300, 35, "bp_abnormal_title", "请根据提示进行操作",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitImageView(imageView_, 193, 120, 80, 80, "bp_abnormal_image", BLOOD_PATH_NOTE);
+ InitLabel(noteLabel_, 53, 200, 360, 100, "bp_abnormal_note", "请根据提示进行操作",
+ DEFAULT_VECTOR_FONT_FILENAME, 24);
+ InitLabelButton(CancelButton_, 58, 320, 170, 90, BP_CLICK_EXIT, "取消", 26);
+ InitLabelButton(RetryButton_, 238, 320, 170, 90, BP_CLICK_RECALIBRATE, "重新校准", 26);
+ RetryButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffff5a36);
+
+ ApplyAbnormalType(type);
+
+ auto *listener = static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance());
+ CancelButton_->SetOnClickListener(listener);
+ RetryButton_->SetOnClickListener(listener);
+ group_->Add(titleLabel_);
+ group_->Add(imageView_);
+ group_->Add(noteLabel_);
+ group_->Add(CancelButton_);
+ group_->Add(RetryButton_);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+/*
+ * Copyright (c) CompanyNameMagicTag. All rights reserved.
+ * Description: 血压模块 AT 调试命令
+ *
+ * AT^BPUIVIEW=viewId[,subType][,enterSlice] — 切换子界面
+ * AT^BPCALIBVAL=sbp,dbp — 模拟校准完成(15s 内收到即成功)
+ * AT^BPMEASVAL=sbp,dbp — 模拟测量完成(15s 内收到即成功)
+ */
+#include "bloodpressure/include/BPAtCmd.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPMainView.h"
+#include "AppViewIDs.h"
+#include "NativeAbility.h"
+#include "at_cmd_api.h"
+#include "custom_utils.h"
+#include "debug_print.h"
+#include <cstdlib>
+#include <cstring>
+
+namespace OHOS {
+} // namespace OHOS
+
+#define BP_AT_MAX_ARGC 4
+#define BP_AT_MAX_ARGV_LEN 16
+#define BP_AT_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+
+static int BpAtConvertParam(uint8_t *para, uint32_t paraLen,
+ uint8_t argv[BP_AT_MAX_ARGC][BP_AT_MAX_ARGV_LEN], uint32_t *argc) {
+ uint32_t pos = 0;
+ *argc = 0;
+ if (para == nullptr || argc == nullptr) {
+ return -1;
+ }
+ for (uint32_t i = 0; i < paraLen; i++) {
+ if ((para[i] == ' ' || para[i] == '\n' || para[i] == ',') && pos != 0) {
+ argv[*argc][pos] = '\0';
+ (*argc)++;
+ pos = 0;
+ if (*argc >= BP_AT_MAX_ARGC) {
+ return -1;
+ }
+ } else if (para[i] != ' ' && para[i] != '\n' && para[i] != ',') {
+ argv[*argc][pos] = para[i];
+ pos++;
+ if (pos >= BP_AT_MAX_ARGV_LEN) {
+ return -1;
+ }
+ }
+ }
+ if (pos != 0) {
+ (*argc)++;
+ }
+ return 0;
+}
+
+static void BpAtPrintHelp(void) {
+ PRINT("AT^BPUIVIEW=viewId[,subType][,enterSlice]\r\n");
+ PRINT("AT^BPCALIBVAL=sbp,dbp — calibration done\r\n");
+ PRINT("AT^BPMEASVAL=sbp,dbp — measure done\r\n");
+ PRINT(" viewId: 0入口 1历史 2说明 3异常 4测量中 5测量停 6测量结果\r\n");
+ PRINT(" 7校准中 8校准停 9值输入 10值确认 11首轮完成 12次轮完成\r\n");
+ PRINT(" subType: instruction 0-4 / abnormal 0-4, else 0\r\n");
+ PRINT(" enterSlice: 1=Enter VIEW_BLOOD_PRESSURE first (default 1)\r\n");
+}
+
+static uint32_t bp_ui_view_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd) {
+ (void)strCommonCmd;
+ if (paraLen == 0 || (para != nullptr && paraLen >= 1 && para[0] == '?')) {
+ BpAtPrintHelp();
+ return ERRCODE_SUCC;
+ }
+
+ uint8_t argv[BP_AT_MAX_ARGC][BP_AT_MAX_ARGV_LEN] = {{0}};
+ uint32_t argc = 0;
+ if (BpAtConvertParam(para, paraLen, argv, &argc) != 0 || argc < 1) {
+ BpAtPrintHelp();
+ return ERRCODE_FAIL;
+ }
+
+ int viewId = atoi(reinterpret_cast<char *>(argv[0]));
+ int subType = (argc >= 2) ? atoi(reinterpret_cast<char *>(argv[1])) : 0;
+ int enterSlice = (argc >= 3) ? atoi(reinterpret_cast<char *>(argv[2])) : 1;
+
+ if (viewId < 0 || viewId >= static_cast<int>(OHOS::MAX_BPVIEW)) {
+ PRINT("BPUIVIEW invalid viewId=%d (0-%d)\r\n", viewId, OHOS::MAX_BPVIEW - 1);
+ BpAtPrintHelp();
+ return ERRCODE_FAIL;
+ }
+
+ uapi_set_screen_on_api();
+
+ if (enterSlice != 0) {
+ OHOS::NativeAbility &ability = OHOS::NativeAbility::GetInstance();
+ if (ability.GetCurSliceId() != VIEW_BLOOD_PRESSURE) {
+ ability.EnterSlice(VIEW_BLOOD_PRESSURE);
+ }
+ }
+
+ OHOS::BPMainView *mainView = OHOS::BPMainView::GetInstance();
+ if (mainView == nullptr) {
+ PRINT("BPUIVIEW fail: BPMainView not ready, try enterSlice=1\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ mainView->ShowAtTestPage(static_cast<int16_t>(viewId), static_cast<int16_t>(subType));
+ PRINT("BPUIVIEW ok: view=%d subType=%d cur=%d\r\n", viewId, subType,
+ mainView->GetCurrentViewId());
+ return ERRCODE_SUCC;
+}
+
+static uint32_t bp_calib_val_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd) {
+ (void)strCommonCmd;
+ if (paraLen == 0 || (para != nullptr && paraLen >= 1 && para[0] == '?')) {
+ PRINT("AT^BPCALIBVAL=sbp,dbp\r\n");
+ return ERRCODE_SUCC;
+ }
+
+ uint8_t argv[BP_AT_MAX_ARGC][BP_AT_MAX_ARGV_LEN] = {{0}};
+ uint32_t argc = 0;
+ if (BpAtConvertParam(para, paraLen, argv, &argc) != 0 || argc < 2) {
+ PRINT("BPCALIBVAL need sbp,dbp\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ int sbp = atoi(reinterpret_cast<char *>(argv[0]));
+ int dbp = atoi(reinterpret_cast<char *>(argv[1]));
+ if (sbp < 0 || sbp > 255 || dbp < 0 || dbp > 255) {
+ PRINT("BPCALIBVAL invalid sbp/dbp\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ uapi_set_screen_on_api();
+ OHOS::BPMainPresenter *presenter = OHOS::BPMainPresenter::GetInstance();
+ if (presenter == nullptr) {
+ PRINT("BPCALIBVAL fail: presenter not ready\r\n");
+ return ERRCODE_FAIL;
+ }
+ presenter->NotifyCalibrationComplete(static_cast<uint8_t>(sbp), static_cast<uint8_t>(dbp));
+ PRINT("BPCALIBVAL ok: sbp=%d dbp=%d\r\n", sbp, dbp);
+ return ERRCODE_SUCC;
+}
+
+static uint32_t bp_meas_val_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd) {
+ (void)strCommonCmd;
+ if (paraLen == 0 || (para != nullptr && paraLen >= 1 && para[0] == '?')) {
+ PRINT("AT^BPMEASVAL=sbp,dbp\r\n");
+ return ERRCODE_SUCC;
+ }
+
+ uint8_t argv[BP_AT_MAX_ARGC][BP_AT_MAX_ARGV_LEN] = {{0}};
+ uint32_t argc = 0;
+ if (BpAtConvertParam(para, paraLen, argv, &argc) != 0 || argc < 2) {
+ PRINT("BPMEASVAL need sbp,dbp\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ int sbp = atoi(reinterpret_cast<char *>(argv[0]));
+ int dbp = atoi(reinterpret_cast<char *>(argv[1]));
+ if (sbp < 0 || sbp > 255 || dbp < 0 || dbp > 255) {
+ PRINT("BPMEASVAL invalid sbp/dbp\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ uapi_set_screen_on_api();
+ OHOS::BPMainPresenter *presenter = OHOS::BPMainPresenter::GetInstance();
+ if (presenter == nullptr) {
+ PRINT("BPMEASVAL fail: presenter not ready\r\n");
+ return ERRCODE_FAIL;
+ }
+ presenter->NotifyMeasureComplete(static_cast<uint8_t>(sbp), static_cast<uint8_t>(dbp));
+ PRINT("BPMEASVAL ok: sbp=%d dbp=%d\r\n", sbp, dbp);
+ return ERRCODE_SUCC;
+}
+
+static const at_cmd_table_t g_bp_ui_at_cmd[] = {
+ {(char *)"AT^BPUIVIEW", bp_ui_view_at, (char *)"viewId[,subType][,enterSlice]"},
+ {(char *)"AT^BPCALIBVAL", bp_calib_val_at, (char *)"sbp,dbp"},
+ {(char *)"AT^BPMEASVAL", bp_meas_val_at, (char *)"sbp,dbp"},
+};
+
+extern "C" at_cmd_table_t *get_bp_ui_at_cmd_table(uint32_t *cmd_count) {
+ if (cmd_count != nullptr) {
+ *cmd_count = BP_AT_ARRAY_COUNT(g_bp_ui_at_cmd);
+ }
+ return const_cast<at_cmd_table_t *>(g_bp_ui_at_cmd);
+}
--- /dev/null
+#include "bloodpressure/include/BPCalibFirstDoneView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "uicomponents/UIComponents.h"
+
+namespace OHOS {
+
+namespace {
+static constexpr int16_t BP_FIRST_DONE_PROGRESS_X = 119;
+static constexpr int16_t BP_FIRST_DONE_PROGRESS_Y = 144;
+static constexpr int16_t BP_FIRST_DONE_PROGRESS_W = 236;
+static constexpr int16_t BP_FIRST_DONE_PROGRESS_H = 10;
+static constexpr int16_t BP_FIRST_DONE_PROGRESS_VALUE = 50;
+static constexpr uint32_t BP_FIRST_DONE_PROGRESS_BG = 0xff333333;
+static constexpr uint32_t BP_FIRST_DONE_PROGRESS_FG = 0xff4fc34c;
+} // namespace
+
+static BPCalibFirstDoneView *g_instance = nullptr;
+
+BPCalibFirstDoneView::BPCalibFirstDoneView() { g_instance = this; }
+
+BPCalibFirstDoneView::~BPCalibFirstDoneView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete noteLabel_;
+ delete imageView_;
+ delete progressBar_;
+ delete RetryButton_;
+ delete group_;
+ titleLabel_ = nullptr;
+ noteLabel_ = nullptr;
+ imageView_ = nullptr;
+ progressBar_ = nullptr;
+ RetryButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPCalibFirstDoneView *BPCalibFirstDoneView::GetInstance() { return g_instance; }
+
+void BPCalibFirstDoneView::InitProgressBar() {
+ progressBar_ = new UIBoxProgress();
+ if (progressBar_ == nullptr) {
+ return;
+ }
+ progressBar_->SetPosition(BP_FIRST_DONE_PROGRESS_X, BP_FIRST_DONE_PROGRESS_Y,
+ BP_FIRST_DONE_PROGRESS_W, BP_FIRST_DONE_PROGRESS_H);
+ progressBar_->SetValidWidth(BP_FIRST_DONE_PROGRESS_W);
+ progressBar_->SetValidHeight(BP_FIRST_DONE_PROGRESS_H);
+ progressBar_->SetRange(100, 0);
+ progressBar_->SetValue(BP_FIRST_DONE_PROGRESS_VALUE);
+ progressBar_->SetStyle(STYLE_BORDER_RADIUS, 4);
+ progressBar_->EnableBackground(true);
+ progressBar_->SetBackgroundStyle(STYLE_BACKGROUND_COLOR, BP_FIRST_DONE_PROGRESS_BG);
+ progressBar_->SetBackgroundStyle(STYLE_BACKGROUND_OPA, 0xff);
+ progressBar_->SetForegroundStyle(STYLE_BACKGROUND_COLOR, BP_FIRST_DONE_PROGRESS_FG);
+ progressBar_->SetForegroundStyle(STYLE_BACKGROUND_OPA, 0xff);
+}
+
+UIViewGroup *BPCalibFirstDoneView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ InitImageView(imageView_, 114, 50, 32, 32, "bp_first_done_image", BLOOD_PATH_FIRST_DONE);
+ InitLabel(titleLabel_, 114 + 32 + 8, 50, 198, 35, "bp_first_done_title", "第1轮校准完成",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitProgressBar();
+ InitLabel(noteLabel_, 83, 178, 300, 105, "bp_first_done_note",
+ "请准备开始第2轮校准\n请再次使用传统血压计\n测量同一只手",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ noteLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xb2));
+ noteLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ noteLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabelButton(RetryButton_, 93, 330, 280, 90, BP_CLICK_FIRST_CALIB_DONE, "确定", 30);
+ RetryButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff0176ff);
+ RetryButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance()));
+ group_->Add(imageView_);
+ group_->Add(titleLabel_);
+ if (progressBar_ != nullptr) {
+ group_->Add(progressBar_);
+ }
+ group_->Add(noteLabel_);
+ group_->Add(RetryButton_);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPCalibSecondDoneView.h"
+#include "bloodpressure/include/BPEntryView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "uicomponents/UIComponents.h"
+
+namespace OHOS {
+
+static BPCalibSecondDoneView *g_instance = nullptr;
+
+BPCalibSecondDoneView::BPCalibSecondDoneView() { g_instance = this; }
+
+BPCalibSecondDoneView::~BPCalibSecondDoneView() {
+ if (group_) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete timeLabel_;
+ delete noteLabel_;
+ delete confirmButton_;
+ delete imageView_;
+ delete group_;
+ titleLabel_ = nullptr;
+ timeLabel_ = nullptr;
+ noteLabel_ = nullptr;
+ confirmButton_ = nullptr;
+ imageView_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPCalibSecondDoneView *BPCalibSecondDoneView::GetInstance() { return g_instance; }
+
+UIViewGroup *BPCalibSecondDoneView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ InitImageView(imageView_, 193, 141, 80, 80, "bp_second_done_image", BLOOD_PATH_SUCC);
+ InitLabel(titleLabel_, 0, 50, 466, 35, "bp_second_done_title", "校准完成",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetTextColor(Color::GetColorFromRGBA(0x4f, 0xc3, 0x4c, 0xff));
+
+ InitLabel(timeLabel_, 0, 90, 466, 28, "bp_second_done_time", "有效期至:----",
+ DEFAULT_VECTOR_FONT_FILENAME, 24);
+ InitLabel(noteLabel_, 0, 141 + 80 + 24, 466, 105, "bp_second_done_note",
+ "校准结果已上传\n30天后可重新校准", DEFAULT_VECTOR_FONT_FILENAME, 30);
+ noteLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ noteLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabelButton(confirmButton_, 93, 340, 280, 90, BP_CLICK_SECOND_CALIB_DONE, "确定", 30);
+ confirmButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff0176ff);
+ confirmButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance()));
+ group_->Add(imageView_);
+ group_->Add(titleLabel_);
+ group_->Add(timeLabel_);
+ group_->Add(noteLabel_);
+ group_->Add(confirmButton_);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPCalibStopView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "UiConfig.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+namespace {
+static constexpr int16_t BP_CALIB_STOP_BTN_X = 111;
+static constexpr int16_t BP_CALIB_STOP_BTN_Y = 299;
+static constexpr int16_t BP_CALIB_STOP_BTN_SIZE = 100;
+static constexpr int16_t BP_CALIB_STOP_BTN_GAP = 45;
+} // namespace
+
+static BPCalibStopView *g_instance = nullptr;
+
+BPCalibStopView::BPCalibStopView() { g_instance = this; }
+
+BPCalibStopView::~BPCalibStopView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete noteLabel_;
+ delete cancelButton_;
+ delete confirmButton_;
+ delete group_;
+ titleLabel_ = nullptr;
+ noteLabel_ = nullptr;
+ cancelButton_ = nullptr;
+ confirmButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPCalibStopView *BPCalibStopView::GetInstance() { return g_instance; }
+
+void BPCalibStopView::UpdateForMode(bool isMeasure) {
+ if (titleLabel_ != nullptr) {
+ titleLabel_->SetText(isMeasure ? "中止测量" : "中止校准");
+ }
+ if (noteLabel_ != nullptr) {
+ noteLabel_->SetText(isMeasure ? "是否中止当前血压测量\n本次测量将取消"
+ : "是否中止当前血压校准\n本次校准数据将丢弃");
+ }
+}
+
+void BPCalibStopView::InitRoundIconButton(UILabelButtonExt *&button, int16_t x, int16_t y,
+ const char *viewId, const char *imagePath) {
+ button = new UILabelButtonExt();
+ if (button == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPCalibStopView InitRoundIconButton fail");
+ return;
+ }
+ button->SetPosition(x, y, BP_CALIB_STOP_BTN_SIZE, BP_CALIB_STOP_BTN_SIZE);
+ button->SetViewId(viewId);
+ button->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);
+ button->SetStyle(STYLE_BACKGROUND_OPA, 25);
+ button->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xffffffff, UIButton::PRESSED);
+ button->SetStyleForState(STYLE_BACKGROUND_OPA, 25, UIButton::PRESSED);
+ button->SetStyle(STYLE_BORDER_RADIUS, 100);
+ button->SetImagePosition(0, 0);
+ button->SetImageSrc(imagePath, imagePath);
+}
+
+UIViewGroup *BPCalibStopView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ InitLabel(titleLabel_, 83, 50, 300, 35, "bp_calib_stop_title", "中止校准",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetStyle(STYLE_TEXT_COLOR, 0xffff5a36);
+ titleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabel(noteLabel_, 53, 167, 360, 70, "bp_calib_stop_note",
+ "是否中止当前血压校准\n本次校准数据将丢弃", DEFAULT_VECTOR_FONT_FILENAME, 30);
+ noteLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ noteLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ auto *listener = static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance());
+ InitRoundIconButton(cancelButton_, BP_CALIB_STOP_BTN_X, BP_CALIB_STOP_BTN_Y,
+ BP_CLICK_CALIB_STOP_CANCEL, PNG_SYSTEM_CHECK_OFF_IMAGE);
+ InitRoundIconButton(confirmButton_, BP_CALIB_STOP_BTN_X + BP_CALIB_STOP_BTN_SIZE + BP_CALIB_STOP_BTN_GAP,
+ BP_CALIB_STOP_BTN_Y, BP_CLICK_CALIB_STOP_CONFIRM, PNG_SYSTEM_CHECK_ON_IMAGE);
+ if (cancelButton_ != nullptr) {
+ cancelButton_->SetOnClickListener(listener);
+ }
+ if (confirmButton_ != nullptr) {
+ confirmButton_->SetOnClickListener(listener);
+ }
+
+ group_->Add(titleLabel_);
+ group_->Add(noteLabel_);
+ if (cancelButton_ != nullptr) {
+ group_->Add(cancelButton_);
+ }
+ if (confirmButton_ != nullptr) {
+ group_->Add(confirmButton_);
+ }
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPCalibratingView.h"
+#include "bloodpressure/include/BPModel.h"
+#include "UiConfig.h"
+
+#include "ui_resource_string.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+#include <string>
+
+namespace OHOS {
+
+namespace {
+static constexpr const char *BP_PROGRESS_BG_IMAGE = "/user/res/step/step_b.bin";
+static constexpr int16_t BP_CALIB_PROGRESS_MAX_SEC = 90;
+static constexpr uint32_t BP_STATUS_COLOR = 0xff5BB8FF;
+static constexpr uint32_t BP_PROGRESS_FG_COLOR = 0xff2195F6;
+static constexpr uint32_t BP_PROGRESS_FG_COLOR_END = 0xff5320E2;
+} // namespace
+
+static BPCalibratingView *g_instance = nullptr;
+
+BPCalibratingView::BPCalibratingView() { g_instance = this; }
+
+BPCalibratingView::~BPCalibratingView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete progressBgImage_;
+ delete circleProgress_;
+ delete statusLabel_;
+ delete percentValue_;
+ delete percentSign_;
+ delete hintLabel_;
+ delete group_;
+ progressBgImage_ = nullptr;
+ circleProgress_ = nullptr;
+ statusLabel_ = nullptr;
+ percentValue_ = nullptr;
+ percentSign_ = nullptr;
+ hintLabel_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPCalibratingView *BPCalibratingView::GetInstance() { return g_instance; }
+
+void BPCalibratingView::InitCircleProgress() {
+ circleProgress_ = new UICircleProgress();
+ if (circleProgress_ == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPCalibratingView circleProgress new fail");
+ return;
+ }
+ circleProgress_->SetPosition(0, 0);
+ circleProgress_->Resize(HORIZONTAL_RESOLUTION, VERTICAL_RESOLUTION);
+ circleProgress_->SetViewId("bp_calib_circle_progress");
+ circleProgress_->SetStyle(STYLE_BACKGROUND_OPA, 0x00);
+ circleProgress_->SetBackgroundStyle(STYLE_BACKGROUND_COLOR, 0xff000000);
+ circleProgress_->SetBackgroundStyle(STYLE_BACKGROUND_OPA, 0x00);
+ circleProgress_->SetBackgroundStyle(STYLE_BORDER_OPA, 0x00);
+ circleProgress_->SetBackgroundStyle(STYLE_LINE_OPA, 0x00);
+ circleProgress_->SetForegroundStyle(STYLE_BACKGROUND_COLOR, BP_PROGRESS_FG_COLOR);
+ circleProgress_->SetForegroundStyle(STYLE_BORDER_COLOR, 0xff000000);
+ circleProgress_->SetForegroundStyle(STYLE_LINE_COLOR, BP_PROGRESS_FG_COLOR_END);
+ circleProgress_->SetForegroundStyle(STYLE_LINE_WIDTH, 20);
+ circleProgress_->SetCenterPosition(233, 229);
+ circleProgress_->SetRadius(205);
+ circleProgress_->SetStartAngle(320);
+ circleProgress_->SetEndAngle(40);
+ circleProgress_->SetCapType(CAP_ROUND);
+ circleProgress_->SetValue(0);
+}
+
+void BPCalibratingView::UpdateProgress(int percent) {
+ if (percent < 0) {
+ percent = 0;
+ } else if (percent > 100) {
+ percent = 100;
+ }
+ if (circleProgress_ != nullptr) {
+ circleProgress_->SetValue(percent);
+ circleProgress_->SetCapType(CAP_ROUND);
+ }
+ if (percentValue_ != nullptr) {
+ percentValue_->SetText(std::to_string(percent).c_str());
+ }
+}
+
+void BPCalibratingView::UpdateRound(uint8_t round) {
+ if (statusLabel_ == nullptr) {
+ return;
+ }
+ const char *text = (round == 2) ? "第2轮校准中" : "第1轮校准中";
+ statusLabel_->SetText(text);
+}
+
+void BPCalibratingView::UpdateTimeText(int seconds) {
+ int percent = 0;
+ if (BP_CALIB_PROGRESS_MAX_SEC > 0 && seconds > 0) {
+ percent = seconds * 100 / BP_CALIB_PROGRESS_MAX_SEC;
+ if (percent > 100) {
+ percent = 100;
+ }
+ }
+ UpdateProgress(percent);
+}
+
+UIViewGroup *BPCalibratingView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ InitImageView(progressBgImage_, 28, 80, 100, 100, "bp_calib_progress_bg", BP_PROGRESS_BG_IMAGE);
+
+ InitCircleProgress();
+
+ InitLabelDefault(statusLabel_, 0, 50, 466, 40, "bp_calib_status", "第1轮校准中", 30);
+ statusLabel_->SetTextColor(Color::GetColorFromRGBA(
+ (BP_STATUS_COLOR >> 16) & 0xFF, (BP_STATUS_COLOR >> 8) & 0xFF, BP_STATUS_COLOR & 0xFF, 0xFF));
+ statusLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ percentValue_ = new UILabel();
+ percentValue_->SetPosition(175-44, 179);
+ percentValue_->Resize(132, 56);
+ percentValue_->SetViewId("bp_calib_percent");
+ percentValue_->SetText("0");
+ percentValue_->SetLineBreakMode(UILabel::LINE_BREAK_ADAPT);
+ percentValue_->SetTextColor(Color::GetColorFromRGBA(0x09, 0x8b, 0xea, 0xff));
+ percentValue_->SetAlign(TEXT_ALIGNMENT_RIGHT, TEXT_ALIGNMENT_CENTER);
+ percentValue_->SetFont(GILROY_BOLD_FONT, 80);
+
+ percentSign_ = new UILabel();
+ percentSign_->SetPosition(175+88+4, 179+56-24);
+ percentSign_->Resize(25, 24);
+ percentSign_->SetViewId("bp_calib_percent_sign");
+ percentSign_->SetText("%");
+ percentSign_->SetLineBreakMode(UILabel::LINE_BREAK_ADAPT);
+ percentSign_->SetTextColor(Color::GetColorFromRGBA(0x09, 0x8b, 0xea, 0xff));
+ percentSign_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+ percentSign_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 34);
+
+ InitLabel(hintLabel_, 83, 278, 300, 40, "bp_calib_hint", "请保持静止",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ hintLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ group_->Add(progressBgImage_);
+ if (circleProgress_ != nullptr) {
+ group_->Add(circleProgress_);
+ }
+ group_->Add(statusLabel_);
+ group_->Add(percentValue_);
+ group_->Add(percentSign_);
+ group_->Add(hintLabel_);
+
+ UpdateRound(BPModel::GetInstance()->GetCalibrationRound());
+ UpdateProgress(0);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPEntryView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPModel.h"
+#include "UiConfig.h"
+#include "ui_resource_string.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+#include "securec.h"
+
+namespace OHOS {
+
+static BPEntryView *g_pBPEntryView = nullptr;
+
+BPEntryView::BPEntryView() {
+ g_pBPEntryView = this;
+}
+
+BPEntryView::~BPEntryView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete instructionLabel_;
+ delete instructionButton_;
+ delete measureButton_;
+ delete imageView_;
+ delete calibWarnBg_;
+ delete calibWarnIcon_;
+ delete calibWarnLabel_;
+ delete dotEntry_;
+ delete dotHistory_;
+ delete group_;
+ titleLabel_ = nullptr;
+ instructionLabel_ = nullptr;
+ instructionButton_ = nullptr;
+ measureButton_ = nullptr;
+ imageView_ = nullptr;
+ calibWarnBg_ = nullptr;
+ calibWarnIcon_ = nullptr;
+ calibWarnLabel_ = nullptr;
+ dotEntry_ = nullptr;
+ dotHistory_ = nullptr;
+ group_ = nullptr;
+ }
+ g_pBPEntryView = nullptr;
+}
+
+BPEntryView *BPEntryView::GetInstance() { return g_pBPEntryView; }
+
+
+UIViewGroup *BPEntryView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(),
+ Screen::GetInstance().GetHeight());
+
+ //获取当前状态
+ currentState_ = BPModel::GetInstance()->GetCurrentstate();
+
+ //根据当前状态显示对应视图
+ if (currentState_ == BP_CALIB_STATE_VALID) {
+ InitCalibratedView();
+ } else if (currentState_ == BP_CALIB_STATE_EXPIRED) {
+ InitCalibrationExpiredView();
+ } else if (currentState_ == BP_CALIB_STATE_NOT_CALIBRATED) {
+ //未校准
+ InitNotCalibratedView();
+ }
+ InitPageIndicator();
+ return group_;
+}
+
+void BPEntryView::InitPageIndicator() {
+ dotEntry_ = new UIView();
+ dotHistory_ = new UIView();
+ if (dotEntry_ == nullptr || dotHistory_ == nullptr || group_ == nullptr) {
+ return;
+ }
+ dotEntry_->SetPosition(213, 430, 10, 10);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotEntry_->SetStyle(STYLE_BORDER_RADIUS, 5);
+ dotHistory_->SetPosition(243, 430, 10, 10);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff666666);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotHistory_->SetStyle(STYLE_BORDER_RADIUS, 5);
+ group_->Add(dotEntry_);
+ group_->Add(dotHistory_);
+}
+
+void BPEntryView::InitCalibExpiringSoonBar() {
+ BPModel *model = BPModel::GetInstance();
+ if (model == nullptr || !model->IsCalibrationExpiringSoon() || group_ == nullptr) {
+ return;
+ }
+
+ int32_t daysLeft = model->GetCalibrationRemainingDays();
+ if (daysLeft < 0) {
+ return;
+ }
+
+ char textBuf[48] = {0};
+ if (sprintf_s(textBuf, sizeof(textBuf), "校准即将过期·剩%d天", daysLeft) < 0) {
+ return;
+ }
+
+ calibWarnBg_ = new UIView();
+ calibWarnIcon_ = new UIImageView();
+ calibWarnLabel_ = new UILabelExt();
+ if (calibWarnBg_ == nullptr || calibWarnIcon_ == nullptr || calibWarnLabel_ == nullptr) {
+ delete calibWarnBg_;
+ delete calibWarnIcon_;
+ delete calibWarnLabel_;
+ calibWarnBg_ = nullptr;
+ calibWarnIcon_ = nullptr;
+ calibWarnLabel_ = nullptr;
+ return;
+ }
+
+ calibWarnBg_->SetPosition(67, 101, 331,51);
+ calibWarnBg_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff2e241b);
+ calibWarnBg_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ calibWarnBg_->SetStyle(STYLE_BORDER_RADIUS, 20);
+
+ calibWarnIcon_->SetPosition(67 + 12, 101 + 12, 26, 26);
+ calibWarnIcon_->SetSrc(BLOOD_PATH_WARNING_S);
+
+ calibWarnLabel_->SetPosition(67 + 26 + 12 + 8, 101+8, 273,35);
+ calibWarnLabel_->SetViewId("bp_calib_warn_label");
+ calibWarnLabel_->SetText(textBuf);
+ calibWarnLabel_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+ calibWarnLabel_->SetTextColor(Color::GetColorFromRGBA(0xe7, 0x96, 0x44, 0xff));
+ calibWarnLabel_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+ calibWarnLabel_->SetLineBreakMode(UILabel::LINE_BREAK_ELLIPSIS);
+
+ group_->Add(calibWarnBg_);
+ group_->Add(calibWarnIcon_);
+ group_->Add(calibWarnLabel_);
+}
+
+void BPEntryView::InitNoCalibratedBar() {
+ BPModel *model = BPModel::GetInstance();
+ if (model == nullptr || group_ == nullptr) {
+ return;
+ }
+
+ calibWarnBg_ = new UIView();
+ calibWarnIcon_ = new UIImageView();
+ calibWarnLabel_ = new UILabelExt();
+ if (calibWarnBg_ == nullptr || calibWarnIcon_ == nullptr || calibWarnLabel_ == nullptr) {
+ delete calibWarnBg_;
+ delete calibWarnIcon_;
+ delete calibWarnLabel_;
+ calibWarnBg_ = nullptr;
+ calibWarnIcon_ = nullptr;
+ calibWarnLabel_ = nullptr;
+ return;
+ }
+
+ calibWarnBg_->SetPosition(159, 101, 148,51);
+ calibWarnBg_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff2e241b);
+ calibWarnBg_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ calibWarnBg_->SetStyle(STYLE_BORDER_RADIUS, 20);
+
+ calibWarnIcon_->SetPosition(159 + 12, 101 + 12, 26, 26);
+ calibWarnIcon_->SetSrc(BLOOD_PATH_WARNING_S);
+
+ calibWarnLabel_->SetPosition(159 + 26 + 12 + 8, 101+8, 90,35);
+ calibWarnLabel_->SetViewId("bp_calib_warn_label");
+ calibWarnLabel_->SetText("未校准");
+ calibWarnLabel_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+ calibWarnLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ calibWarnLabel_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+ calibWarnLabel_->SetLineBreakMode(UILabel::LINE_BREAK_ELLIPSIS);
+
+ group_->Add(calibWarnBg_);
+ group_->Add(calibWarnIcon_);
+ group_->Add(calibWarnLabel_);
+}
+
+void BPEntryView::InitCalibratedView() {
+ //初始化已校准视图
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "bp_entry_title", "血压测量",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitCalibExpiringSoonBar();
+ InitUILabel(instructionLabel_, 68, 198, 330, 70, "bp_instruction_label", "测量前30分钟\n请避免剧烈运动、咖啡和烟酒",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitLabelButton(instructionButton_, 58, 320, 170, 90, BP_CLICK_INSTRUCTION, "测量说明", 26);
+ InitLabelButton(measureButton_, 58+170+10, 320, 170, 90, BP_CLICK_MEASURE, "马上测量", 26);
+ group_->Add(titleLabel_);
+ group_->Add(instructionLabel_);
+ auto *listener = static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance());
+ instructionButton_->SetOnClickListener(listener);
+ measureButton_->SetOnClickListener(listener);
+ group_->Add(instructionButton_);
+ group_->Add(measureButton_);
+}
+
+void BPEntryView::InitCalibrationExpiredView() {
+ //初始化校准已过期视图
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "bp_entry_title", "校准已过期",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ InitImageView(imageView_, 193, 120, 80, 80, "bp_calibration_expired_image", BLOOD_PATH_CALIBRATION_EXPIRED);
+ InitUILabel(instructionLabel_, 53, 230, 360, 70, "bp_instruction_label", "继续测量的数据仅供\n趋势参考",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitLabelButton(instructionButton_, 58, 320, 170, 90, BP_CLICK_MEASURE, "马上测量", 26);
+ InitLabelButton(measureButton_, 238, 320, 170, 90, BP_CLICK_RECALIBRATE, "重新校准", 26);
+ auto *listener = static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance());
+ instructionButton_->SetOnClickListener(listener);
+ measureButton_->SetOnClickListener(listener);
+ group_->Add(titleLabel_);
+ group_->Add(imageView_);
+ group_->Add(instructionLabel_);
+ group_->Add(instructionButton_);
+ group_->Add(measureButton_);
+}
+
+void BPEntryView::InitNotCalibratedView() {
+ //初始化未校准视图
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "bp_entry_title", "血压测量",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitNoCalibratedBar();
+ InitUILabel(instructionLabel_, 33, 152, 400, 162, "bp_instruction_label", "请先完成校准",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitLabelButton(measureButton_, 93, 320, 280, 90, BP_CLICK_MEASURE, "开始校准", 26);
+ measureButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance()));
+ group_->Add(titleLabel_);
+ group_->Add(instructionLabel_);
+ group_->Add(measureButton_);
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPHistoryView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPModel.h"
+#include "components/ui_button.h"
+#include "common/image_cache_manager.h"
+#include "common/screen.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+namespace {
+static constexpr uint32_t BP_COLOR_LINE = 0xff333333;
+static constexpr int16_t BP_RESULT_LEFT_X = 56;
+static constexpr int16_t BP_RESULT_RIGHT_X = 56+233;
+static constexpr int16_t BP_HISTORY_PULL_X = 213;
+static constexpr int16_t BP_HISTORY_PULL_Y = 360;
+static constexpr int16_t BP_HISTORY_PULL_W = 40;
+static constexpr int16_t BP_HISTORY_PULL_H = 40;
+static constexpr int16_t BP_HISTORY_PULL_NUM = 70;
+static constexpr int16_t BP_HISTORY_PULL_DELAY = 100;
+static constexpr int16_t BP_HISTORY_MAX_ITEMS = 10;
+static constexpr const char *BP_PULLUP_PATH = "/user/res/weather/PullUp/PullUp_";
+static constexpr const char *BP_NO_RECORD_IMAGE = "/user/res/Training/modules/Norecord.bin";
+} // namespace
+
+static BPHistoryView *g_pBPHistoryView = nullptr;
+
+BPHistoryView::BPHistoryView() { g_pBPHistoryView = this; }
+
+BPHistoryView::~BPHistoryView() {
+ StopArrowAnimation();
+ UnloadPullDownAnimator();
+ ClearRecordPages();
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete recordSwipeView_;
+ delete emptyPage_;
+ delete dotEntry_;
+ delete dotHistory_;
+ delete group_;
+ recordSwipeView_ = nullptr;
+ emptyPage_ = nullptr;
+ dotEntry_ = nullptr;
+ dotHistory_ = nullptr;
+ group_ = nullptr;
+ }
+ emptyTitleLabel_ = nullptr;
+ emptyImage_ = nullptr;
+ emptyHintLabel_ = nullptr;
+ startMeasureButton_ = nullptr;
+ pullDownAnimator_ = nullptr;
+ g_pBPHistoryView = nullptr;
+}
+
+BPHistoryView *BPHistoryView::GetInstance() { return g_pBPHistoryView; }
+
+//判断血压是否偏高
+bool BPHistoryView::IsHighBloodPressure(uint8_t sbp, uint8_t dbp) {
+ return sbp >= 140 || dbp >= 90;
+}
+
+//格式化记录时间
+std::string BPHistoryView::FormatRecordTime(uint32_t timestamp) {
+ return BPModel::GetInstance()->FormatShortTimestamp(timestamp);
+}
+
+//清除记录页面
+void BPHistoryView::ClearRecordPages() {
+ if (recordSwipeView_ != nullptr) {
+ recordSwipeView_->RemoveAll();
+ }
+ for (auto *page : recordPages_) {
+ delete page;
+ }
+ recordPages_.clear();
+}
+
+//初始化页面指示器
+void BPHistoryView::InitPageIndicator() {
+ dotEntry_ = new UIView();
+ dotHistory_ = new UIView();
+ if (dotEntry_ == nullptr || dotHistory_ == nullptr) {
+ return;
+ }
+ dotEntry_->SetPosition(213, 430, 10, 10);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff666666);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotEntry_->SetStyle(STYLE_BORDER_RADIUS, 5);
+ dotHistory_->SetPosition(243, 430, 10, 10);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotHistory_->SetStyle(STYLE_BORDER_RADIUS, 5);
+}
+
+//初始化下拉动画
+void BPHistoryView::InitPullDownAnimator() {
+ if (pullDownInited_) {
+ return;
+ }
+ pullUpImageInfo_ = CreateImageInfoArray(BP_PULLUP_PATH, BP_HISTORY_PULL_NUM);
+ if (pullUpImageInfo_ == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPHistoryView CreateImageInfoArray fail");
+ return;
+ }
+ pullDownAnimator_ = new UIImageAnimatorView();
+ if (pullDownAnimator_ == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPHistoryView pullDownAnimator new fail");
+ return;
+ }
+ for (int16_t i = 0; i < BP_HISTORY_PULL_NUM; i++) {
+ pullDownFrameInfo_[i].imageInfo = pullUpImageInfo_[BP_HISTORY_PULL_NUM - 1 - i];
+ pullDownFrameInfo_[i].imageType = IMG_SRC_IMAGE_INFO;
+ pullDownFrameInfo_[i].pos.x = BP_HISTORY_PULL_X;
+ pullDownFrameInfo_[i].pos.y = BP_HISTORY_PULL_Y;
+ pullDownFrameInfo_[i].width = BP_HISTORY_PULL_W;
+ pullDownFrameInfo_[i].height = BP_HISTORY_PULL_H;
+ }
+ pullDownAnimator_->SetPosition(BP_HISTORY_PULL_X, BP_HISTORY_PULL_Y, BP_HISTORY_PULL_W,
+ BP_HISTORY_PULL_H);
+ pullDownAnimator_->SetImageAnimatorSrc(pullDownFrameInfo_, BP_HISTORY_PULL_NUM,
+ BP_HISTORY_PULL_DELAY);
+ pullDownAnimator_->SetRepeat(true);
+ pullDownAnimator_->SetAutoEnable(false);
+ pullDownAnimator_->SetResizeMode(UIImageView::FILL);
+ pullDownAnimator_->SetTouchable(false);
+ pullDownInited_ = true;
+}
+
+//卸载下拉动画
+void BPHistoryView::UnloadPullDownAnimator() {
+ if (!pullDownInited_) {
+ return;
+ }
+ if (pullDownAnimator_ != nullptr) {
+ delete pullDownAnimator_;
+ pullDownAnimator_ = nullptr;
+ }
+ if (pullUpImageInfo_ != nullptr) {
+ FreeImageInfoArray(pullUpImageInfo_, BP_PULLUP_PATH, BP_HISTORY_PULL_NUM);
+ pullUpImageInfo_ = nullptr;
+ }
+ pullDownInited_ = false;
+}
+
+//开始箭头动画
+void BPHistoryView::StartArrowAnimation() {
+ const auto &records = BPModel::GetInstance()->GetHistoryRecords();
+ if (records.empty() || currentRecordIndex_ + 1 >= records.size()) {
+ StopArrowAnimation();
+ return;
+ }
+ if (!pullDownInited_) {
+ InitPullDownAnimator();
+ }
+ if (pullDownAnimator_ != nullptr) {
+ pullDownAnimator_->SetVisible(true);
+ pullDownAnimator_->Start();
+ }
+}
+
+//停止箭头动画
+void BPHistoryView::StopArrowAnimation() {
+ if (pullDownAnimator_ != nullptr) {
+ pullDownAnimator_->Stop();
+ }
+}
+
+//隐藏箭头动画
+void BPHistoryView::HideArrowWhileDragging() {
+ if (pullDownAnimator_ != nullptr) {
+ pullDownAnimator_->SetVisible(false);
+ }
+}
+//创建记录页面
+UIViewGroup *BPHistoryView::CreateRecordPage(const BPRecord &record, size_t index) {
+ UIViewGroup *page = new UIViewGroup();
+ if (page == nullptr) {
+ return nullptr;
+ }
+ page->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ UILabel *titleLabel = nullptr;
+ UILabel *timeLabel = nullptr;
+ UIImageView *hlineTop = nullptr;
+ UIImageView *hlineBottom = nullptr;
+ UIImageView *vlineMid = nullptr;
+ UILabelExt *sbpTitle = nullptr;
+ UILabelExt *dbpTitle = nullptr;
+ UILabel *sbpValue = nullptr;
+ UILabel *dbpValue = nullptr;
+ UILabel *sbpUnit = nullptr;
+ UILabel *dbpUnit = nullptr;
+
+ InitUILabel(titleLabel, 0, 50, 466, 35, "bp_history_record_title", index == 0 ? "最新记录" : "最近记录",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ if (index == 0) {
+ titleLabel->SetTextColor(Color::GetColorFromRGBA(0x4f, 0xc3, 0x4c, 0xff));
+ } else {
+ titleLabel->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ }
+ titleLabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ std::string timeStr = FormatRecordTime(record.timestamp);
+ InitUILabel(timeLabel, 0, 93, 466, 33, "bp_history_record_time", timeStr.c_str(),
+ DEFAULT_VECTOR_FONT_FILENAME, 28);
+ timeLabel->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ timeLabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ hlineTop = new UIImageView();
+ if (hlineTop != nullptr) {
+ hlineTop->SetPosition(0, 158, 466, 2);
+ hlineTop->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ hlineTop->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ }
+ hlineBottom = new UIImageView();
+ if (hlineBottom != nullptr) {
+ hlineBottom->SetPosition(0, 308, 466, 2);
+ hlineBottom->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ hlineBottom->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ }
+ vlineMid = new UIImageView();
+ if (vlineMid != nullptr) {
+ vlineMid->SetPosition(233, 188, 2, 90);
+ vlineMid->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ vlineMid->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ }
+
+ InitLabel(sbpTitle, 0, 158+30+5, 233, 35, "bp_history_sbp_title", "收缩压",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitLabel(dbpTitle, 233, 158+30+5, 233, 35, "bp_history_dbp_title", "舒张压",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ sbpTitle->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpTitle->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ sbpTitle->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ dbpTitle->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+
+ std::string sbpStr = std::to_string(record.sbp);
+ std::string dbpStr = std::to_string(record.dbp);
+ InitUILabel(sbpValue, BP_RESULT_LEFT_X, 250, 58, 28, "bp_history_sbp", sbpStr.c_str(),
+ DEFAULT_VECTOR_FONT_FILENAME, 40);
+ InitUILabel(dbpValue, BP_RESULT_RIGHT_X, 250, 58, 28, "bp_history_dbp", dbpStr.c_str(),
+ DEFAULT_VECTOR_FONT_FILENAME, 40);
+ sbpValue->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpValue->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ if (IsHighBloodPressure(record.sbp, record.dbp)) {
+ sbpValue->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ dbpValue->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ }
+
+ InitUILabel(sbpUnit, BP_RESULT_LEFT_X+4, 264, 62, 14, "bp_history_sbp_unit", "mmHg",
+ DEFAULT_VECTOR_FONT_FILENAME, 20);
+ InitUILabel(dbpUnit, BP_RESULT_RIGHT_X+4, 264, 62, 14, "bp_history_dbp_unit", "mmHg",
+ DEFAULT_VECTOR_FONT_FILENAME, 20);
+ sbpUnit->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpUnit->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ sbpUnit->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ dbpUnit->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+
+ page->Add(titleLabel);
+ page->Add(timeLabel);
+ if (hlineTop != nullptr) {
+ page->Add(hlineTop);
+ }
+ if (vlineMid != nullptr) {
+ page->Add(vlineMid);
+ }
+ page->Add(sbpTitle);
+ page->Add(dbpTitle);
+ page->Add(sbpValue);
+ page->Add(dbpValue);
+ page->Add(sbpUnit);
+ page->Add(dbpUnit);
+ if (hlineBottom != nullptr) {
+ page->Add(hlineBottom);
+ }
+ return page;
+}
+
+//重建记录页面
+void BPHistoryView::RebuildRecordPages() {
+ if (recordSwipeView_ == nullptr) {
+ return;
+ }
+ ClearRecordPages();
+ const auto &records = BPModel::GetInstance()->GetHistoryRecords();
+ size_t count = records.size();
+ if (count > BP_HISTORY_MAX_ITEMS) {
+ count = BP_HISTORY_MAX_ITEMS;
+ }
+ for (size_t i = 0; i < count; i++) {
+ UIViewGroup *page = CreateRecordPage(records[i], i);
+ if (page == nullptr) {
+ continue;
+ }
+ page->SetViewIndex(static_cast<uint16_t>(i));
+ recordSwipeView_->Add(page);
+ recordPages_.push_back(page);
+ }
+ recordSwipeView_->SetCurrentPage(0, false);
+ currentRecordIndex_ = 0;
+}
+
+//初始化空页面
+void BPHistoryView::InitEmptyPage() {
+ emptyPage_ = new UIViewGroup();
+ if (emptyPage_ == nullptr) {
+ return;
+ }
+ emptyPage_->SetPosition(0, 0, Screen::GetInstance().GetWidth(),
+ Screen::GetInstance().GetHeight());
+ emptyPage_->SetDraggable(true);
+ emptyPage_->SetTouchable(true);
+ emptyPage_->SetOnDragListener(
+ static_cast<UIView::OnDragListener *>(BPMainPresenter::GetInstance()));
+
+ InitUILabel(emptyTitleLabel_, 0, 50, 466, 35, "bp_history_empty_title", "暂无测量记录",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ emptyTitleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitImageView(emptyImage_, 174, 120, 118, 120, "bp_history_empty_image", BP_NO_RECORD_IMAGE);
+
+ InitUILabel(emptyHintLabel_, 53, 260, 360, 70, "bp_history_empty_hint",
+ "完成一次测量后可在这里查看记录", DEFAULT_VECTOR_FONT_FILENAME, 28);
+ emptyHintLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ emptyHintLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+
+ InitLabelButton(startMeasureButton_, 93, 330, 280, 90, BP_CLICK_MEASURE, "开始测量", 30);
+ startMeasureButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff4fc34c);
+ startMeasureButton_->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xff4fc34c, UIButton::PRESSED);
+ startMeasureButton_->SetStyle(STYLE_BORDER_RADIUS, 45);
+ startMeasureButton_->SetOnClickListener(
+ static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance()));
+
+ emptyPage_->Add(emptyTitleLabel_);
+ emptyPage_->Add(emptyImage_);
+ emptyPage_->Add(emptyHintLabel_);
+ emptyPage_->Add(startMeasureButton_);
+}
+
+//更新箭头可见性
+void BPHistoryView::UpdateArrowVisibility() {
+ if (pullDownAnimator_ == nullptr) {
+ return;
+ }
+ const auto &records = BPModel::GetInstance()->GetHistoryRecords();
+ size_t count = records.size();
+ if (count > BP_HISTORY_MAX_ITEMS) {
+ count = BP_HISTORY_MAX_ITEMS;
+ }
+ bool hasNext = count > 0 && currentRecordIndex_ + 1 < count;
+ pullDownAnimator_->SetVisible(hasNext);
+ if (hasNext) {
+ pullDownAnimator_->Start();
+ } else {
+ pullDownAnimator_->Stop();
+ }
+}
+
+//更新页面可见性
+void BPHistoryView::UpdatePageVisibility() {
+ const auto &records = BPModel::GetInstance()->GetHistoryRecords();
+ bool hasRecords = !records.empty();
+ if (recordSwipeView_ != nullptr) {
+ recordSwipeView_->SetVisible(hasRecords);
+ }
+ if (pullDownAnimator_ != nullptr) {
+ pullDownAnimator_->SetVisible(hasRecords);
+ }
+ if (emptyPage_ != nullptr) {
+ emptyPage_->SetVisible(!hasRecords);
+ }
+ if (dotEntry_ != nullptr && dotHistory_ != nullptr) {
+ dotEntry_->SetVisible(!hasRecords);
+ dotHistory_->SetVisible(!hasRecords);
+ }
+}
+
+//记录页面改变
+void BPHistoryView::OnRecordPageChanged(uint16_t pageIndex) {
+ currentRecordIndex_ = pageIndex;
+ UpdateArrowVisibility();
+ if (group_ != nullptr) {
+ group_->Invalidate();
+ }
+}
+
+//刷新页面
+void BPHistoryView::RefreshView() {
+ RebuildRecordPages();
+ UpdatePageVisibility();
+ UpdateArrowVisibility();
+ if (group_ != nullptr) {
+ group_->Invalidate();
+ }
+}
+
+//初始化页面
+UIViewGroup *BPHistoryView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(),
+ Screen::GetInstance().GetHeight());
+
+ recordSwipeView_ = new UISwipeView(UISwipeView::VERTICAL);
+ if (recordSwipeView_ == nullptr) {
+ return group_;
+ }
+ recordSwipeView_->SetPosition(0, 0, Screen::GetInstance().GetWidth(),
+ Screen::GetInstance().GetHeight());
+ recordSwipeView_->SetAnimatorTime(50);
+ recordSwipeView_->SetDraggable(true);
+ recordSwipeView_->SetTouchable(true);
+ auto *presenter = static_cast<BPMainPresenter *>(BPMainPresenter::GetInstance());
+ recordSwipeView_->SetOnDragListener(presenter);
+ recordSwipeView_->SetOnSwipeListener(presenter);
+
+ InitPullDownAnimator();
+ InitEmptyPage();
+ InitPageIndicator();
+
+ group_->Add(recordSwipeView_);
+ if (pullDownAnimator_ != nullptr) {
+ group_->Add(pullDownAnimator_);
+ }
+ if (emptyPage_ != nullptr) {
+ group_->Add(emptyPage_);
+ }
+ if (dotEntry_ != nullptr) {
+ group_->Add(dotEntry_);
+ }
+ if (dotHistory_ != nullptr) {
+ group_->Add(dotHistory_);
+ }
+
+ RefreshView();
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPInstructionView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "UiConfig.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+static BPInstructionView *g_pBPInstructionView = nullptr;
+
+BPInstructionView::BPInstructionView() { g_pBPInstructionView = this; }
+
+BPInstructionView::~BPInstructionView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete imageView_;
+ delete instructionLabel_;
+ delete backButton_;
+ delete measureButton_;
+ delete group_;
+ imageView_ = nullptr;
+ instructionLabel_ = nullptr;
+ backButton_ = nullptr;
+ measureButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_pBPInstructionView = nullptr;
+}
+
+BPInstructionView *BPInstructionView::GetInstance() { return g_pBPInstructionView; }
+
+void BPInstructionView::ApplyInstructionType(BPInstructionType type) {
+ if (imageView_ == nullptr || instructionLabel_ == nullptr || measureButton_ == nullptr) {
+ return;
+ }
+ switch (type) {
+ case BP_INSTRUCTION_TYPE_SEAT:
+ imageView_->SetSrc(BLOOD_PATH_SEAT);
+ instructionLabel_->SetText("双脚着地,端坐在椅子上\n双手手背朝上,手臂平放在桌子上");
+ instructionLabel_->SetPosition(8, 230, 450, 70);
+ measureButton_->SetText("开始测量");
+ break;
+ case BP_INSTRUCTION_TYPE_WEAR:
+ imageView_->SetSrc(BLOOD_PATH_WEAR);
+ instructionLabel_->SetText("手表和手腕之间仅限一指距离\n偏紧佩戴手表");
+ instructionLabel_->SetPosition(38, 230, 370, 70);
+ measureButton_->SetText("开始测量");
+ break;
+ case BP_INSTRUCTION_TYPE_BELT_LOOSE:
+ imageView_->SetSrc(BLOOD_PATH_BELT_LOOSE);
+ instructionLabel_->SetText("表带过松\n请调整松紧后重试");
+ instructionLabel_->SetPosition(113, 230, 240, 70);
+ measureButton_->SetText("重新测量");
+ break;
+ case BP_INSTRUCTION_TYPE_FIRST:
+ imageView_->SetSrc(BLOOD_PATH_FIRST);
+ instructionLabel_->SetText("坐姿端正,手臂平放桌面\n手表与心脏同高");
+ instructionLabel_->SetPosition(68, 230, 330, 70);
+ measureButton_->SetText("开始校准");
+ break;
+ case BP_INSTRUCTION_TYPE_SECOND:
+ imageView_->SetSrc(BLOOD_PATH_SECOND);
+ instructionLabel_->SetText("测量同一只手,保持相同姿势\n准备完成后开始校准");
+ instructionLabel_->SetPosition(38, 230, 390, 70);
+ measureButton_->SetText("开始校准");
+ break;
+ default:
+ break;
+ }
+}
+
+void BPInstructionView::UpdateForType(BPInstructionType type) { ApplyInstructionType(type); }
+
+UIViewGroup *BPInstructionView::InitView(BPInstructionType type) {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(),
+ Screen::GetInstance().GetHeight());
+
+ InitImageView(imageView_, 141, 36, 184, 184, "bp_instruction_image", BLOOD_PATH_SEAT);
+ InitUILabel(instructionLabel_, 53, 230, 360, 70, "bp_instruction_label", "",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ instructionLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ instructionLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ InitLabelButton(backButton_, 58, 320, 170, 90, BP_CLICK_INSTRUCTION_BACK, "取消", 26);
+ InitLabelButton(measureButton_, 238, 320, 170, 90, BP_CLICK_MEASURE, "开始", 26);
+
+ ApplyInstructionType(type);
+
+ auto *clickListener = static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance());
+ auto *dragListener = static_cast<UIView::OnDragListener *>(BPMainPresenter::GetInstance());
+ backButton_->SetOnClickListener(clickListener);
+ measureButton_->SetOnClickListener(clickListener);
+ group_->SetOnDragListener(dragListener);
+
+ group_->Add(imageView_);
+ group_->Add(instructionLabel_);
+ group_->Add(backButton_);
+ group_->Add(measureButton_);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPEntryView.h"
+#include "bloodpressure/include/BPInstructionView.h"
+#include "bloodpressure/include/BPHistoryView.h"
+#include "bloodpressure/include/BPValueInputView.h"
+#include "bloodpressure/include/BPModel.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "bloodpressure/include/BPCalibStopView.h"
+#include "bloodpressure/include/BPMeasuringView.h"
+#include "bloodpressure/include/BPCalibratingView.h"
+#include "bloodpressure/include/BPAbnormalView.h"
+#include "NativeRegisterManager.h"
+#include "NativeAbility.h"
+#include "UiConfig.h"
+#include "power_display_service.h"
+#include "graphic_timer.h"
+#include "ohos_timer.h"
+#include "service/graphic_service.h"
+#include "wearable_log.h"
+#include <custom_utils.h>
+#include <map>
+#include <string>
+
+namespace OHOS {
+
+static BPMainPresenter *g_pBPMainPresenter = nullptr;
+
+REGIST_MENU_PRO(VIEW_BLOOD_PRESSURE, BPMainView, BPMainPresenter,
+ PNG_APPLIST_BP_MESSURE_IMAGE_BIG, PNG_APPLIST_BP_MESSURE_IMAGE,
+ PNG_APPLIST_BP_MESSURE_IMAGE, "血压", STR_BP_CHECK);
+
+BPMainPresenter::BPMainPresenter() { g_pBPMainPresenter = this; }
+
+BPMainPresenter::~BPMainPresenter() {
+ StopProgressSession();
+ g_pBPMainPresenter = nullptr;
+}
+
+BPMainPresenter *BPMainPresenter::GetInstance() { return g_pBPMainPresenter; }
+
+void BPMainPresenter::OnStart() { Presenter<BPMainView>::OnStart(); }
+
+void BPMainPresenter::OnPause(void) { PauseProgressSession(); }
+
+void BPMainPresenter::OnResume(void) {
+ RootView::GetInstance()->SetOnKeyActListener(this);
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView != nullptr && !progressDataReceived_ && progressMode_ != BPProgressMode::None) {
+ int16_t current = mainView->GetCurrentViewId();
+ if (current == BP_MEASURING_VIEW || current == BP_CALIBRATING_VIEW) {
+ ResumeProgressSession();
+ }
+ }
+}
+
+void BPMainPresenter::OnStop(void) { StopProgressSession(); }
+
+void BPMainPresenter::NavigateBack() {
+ StopProgressSession();
+ NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);
+}
+
+bool BPMainPresenter::IsFlowSubPage(int16_t viewId) const {
+ return viewId != BP_ENTRY_VIEW && viewId != BP_HISTORY_VIEW;
+}
+
+bool BPMainPresenter::NeedsStopConfirm(int16_t viewId) const {
+ return viewId == BP_MEASURING_VIEW || viewId == BP_CALIBRATING_VIEW;
+}
+
+void BPMainPresenter::RequestStopConfirm(int16_t fromView) {
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView == nullptr) {
+ return;
+ }
+ stopReturnView_ = fromView;
+ stopIsMeasure_ = (fromView == BP_MEASURING_VIEW);
+ PauseProgressSession();
+ BPCalibStopView *stopView = BPCalibStopView::GetInstance();
+ if (stopView != nullptr) {
+ stopView->UpdateForMode(stopIsMeasure_);
+ }
+ mainView->ChangeView(fromView, BP_CALIB_STOP_VIEW);
+}
+
+bool BPMainPresenter::HandleSwipeBack() {
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView == nullptr) {
+ NavigateBack();
+ return true;
+ }
+ int16_t current = mainView->GetCurrentViewId();
+ if (NeedsStopConfirm(current)) {
+ RequestStopConfirm(current);
+ return true;
+ }
+ if (current == BP_CALIB_STOP_VIEW) {
+ return true;
+ }
+ if (current == BP_ENTRY_VIEW) {
+ NavigateBack();
+ } else if (IsFlowSubPage(current)) {
+ mainView->ChangeView(current, BP_ENTRY_VIEW);
+ } else {
+ mainView->ChangeView(current, BP_ENTRY_VIEW);
+ }
+ return true;
+}
+
+void BPMainPresenter::ProgressTimerThunk(void *arg) {
+ auto *self = static_cast<BPMainPresenter *>(arg);
+ if (self == nullptr) {
+ return;
+ }
+ GraphicService::GetInstance()->PostGraphicEvent([self]() { self->OnProgressTick(); });
+}
+
+void BPMainPresenter::StartProgressSession(BPProgressMode mode) {
+ StopProgressSession();
+ progressMode_ = mode;
+ progressPercent_ = 0;
+ progressDataReceived_ = false;
+ progressPaused_ = false;
+ UpdateProgressUi(0);
+
+ const uint32_t tick = GetOSTick(static_cast<int32_t>(kProgressTickMs));
+ progressTimer_ = new GraphicTimer(static_cast<int32_t>(tick), ProgressTimerThunk, this, true);
+ if (progressTimer_ == nullptr || !progressTimer_->Start()) {
+ delete progressTimer_;
+ progressTimer_ = nullptr;
+ }
+}
+
+void BPMainPresenter::PauseProgressSession() { progressPaused_ = true; }
+
+void BPMainPresenter::ResumeProgressSession() { progressPaused_ = false; }
+
+void BPMainPresenter::StopProgressSession() {
+ if (progressTimer_ != nullptr) {
+ progressTimer_->Stop();
+ delete progressTimer_;
+ progressTimer_ = nullptr;
+ }
+ progressMode_ = BPProgressMode::None;
+ progressPercent_ = 0;
+ progressDataReceived_ = false;
+ progressPaused_ = false;
+}
+
+void BPMainPresenter::UpdateProgressUi(int percent) {
+ if (progressMode_ == BPProgressMode::Measure) {
+ BPMeasuringView *view = BPMeasuringView::GetInstance();
+ if (view != nullptr) {
+ view->UpdateProgressPercent(percent);
+ }
+ } else if (progressMode_ == BPProgressMode::Calibrate) {
+ BPCalibratingView *view = BPCalibratingView::GetInstance();
+ if (view != nullptr) {
+ view->UpdateProgress(percent);
+ }
+ }
+}
+
+void BPMainPresenter::OnProgressTick() {
+ if (progressPaused_ || progressDataReceived_ || progressMode_ == BPProgressMode::None) {
+ return;
+ }
+
+ progressPercent_ += 1;
+ if (progressPercent_ > kProgressMaxPercent) {
+ progressPercent_ = kProgressMaxPercent;
+ }
+ UpdateProgressUi(progressPercent_);
+
+ if (progressPercent_ >= kProgressMaxPercent && !progressDataReceived_) {
+ lastTimeoutMode_ = progressMode_;
+ OnProgressTimeout();
+ }
+}
+
+void BPMainPresenter::OnProgressTimeout() {
+ StopProgressSession();
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView == nullptr) {
+ return;
+ }
+ int16_t current = mainView->GetCurrentViewId();
+ if (current == BP_CALIB_STOP_VIEW && stopReturnView_ < MAX_BPVIEW) {
+ current = stopReturnView_;
+ }
+ if (current == BP_MEASURING_VIEW || current == BP_CALIBRATING_VIEW) {
+ mainView->ChangeView(current, BP_ABNORMAL_VIEW, BP_ABNORMAL_TYPE_MEASURE_TIMEOUT);
+ }
+}
+
+void BPMainPresenter::CompleteCalibrationFlow(uint8_t sbp, uint8_t dbp) {
+ UNUSED(sbp);
+ UNUSED(dbp);
+ BPModel *model = BPModel::GetInstance();
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView == nullptr) {
+ return;
+ }
+
+ if (model->GetCalibrationRound() == 1) {
+ model->SaveFirstRoundInput();
+ mainView->ChangeView(BP_CALIBRATING_VIEW, BP_CALIB_FIRST_DONE_VIEW);
+ return;
+ }
+
+ model->SetSecondRoundInput(model->GetInputSbp(), model->GetInputDbp());
+ if (model->IsCalibrationDifferenceTooLarge()) {
+ mainView->ChangeView(BP_CALIBRATING_VIEW, BP_ABNORMAL_VIEW,
+ BP_ABNORMAL_TYPE_CALIBRATION_DIFFERENCE_TOO_LARGE);
+ return;
+ }
+
+ model->SaveCalibrationResult();
+ mainView->ChangeView(BP_CALIBRATING_VIEW, BP_CALIB_SECOND_DONE_VIEW);
+}
+
+void BPMainPresenter::CompleteMeasureFlow(uint8_t sbp, uint8_t dbp) {
+ BPModel *model = BPModel::GetInstance();
+ model->SetMeasureResult(sbp, dbp);
+ model->SaveMeasureRecord(sbp, dbp);
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView != nullptr) {
+ mainView->ChangeView(BP_MEASURING_VIEW, BP_MEASURE_RESULT_VIEW);
+ }
+}
+
+void BPMainPresenter::NotifyCalibrationComplete(uint8_t sbp, uint8_t dbp) {
+ if (progressDataReceived_) {
+ return;
+ }
+ progressDataReceived_ = true;
+ StopProgressSession();
+ BPModel::GetInstance()->SetMeasureResult(sbp, dbp);
+ CompleteCalibrationFlow(sbp, dbp);
+}
+
+void BPMainPresenter::NotifyMeasureComplete(uint8_t sbp, uint8_t dbp) {
+ if (progressDataReceived_) {
+ return;
+ }
+ progressDataReceived_ = true;
+ StopProgressSession();
+ CompleteMeasureFlow(sbp, dbp);
+}
+
+bool BPMainPresenter::OnDragStart(UIView &view, const DragEvent &event) {
+ UNUSED(view);
+ UNUSED(event);
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView == nullptr || mainView->GetCurrentViewId() != BP_HISTORY_VIEW) {
+ return false;
+ }
+ BPHistoryView *historyView = BPHistoryView::GetInstance();
+ if (historyView != nullptr) {
+ historyView->HideArrowWhileDragging();
+ }
+ return false;
+}
+
+bool BPMainPresenter::OnDrag(UIView &view, const DragEvent &event) {
+ UNUSED(view);
+ UNUSED(event);
+ return false;
+}
+
+bool BPMainPresenter::OnDragEnd(UIView &view, const DragEvent &event) {
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView == nullptr) {
+ return false;
+ }
+ int16_t current = mainView->GetCurrentViewId();
+ uint8_t direction = event.GetDragDirection();
+
+ if (direction == DragEvent::DIRECTION_LEFT_TO_RIGHT) {
+ HandleSwipeBack();
+ return true;
+ }
+ if (current == BP_ENTRY_VIEW && direction == DragEvent::DIRECTION_RIGHT_TO_LEFT) {
+ mainView->ChangeView(BP_ENTRY_VIEW, BP_HISTORY_VIEW);
+ return true;
+ }
+ return false;
+}
+
+void BPMainPresenter::OnSwipe(UISwipeView &view) {
+ UNUSED(view);
+ BPHistoryView *historyView = BPHistoryView::GetInstance();
+ if (historyView != nullptr) {
+ historyView->OnRecordPageChanged(view.GetCurrentPage());
+ }
+}
+
+void BPMainPresenter::StartMeasureFlow(BPMainView *mainView) {
+ int16_t hide = mainView->GetCurrentViewId();
+ mainView->ChangeView(hide, BP_MEASURING_VIEW);
+ StartProgressSession(BPProgressMode::Measure);
+}
+
+void BPMainPresenter::StartCalibrationFlow(BPMainView *mainView, uint8_t round) {
+ BPModel *model = BPModel::GetInstance();
+ model->SetCalibrationRound(round);
+ model->ResetInputValues();
+ int16_t hide = mainView->GetCurrentViewId();
+ mainView->ChangeView(hide, BP_VALUE_INPUT_VIEW);
+}
+
+bool BPMainPresenter::OnClick(UIView &view, const ClickEvent &event) {
+ UNUSED(event);
+ std::string viewId = view.GetViewId();
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView == nullptr) {
+ return true;
+ }
+ int16_t current = mainView->GetCurrentViewId();
+ BPModel *model = BPModel::GetInstance();
+
+ if (viewId == BP_CLICK_DISCLAIMER_ACCEPT) {
+ model->MarkDisclaimerAccepted();
+ mainView->ChangeView(BP_ABNORMAL_VIEW, BP_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_HISTORY) {
+ mainView->ChangeView(BP_ENTRY_VIEW, BP_HISTORY_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_HISTORY_BACK) {
+ mainView->ChangeView(BP_HISTORY_VIEW, BP_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_INSTRUCTION_BACK) {
+ mainView->ChangeView(BP_INSTRUCTION_VIEW, BP_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_INSTRUCTION) {
+ mainView->ChangeView(BP_ENTRY_VIEW, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_SEAT);
+ return true;
+ }
+ if (viewId == BP_CLICK_VALUE_NEXT) {
+ BPValueInputView *inputView = BPValueInputView::GetInstance();
+ if (inputView != nullptr && inputView->GetSbpPicker() != nullptr &&
+ inputView->GetDbpPicker() != nullptr) {
+ model->SetInputSbp(static_cast<uint8_t>(
+ BPValueInputView::SBP_MIN + inputView->GetSbpPicker()->GetSelected()));
+ model->SetInputDbp(static_cast<uint8_t>(
+ BPValueInputView::DBP_MIN + inputView->GetDbpPicker()->GetSelected()));
+ }
+ mainView->ChangeView(BP_VALUE_INPUT_VIEW, BP_VALUE_CONFIRM_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_VALUE_CONFIRM) {
+ if (model->GetInputSbp() > model->GetInputDbp()) {
+ mainView->ChangeView(BP_VALUE_CONFIRM_VIEW, BP_CALIBRATING_VIEW);
+ StartProgressSession(BPProgressMode::Calibrate);
+ }
+ return true;
+ }
+ if (viewId == BP_CLICK_VALUE_ERROR_OK) {
+ mainView->ChangeView(BP_VALUE_CONFIRM_VIEW, BP_VALUE_INPUT_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_STOP && current == BP_VALUE_CONFIRM_VIEW) {
+ mainView->ChangeView(BP_VALUE_CONFIRM_VIEW, BP_VALUE_INPUT_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_MEASURE_STOP) {
+ RequestStopConfirm(BP_MEASURING_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_CALIB_STOP) {
+ RequestStopConfirm(BP_CALIBRATING_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_CALIB_STOP_CANCEL) {
+ int16_t backView = (stopReturnView_ < MAX_BPVIEW) ? stopReturnView_ : BP_CALIBRATING_VIEW;
+ mainView->ChangeView(BP_CALIB_STOP_VIEW, backView);
+ ResumeProgressSession();
+ return true;
+ }
+ if (viewId == BP_CLICK_CALIB_STOP_CONFIRM) {
+ StopProgressSession();
+ mainView->ChangeView(BP_CALIB_STOP_VIEW, BP_ENTRY_VIEW);
+ stopReturnView_ = MAX_BPVIEW;
+ return true;
+ }
+ if (viewId == BP_CLICK_STOP) {
+ if (NeedsStopConfirm(current)) {
+ RequestStopConfirm(current);
+ return true;
+ }
+ StopProgressSession();
+ mainView->ChangeView(current, BP_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_EXIT) {
+ StopProgressSession();
+ mainView->ChangeView(current, BP_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == BP_CLICK_RETRY) {
+ if (current == BP_ABNORMAL_VIEW) {
+ if (lastTimeoutMode_ == BPProgressMode::Calibrate) {
+ mainView->ChangeView(BP_ABNORMAL_VIEW, BP_CALIBRATING_VIEW);
+ StartProgressSession(BPProgressMode::Calibrate);
+ } else {
+ StartMeasureFlow(mainView);
+ }
+ } else if (current == BP_CALIB_STOP_VIEW) {
+ int16_t backView = (stopReturnView_ < MAX_BPVIEW) ? stopReturnView_ : BP_CALIBRATING_VIEW;
+ mainView->ChangeView(BP_CALIB_STOP_VIEW, backView);
+ ResumeProgressSession();
+ }
+ return true;
+ }
+ if (viewId == BP_CLICK_CONFIRM) {
+ if (current == BP_MEASURE_RESULT_VIEW) {
+ mainView->ChangeView(BP_MEASURE_RESULT_VIEW, BP_ENTRY_VIEW);
+ }
+ return true;
+ }
+ if (viewId == BP_CLICK_FIRST_CALIB_DONE) {
+ model->SetCalibrationRound(2);
+ mainView->ChangeView(BP_CALIB_FIRST_DONE_VIEW, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_SECOND);
+ return true;
+ }
+ if (viewId == BP_CLICK_SECOND_CALIB_DONE) {
+ mainView->ChangeView(BP_CALIB_SECOND_DONE_VIEW, BP_ENTRY_VIEW);
+ return true;
+ }
+
+ if (viewId == BP_CLICK_RECALIBRATE) {
+ if (current == BP_ABNORMAL_VIEW) {
+ model->SetCalibrationRound(1);
+ mainView->ChangeView(BP_ABNORMAL_VIEW, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_FIRST);
+ return true;
+ }
+ model->SetCalibrationRound(1);
+ mainView->ChangeView(current, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_FIRST);
+ return true;
+ }
+
+ if (viewId == BP_CLICK_MEASURE) {
+ if (current == BP_INSTRUCTION_VIEW) {
+ auto instructionType =
+ static_cast<BPInstructionType>(mainView->GetInstructionSubType());
+ if (instructionType == BP_INSTRUCTION_TYPE_FIRST ||
+ instructionType == BP_INSTRUCTION_TYPE_SECOND) {
+ StartCalibrationFlow(mainView, model->GetCalibrationRound());
+ return true;
+ }
+ int8_t state = model->GetCurrentstate();
+ if (state == BP_CALIB_STATE_NOT_CALIBRATED) {
+ StartCalibrationFlow(mainView, model->GetCalibrationRound());
+ } else {
+ StartMeasureFlow(mainView);
+ }
+ return true;
+ }
+ if (current == BP_HISTORY_VIEW) {
+ int8_t state = model->GetCurrentstate();
+ if (state == BP_CALIB_STATE_NOT_CALIBRATED) {
+ model->SetCalibrationRound(1);
+ mainView->ChangeView(BP_HISTORY_VIEW, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_FIRST);
+ } else if (state == BP_CALIB_STATE_EXPIRED) {
+ StartMeasureFlow(mainView);
+ } else {
+ mainView->ChangeView(BP_HISTORY_VIEW, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_SEAT);
+ }
+ return true;
+ }
+ if (current == BP_ENTRY_VIEW) {
+ int8_t state = model->GetCurrentstate();
+ if (state == BP_CALIB_STATE_NOT_CALIBRATED) {
+ model->SetCalibrationRound(1);
+ mainView->ChangeView(BP_ENTRY_VIEW, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_FIRST);
+ } else if (state == BP_CALIB_STATE_EXPIRED) {
+ StartMeasureFlow(mainView);
+ } else {
+ mainView->ChangeView(BP_ENTRY_VIEW, BP_INSTRUCTION_VIEW, BP_INSTRUCTION_TYPE_SEAT);
+ }
+ return true;
+ }
+ }
+
+ return true;
+}
+
+bool BPMainPresenter::OnKeyAct(UIView &view, const KeyEvent &event) {
+ UNUSED(view);
+ unsigned short keyID = event.GetKeyId();
+ unsigned short keyState = event.GetState();
+ if (keyID == static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_POWER) && keyState == 1 &&
+ uapi_get_screen_state() == 0) {
+ BPMainView *mainView = BPMainView::GetInstance();
+ if (mainView != nullptr && NeedsStopConfirm(mainView->GetCurrentViewId())) {
+ RequestStopConfirm(mainView->GetCurrentViewId());
+ return true;
+ }
+ HandleSwipeBack();
+ }
+ return true;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPMainView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPModel.h"
+#include "bloodpressure/include/BPEntryView.h"
+#include "bloodpressure/include/BPHistoryView.h"
+#include "bloodpressure/include/BPInstructionView.h"
+#include "bloodpressure/include/BPAbnormalView.h"
+#include "bloodpressure/include/BPMeasuringView.h"
+#include "bloodpressure/include/BPMeasureResultView.h"
+#include "bloodpressure/include/BPCalibratingView.h"
+#include "bloodpressure/include/BPCalibStopView.h"
+#include "bloodpressure/include/BPValueInputView.h"
+#include "bloodpressure/include/BPValueConfirmView.h"
+#include "bloodpressure/include/BPCalibFirstDoneView.h"
+#include "bloodpressure/include/BPCalibSecondDoneView.h"
+#include "wearable_log.h"
+#include "common/screen.h"
+
+namespace OHOS {
+
+static BPMainView *g_pBPMainView = nullptr;
+
+BPMainView::BPMainView() {
+ g_pBPMainView = this;
+}
+
+BPMainView::~BPMainView() {
+ DeInit();
+ g_pBPMainView = nullptr;
+}
+
+BPMainView *BPMainView::GetInstance() { return g_pBPMainView; }
+
+void BPMainView::DeInit() {
+ delete entryView_;
+ delete historyView_;
+ delete instructionView_;
+ delete abnormalView_;
+ delete measuringView_;
+ delete measureResultView_;
+ delete calibratingView_;
+ delete calibStopView_;
+ delete valueInputView_;
+ delete valueConfirmView_;
+ delete calibFirstDoneView_;
+ delete calibSecondDoneView_;
+ entryView_ = nullptr;
+ historyView_ = nullptr;
+ instructionView_ = nullptr;
+ abnormalView_ = nullptr;
+ measuringView_ = nullptr;
+ measureResultView_ = nullptr;
+ calibratingView_ = nullptr;
+ calibStopView_ = nullptr;
+ valueInputView_ = nullptr;
+ valueConfirmView_ = nullptr;
+ calibFirstDoneView_ = nullptr;
+ calibSecondDoneView_ = nullptr;
+}
+
+void BPMainView::OnStart() {
+ View<BPMainPresenter>::OnStart();
+ if (container_ == nullptr) {
+ container_ = new UIViewGroup();
+ if (container_ == nullptr) {
+ return;
+ }
+ container_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ container_->SetDraggable(true);
+ container_->SetTouchable(true);
+ container_->SetOnDragListener(presenter_);
+ AddViewToRootContainer(container_);
+ }
+ BPModel::GetInstance()->LoadData();
+ if (BPModel::GetInstance()->IsFirstUse()) {
+ ChangeView(MAX_BPVIEW, BP_ABNORMAL_VIEW, BP_ABNORMAL_TYPE_DISCLAIMER);
+ } else {
+ ChangeView(MAX_BPVIEW, BP_ENTRY_VIEW);
+ }
+ RootView::GetInstance()->SetOnKeyActListener(
+ static_cast<RootView::OnKeyActListener *>(BPMainPresenter::GetInstance()));
+ Draw();
+}
+
+void BPMainView::OnStop() {
+ View<BPMainPresenter>::OnStop();
+ if (container_ != nullptr) {
+ container_->RemoveAll();
+ delete container_;
+ container_ = nullptr;
+ }
+}
+
+bool BPMainView::ChangeBPEntryView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ entryView_ = new BPEntryView();
+ if (entryView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = entryView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPHistoryView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (historyView_ != nullptr) {
+ historyView_->RefreshView();
+ }
+ return true;
+ }
+ historyView_ = new BPHistoryView();
+ if (historyView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = historyView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPInstructionView(int16_t thisView, int16_t type) {
+ instructionSubType_ = type;
+ if (group_[thisView] != nullptr) {
+ if (instructionView_ != nullptr) {
+ instructionView_->UpdateForType(static_cast<BPInstructionType>(type));
+ }
+ return true;
+ }
+ instructionView_ = new BPInstructionView();
+ if (instructionView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = instructionView_->InitView(static_cast<BPInstructionType>(type));
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPAbnormalView(int16_t thisView, int16_t type) {
+ if (group_[thisView] != nullptr) {
+ if (abnormalView_ != nullptr) {
+ abnormalView_->UpdateForType(static_cast<BPAbnormalType>(type));
+ }
+ return true;
+ }
+ abnormalView_ = new BPAbnormalView();
+ if (abnormalView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = abnormalView_->InitView(static_cast<BPAbnormalType>(type));
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPMeasuringView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ measuringView_ = new BPMeasuringView();
+ if (measuringView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = measuringView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPMeasureResultView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (measureResultView_ != nullptr) {
+ measureResultView_->UpdateText();
+ }
+ return true;
+ }
+ measureResultView_ = new BPMeasureResultView();
+ if (measureResultView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = measureResultView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPCalibratingView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (calibratingView_ != nullptr) {
+ calibratingView_->UpdateRound(BPModel::GetInstance()->GetCalibrationRound());
+ calibratingView_->UpdateProgress(0);
+ }
+ return true;
+ }
+ calibratingView_ = new BPCalibratingView();
+ if (calibratingView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = calibratingView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPCalibStopView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ calibStopView_ = new BPCalibStopView();
+ if (calibStopView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = calibStopView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPValueInputView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (valueInputView_ != nullptr) {
+ valueInputView_->SyncPickerFromModel();
+ }
+ return true;
+ }
+ valueInputView_ = new BPValueInputView();
+ if (valueInputView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = valueInputView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPValueConfirmView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (valueConfirmView_ != nullptr) {
+ valueConfirmView_->UpdateView();
+ }
+ return true;
+ }
+ valueConfirmView_ = new BPValueConfirmView();
+ if (valueConfirmView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = valueConfirmView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPCalibFirstDoneView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ calibFirstDoneView_ = new BPCalibFirstDoneView();
+ if (calibFirstDoneView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = calibFirstDoneView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool BPMainView::ChangeBPCalibSecondDoneView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ calibSecondDoneView_ = new BPCalibSecondDoneView();
+ if (calibSecondDoneView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = calibSecondDoneView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+struct BPViewMapper {
+ const int16_t viewId;
+ bool (BPMainView::*func)(int16_t, int16_t);
+};
+
+static const BPViewMapper ViewMapper[] = {
+ {BP_ENTRY_VIEW, &BPMainView::ChangeBPEntryView},
+ {BP_HISTORY_VIEW, &BPMainView::ChangeBPHistoryView},
+ {BP_INSTRUCTION_VIEW, &BPMainView::ChangeBPInstructionView},
+ {BP_ABNORMAL_VIEW, &BPMainView::ChangeBPAbnormalView},
+ {BP_MEASURING_VIEW, &BPMainView::ChangeBPMeasuringView},
+ {BP_MEASURE_RESULT_VIEW, &BPMainView::ChangeBPMeasureResultView},
+ {BP_CALIBRATING_VIEW, &BPMainView::ChangeBPCalibratingView},
+ {BP_CALIB_STOP_VIEW, &BPMainView::ChangeBPCalibStopView},
+ {BP_VALUE_INPUT_VIEW, &BPMainView::ChangeBPValueInputView},
+ {BP_VALUE_CONFIRM_VIEW, &BPMainView::ChangeBPValueConfirmView},
+ {BP_CALIB_FIRST_DONE_VIEW, &BPMainView::ChangeBPCalibFirstDoneView},
+ {BP_CALIB_SECOND_DONE_VIEW, &BPMainView::ChangeBPCalibSecondDoneView},
+};
+
+bool BPMainView::ClickFunc(int16_t interface, int16_t type) {
+ int funcSize = sizeof(ViewMapper) / sizeof(ViewMapper[0]);
+ for (int i = 0; i < funcSize; i++) {
+ if (interface == ViewMapper[i].viewId) {
+ return (this->*(ViewMapper[i].func))(interface, type);
+ }
+ }
+ return false;
+}
+
+void BPMainView::ChangeView(int16_t hideView, int16_t showView, int16_t type) {
+ if (showView >= MAX_BPVIEW) {
+ return;
+ }
+ if (hideView != MAX_BPVIEW) {
+ if (hideView >= MAX_BPVIEW || group_[hideView] == nullptr) {
+ return;
+ }
+ if (hideView == BP_MEASURING_VIEW && measuringView_ != nullptr) {
+ measuringView_->StopMeasuringAnimation();
+ }
+ if (hideView == BP_HISTORY_VIEW && historyView_ != nullptr) {
+ historyView_->StopArrowAnimation();
+ }
+ group_[hideView]->SetVisible(false);
+ }
+ if (!ClickFunc(showView, type)) {
+ return;
+ }
+ group_[showView]->SetVisible(true);
+ currentViewId_ = showView;
+ if (showView == BP_MEASURING_VIEW && measuringView_ != nullptr) {
+ measuringView_->StartMeasuringAnimation();
+ measuringView_->UpdateProgressPercent(0);
+ }
+ if (showView == BP_HISTORY_VIEW && historyView_ != nullptr) {
+ historyView_->RefreshView();
+ historyView_->StartArrowAnimation();
+ }
+ RefreshCurrentView();
+ container_->Invalidate();
+}
+
+void BPMainView::RefreshCurrentView() {
+ switch (currentViewId_) {
+ case BP_MEASURE_RESULT_VIEW:
+ if (measureResultView_ != nullptr) {
+ measureResultView_->UpdateText();
+ }
+ break;
+ case BP_VALUE_INPUT_VIEW:
+ if (valueInputView_ != nullptr) {
+ valueInputView_->SyncPickerFromModel();
+ }
+ break;
+ case BP_VALUE_CONFIRM_VIEW:
+ if (valueConfirmView_ != nullptr) {
+ valueConfirmView_->UpdateView();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+int16_t BPMainView::GetCurrentViewId() const { return currentViewId_; }
+
+void BPMainView::ShowAtTestPage(int16_t viewId, int16_t subType) {
+ if (viewId < 0 || viewId >= MAX_BPVIEW) {
+ return;
+ }
+ instructionSubType_ = subType;
+ if (currentViewId_ == viewId) {
+ if (viewId == BP_INSTRUCTION_VIEW && instructionView_ != nullptr) {
+ instructionView_->UpdateForType(static_cast<BPInstructionType>(subType));
+ } else if (viewId == BP_ABNORMAL_VIEW && abnormalView_ != nullptr) {
+ abnormalView_->UpdateForType(static_cast<BPAbnormalType>(subType));
+ } else {
+ RefreshCurrentView();
+ }
+ if (container_ != nullptr) {
+ container_->Invalidate();
+ }
+ return;
+ }
+ ChangeView(currentViewId_, viewId, subType);
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPMeasureResultView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPModel.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "securec.h"
+#include "uicomponents/UIComponents.h"
+#include <ctime>
+
+namespace OHOS {
+
+namespace {
+static constexpr uint32_t BP_COLOR_LINE = 0xff333333;
+static constexpr int16_t BP_RESULT_COL_W = 170;
+static constexpr int16_t BP_RESULT_LEFT_X = 58;
+static constexpr int16_t BP_RESULT_RIGHT_X = 238;
+} // namespace
+
+static BPMeasureResultView *g_instance = nullptr;
+
+BPMeasureResultView::BPMeasureResultView() { g_instance = this; }
+
+BPMeasureResultView::~BPMeasureResultView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete timeLabel_;
+ delete statusLabel_;
+ delete hlineTop_;
+ delete hlineBottom_;
+ delete vlineMid_;
+ delete sbpTitle_;
+ delete dbpTitle_;
+ delete sbpValue_;
+ delete dbpValue_;
+ delete sbpUnit_;
+ delete dbpUnit_;
+ delete confirmButton_;
+ delete group_;
+ titleLabel_ = nullptr;
+ timeLabel_ = nullptr;
+ statusLabel_ = nullptr;
+ hlineTop_ = nullptr;
+ hlineBottom_ = nullptr;
+ vlineMid_ = nullptr;
+ sbpTitle_ = nullptr;
+ dbpTitle_ = nullptr;
+ sbpValue_ = nullptr;
+ dbpValue_ = nullptr;
+ sbpUnit_ = nullptr;
+ dbpUnit_ = nullptr;
+ confirmButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPMeasureResultView *BPMeasureResultView::GetInstance() { return g_instance; }
+
+//判断血压是否偏高
+bool BPMeasureResultView::IsHighBloodPressure(uint8_t sbp, uint8_t dbp) {
+ return sbp >= 140 || dbp >= 90;
+}
+//格式化测量时间
+std::string BPMeasureResultView::FormatMeasureTime() {
+ time_t now = time(nullptr);
+ if (now <= 0) {
+ return "--";
+ }
+ tm *timeInfo = localtime(&now);
+ if (timeInfo == nullptr) {
+ return "--";
+ }
+ char buf[32] = {0};
+ if (sprintf_s(buf, sizeof(buf), "%02d-%02d %02d:%02d", timeInfo->tm_mon + 1,
+ timeInfo->tm_mday, timeInfo->tm_hour, timeInfo->tm_min) < 0) {
+ return "--";
+ }
+ return std::string(buf);
+}
+
+//初始化分割线
+void BPMeasureResultView::InitDividerLines() {
+ hlineTop_ = new UIImageView();
+ if (hlineTop_ != nullptr) {
+ hlineTop_->SetPosition(0, 158, 466, 2);
+ hlineTop_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ hlineTop_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ }
+ hlineBottom_ = new UIImageView();
+ if (hlineBottom_ != nullptr) {
+ hlineBottom_->SetPosition(0, 308, 466, 2);
+ hlineBottom_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ hlineBottom_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ }
+ vlineMid_ = new UIImageView();
+ if (vlineMid_ != nullptr) {
+ vlineMid_->SetPosition(233, 188, 2, 90);
+ vlineMid_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ vlineMid_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ }
+}
+
+//应用正常布局
+void BPMeasureResultView::ApplyNormalLayout() {
+ if (titleLabel_ != nullptr) {
+ titleLabel_->SetText("测量成功");
+ titleLabel_->SetTextColor(Color::GetColorFromRGBA(0x4f, 0xc3, 0x4c, 0xff));
+ }
+ if (statusLabel_ != nullptr) {
+ statusLabel_->SetText("血压处于正常范围");
+ statusLabel_->SetTextColor(Color::GetColorFromRGBA(0x4f, 0xc3, 0x4c, 0xff));
+ }
+ if (sbpValue_ != nullptr) {
+ sbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ }
+ if (dbpValue_ != nullptr) {
+ dbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ }
+}
+
+//应用偏高布局
+void BPMeasureResultView::ApplyHighLayout() {
+ if (titleLabel_ != nullptr) {
+ titleLabel_->SetText("测量成功");
+ titleLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ }
+ if (statusLabel_ != nullptr) {
+ statusLabel_->SetText("血压偏高,请关注");
+ statusLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ }
+ if (sbpValue_ != nullptr) {
+ sbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ }
+ if (dbpValue_ != nullptr) {
+ dbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ }
+}
+
+//应用校准过期布局
+void BPMeasureResultView::ApplyCalibExpiredLayout() {
+ if (titleLabel_ != nullptr) {
+ titleLabel_->SetText("测量成功");
+ }
+ if (statusLabel_ != nullptr) {
+ statusLabel_->SetText("校准已过期");
+ statusLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ }
+
+}
+
+//更新文本
+void BPMeasureResultView::UpdateText() {
+ BPModel *model = BPModel::GetInstance();
+ uint8_t sbp = model->GetResultSbp();
+ uint8_t dbp = model->GetResultDbp();
+
+ if (timeLabel_ != nullptr) {
+ timeLabel_->SetText(FormatMeasureTime().c_str());
+ }
+ if (sbpValue_ != nullptr) {
+ sbpValue_->SetText(std::to_string(sbp).c_str());
+ }
+ if (dbpValue_ != nullptr) {
+ dbpValue_->SetText(std::to_string(dbp).c_str());
+ }
+
+ if (IsHighBloodPressure(sbp, dbp)) {
+ ApplyHighLayout();
+ } else {
+ ApplyNormalLayout();
+ }
+}
+
+UIViewGroup *BPMeasureResultView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "bp_result_title", "测量成功",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(timeLabel_, 0, 93, 466, 33, "bp_result_time", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 28);
+ timeLabel_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ timeLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitDividerLines();
+
+ InitLabel(sbpTitle_, BP_RESULT_LEFT_X, 140, BP_RESULT_COL_W, 30, "bp_result_sbp_title", "收缩压",
+ DEFAULT_VECTOR_FONT_FILENAME, 22);
+ InitLabel(dbpTitle_, BP_RESULT_RIGHT_X, 140, BP_RESULT_COL_W, 30, "bp_result_dbp_title", "舒张压",
+ DEFAULT_VECTOR_FONT_FILENAME, 22);
+ sbpTitle_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpTitle_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ sbpTitle_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ dbpTitle_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+
+ InitUILabel(sbpValue_, BP_RESULT_LEFT_X, 178, BP_RESULT_COL_W, 64, "bp_result_sbp", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 56);
+ InitUILabel(dbpValue_, BP_RESULT_RIGHT_X, 178, BP_RESULT_COL_W, 64, "bp_result_dbp", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 56);
+ sbpValue_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpValue_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(sbpUnit_, BP_RESULT_LEFT_X, 248, BP_RESULT_COL_W, 28, "bp_result_sbp_unit", "mmHg",
+ DEFAULT_VECTOR_FONT_FILENAME, 22);
+ InitUILabel(dbpUnit_, BP_RESULT_RIGHT_X, 248, BP_RESULT_COL_W, 28, "bp_result_dbp_unit", "mmHg",
+ DEFAULT_VECTOR_FONT_FILENAME, 22);
+ sbpUnit_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpUnit_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ sbpUnit_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ dbpUnit_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+
+ InitLabel(statusLabel_, 0, 292, 466, 35, "bp_result_status", "血压处于正常范围",
+ DEFAULT_VECTOR_FONT_FILENAME, 26);
+ statusLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabelButton(confirmButton_, 93, 330, 280, 90, BP_CLICK_CONFIRM, "确定", 30);
+ confirmButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff191919);
+ confirmButton_->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xff191919, UIButton::PRESSED);
+ confirmButton_->SetStyle(STYLE_BORDER_RADIUS, 45);
+ confirmButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance()));
+
+ group_->Add(titleLabel_);
+ group_->Add(timeLabel_);
+ if (hlineTop_ != nullptr) {
+ group_->Add(hlineTop_);
+ }
+ if (vlineMid_ != nullptr) {
+ group_->Add(vlineMid_);
+ }
+ group_->Add(sbpTitle_);
+ group_->Add(dbpTitle_);
+ group_->Add(sbpValue_);
+ group_->Add(dbpValue_);
+ group_->Add(sbpUnit_);
+ group_->Add(dbpUnit_);
+ if (hlineBottom_ != nullptr) {
+ group_->Add(hlineBottom_);
+ }
+ group_->Add(statusLabel_);
+ group_->Add(confirmButton_);
+
+ UpdateText();
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPMeasuringView.h"
+#include "common/image_cache_manager.h"
+#include "securec.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+static constexpr int16_t BP_MEASURING_ANIM_X = 133;
+static constexpr int16_t BP_MEASURING_ANIM_Y = 105;
+static constexpr int16_t BP_MEASURING_ANIM_W = 200;
+static constexpr int16_t BP_MEASURING_ANIM_H = 200;
+static constexpr int16_t BP_MEASURING_ANIM_FRAME_COUNT = 40;
+static constexpr int16_t BP_MEASURING_ANIM_DELAY = 70;
+
+static const char *kMeasuringAnimPaths[BP_MEASURING_ANIM_FRAME_COUNT] = {
+ "/user/res/AIVoice/AIVoiceThinking/1.bin",
+ "/user/res/AIVoice/AIVoiceThinking/2.bin",
+ "/user/res/AIVoice/AIVoiceThinking/3.bin",
+ "/user/res/AIVoice/AIVoiceThinking/4.bin",
+ "/user/res/AIVoice/AIVoiceThinking/5.bin",
+ "/user/res/AIVoice/AIVoiceThinking/6.bin",
+ "/user/res/AIVoice/AIVoiceThinking/7.bin",
+ "/user/res/AIVoice/AIVoiceThinking/8.bin",
+ "/user/res/AIVoice/AIVoiceThinking/9.bin",
+ "/user/res/AIVoice/AIVoiceThinking/10.bin",
+ "/user/res/AIVoice/AIVoiceThinking/11.bin",
+ "/user/res/AIVoice/AIVoiceThinking/12.bin",
+ "/user/res/AIVoice/AIVoiceThinking/13.bin",
+ "/user/res/AIVoice/AIVoiceThinking/14.bin",
+ "/user/res/AIVoice/AIVoiceThinking/15.bin",
+ "/user/res/AIVoice/AIVoiceThinking/16.bin",
+ "/user/res/AIVoice/AIVoiceThinking/17.bin",
+ "/user/res/AIVoice/AIVoiceThinking/18.bin",
+ "/user/res/AIVoice/AIVoiceThinking/19.bin",
+ "/user/res/AIVoice/AIVoiceThinking/20.bin",
+ "/user/res/AIVoice/AIVoiceThinking/21.bin",
+ "/user/res/AIVoice/AIVoiceThinking/22.bin",
+ "/user/res/AIVoice/AIVoiceThinking/23.bin",
+ "/user/res/AIVoice/AIVoiceThinking/24.bin",
+ "/user/res/AIVoice/AIVoiceThinking/25.bin",
+ "/user/res/AIVoice/AIVoiceThinking/26.bin",
+ "/user/res/AIVoice/AIVoiceThinking/27.bin",
+ "/user/res/AIVoice/AIVoiceThinking/28.bin",
+ "/user/res/AIVoice/AIVoiceThinking/29.bin",
+ "/user/res/AIVoice/AIVoiceThinking/30.bin",
+ "/user/res/AIVoice/AIVoiceThinking/31.bin",
+ "/user/res/AIVoice/AIVoiceThinking/32.bin",
+ "/user/res/AIVoice/AIVoiceThinking/33.bin",
+ "/user/res/AIVoice/AIVoiceThinking/34.bin",
+ "/user/res/AIVoice/AIVoiceThinking/35.bin",
+ "/user/res/AIVoice/AIVoiceThinking/36.bin",
+ "/user/res/AIVoice/AIVoiceThinking/37.bin",
+ "/user/res/AIVoice/AIVoiceThinking/38.bin",
+ "/user/res/AIVoice/AIVoiceThinking/39.bin",
+ "/user/res/AIVoice/AIVoiceThinking/40.bin",
+};
+
+static BPMeasuringView *g_instance = nullptr;
+
+BPMeasuringView::BPMeasuringView() { g_instance = this; }
+
+BPMeasuringView::~BPMeasuringView() {
+ StopMeasuringAnimation();
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete noteLabel_;
+ delete animatorView_;
+ delete group_;
+ noteLabel_ = nullptr;
+ animatorView_ = nullptr;
+ group_ = nullptr;
+ }
+ UnloadAnimatorFrames();
+ g_instance = nullptr;
+}
+
+BPMeasuringView *BPMeasuringView::GetInstance() { return g_instance; }
+
+bool BPMeasuringView::InitMeasuringAnimator() {
+ if (animatorInited_) {
+ return animatorView_ != nullptr;
+ }
+ animatorView_ = new UIImageAnimatorView();
+ if (animatorView_ == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPMeasuringView animator new fail");
+ return false;
+ }
+ for (int16_t i = 0; i < BP_MEASURING_ANIM_FRAME_COUNT; i++) {
+ frameInfo_[i].imageInfo =
+ ImageCacheManager::GetInstance().LoadSingleRes(kMeasuringAnimPaths[i]);
+ frameInfo_[i].imageType = IMG_SRC_IMAGE_INFO;
+ frameInfo_[i].pos.x = BP_MEASURING_ANIM_X;
+ frameInfo_[i].pos.y = BP_MEASURING_ANIM_Y;
+ frameInfo_[i].width = BP_MEASURING_ANIM_W;
+ frameInfo_[i].height = BP_MEASURING_ANIM_H;
+ }
+ animatorView_->SetPosition(BP_MEASURING_ANIM_X, BP_MEASURING_ANIM_Y,
+ BP_MEASURING_ANIM_W, BP_MEASURING_ANIM_H);
+ animatorView_->SetImageAnimatorSrc(frameInfo_, BP_MEASURING_ANIM_FRAME_COUNT,
+ BP_MEASURING_ANIM_DELAY);
+ animatorView_->SetRepeat(true);
+ animatorView_->SetAutoEnable(false);
+ animatorView_->SetResizeMode(UIImageView::FILL);
+ animatorInited_ = true;
+ return true;
+}
+
+void BPMeasuringView::UnloadAnimatorFrames() {
+ if (!animatorInited_) {
+ return;
+ }
+ for (int16_t i = 0; i < BP_MEASURING_ANIM_FRAME_COUNT; i++) {
+ ImageCacheManager::GetInstance().UnloadSingleRes(kMeasuringAnimPaths[i]);
+ frameInfo_[i].imageInfo = nullptr;
+ }
+ animatorInited_ = false;
+}
+
+void BPMeasuringView::StartMeasuringAnimation() {
+ if (!InitMeasuringAnimator() || animatorView_ == nullptr) {
+ return;
+ }
+ animatorView_->Start();
+}
+
+void BPMeasuringView::StopMeasuringAnimation() {
+ if (animatorView_ != nullptr) {
+ animatorView_->Stop();
+ }
+}
+
+UIViewGroup *BPMeasuringView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(),
+ Screen::GetInstance().GetHeight());
+ if (!InitMeasuringAnimator()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPMeasuringView InitMeasuringAnimator fail");
+ } else if (animatorView_ != nullptr) {
+ group_->Add(animatorView_);
+ }
+ InitUILabel(noteLabel_, 0, BP_MEASURING_ANIM_Y + BP_MEASURING_ANIM_H + 20, 466, 35,
+ "bp_measuring_note", "请保持静止", DEFAULT_VECTOR_FONT_FILENAME, 30);
+ group_->Add(noteLabel_);
+ return group_;
+}
+
+void BPMeasuringView::UpdateProgressPercent(int percent) {
+ if (noteLabel_ == nullptr) {
+ return;
+ }
+ if (percent < 0) {
+ percent = 0;
+ } else if (percent > 100) {
+ percent = 100;
+ }
+ char buf[32] = {0};
+ if (sprintf_s(buf, sizeof(buf), "请保持静止 %d%%", percent) < 0) {
+ noteLabel_->SetText("请保持静止");
+ return;
+ }
+ noteLabel_->SetText(buf);
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPModel.h"
+#include "custom_utils.h"
+#include "kv_store.h"
+#include "securec.h"
+#include "wearable_log.h"
+#include <cstdio>
+#include <ctime>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace OHOS {
+
+namespace {
+static constexpr const char *BP_DATA_DIR = "/user/userdata/health/bp";
+static constexpr const char *BP_CALIBRATION_FILE = "/user/userdata/health/bp/BPCalibration.bin";
+static constexpr const char *BP_HISTORY_FILE = "/user/userdata/health/bp/BPHistory.bin";
+static constexpr const char *BP_KV_DISCLAIMER = "bp.disclaimer";
+/** 校准时间戳无效值 */
+static constexpr int32_t BP_CALIBRATION_INVALID_TS = -1;
+static constexpr uint32_t BP_CALIBRATION_MAGIC_V1 = 0x42504301U;
+static constexpr uint32_t BP_CALIBRATION_MAGIC_V2 = 0x42504302U;
+static constexpr uint32_t BP_HISTORY_MAGIC = 0x42504801U;
+static constexpr uint32_t BP_HISTORY_MAX_COUNT = 50U;
+
+struct BPCalibrationStorageV1 {
+ uint32_t magic;
+ int32_t timestamp;
+};
+
+struct BPCalibrationStorageV2 {
+ uint32_t magic;
+ int32_t timestamp;
+ uint8_t sbp1;
+ uint8_t dbp1;
+ uint8_t sbp2;
+ uint8_t dbp2;
+ uint8_t reserved[3];
+};
+
+struct BPHistoryStorage {
+ uint32_t magic;
+ uint32_t count;
+ BPRecord records[50];
+};
+} // namespace
+
+BPModel::BPModel() {
+ LoadCalibrationFromStorage();
+ LoadData();
+}
+
+BPModel::~BPModel() {}
+
+BPModel *BPModel::GetInstance() {
+ static BPModel instance;
+ return &instance;
+}
+
+bool BPModel::IsFirstUse() const {
+ char buf[8] = {0};
+ int len = UtilsGetValue(BP_KV_DISCLAIMER, buf, sizeof(buf));
+ return len <= 0 || buf[0] != '1';
+}
+
+bool BPModel::MarkDisclaimerAccepted() {
+ return UtilsSetValue(BP_KV_DISCLAIMER, "1") == 0;
+}
+
+bool BPModel::CreateBPDataDirectory() {
+ if (access(BP_DATA_DIR, F_OK) == 0) {
+ return true;
+ }
+ if (mkdir(BP_DATA_DIR, S_IRWXU) != 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPModel::CreateBPDataDirectory mkdir fail");
+ return false;
+ }
+ return true;
+}
+
+/** 从存储中加载校准数据 */
+bool BPModel::LoadCalibrationFromStorage() {
+ calibrationTimestamp_ = BP_CALIBRATION_INVALID_TS;
+ firstRoundSaved_ = false;
+
+ FILE *fp = fopen(BP_CALIBRATION_FILE, "rb");
+ if (fp == nullptr) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "BPModel no calibration file, state=-1");
+ return false;
+ }
+
+ BPCalibrationStorageV2 storage = {0};
+ size_t readSize = fread(&storage, sizeof(BPCalibrationStorageV2), 1, fp);
+ fclose(fp);
+
+ if (readSize != 1) {
+ calibrationTimestamp_ = BP_CALIBRATION_INVALID_TS;
+ return false;
+ }
+
+ if (storage.magic == BP_CALIBRATION_MAGIC_V2) {
+ calibrationTimestamp_ = storage.timestamp;
+ firstRoundSbp_ = storage.sbp1;
+ firstRoundDbp_ = storage.dbp1;
+ secondRoundSbp_ = storage.sbp2;
+ secondRoundDbp_ = storage.dbp2;
+ firstRoundSaved_ = true;
+ return true;
+ }
+
+ if (storage.magic == BP_CALIBRATION_MAGIC_V1) {
+ BPCalibrationStorageV1 *legacy = reinterpret_cast<BPCalibrationStorageV1 *>(&storage);
+ if (legacy->timestamp != BP_CALIBRATION_INVALID_TS) {
+ calibrationTimestamp_ = legacy->timestamp;
+ return true;
+ }
+ }
+
+ calibrationTimestamp_ = BP_CALIBRATION_INVALID_TS;
+ return false;
+}
+
+/** 校准完成后写入当前时间戳与校准值 */
+bool BPModel::SaveCalibrationToStorage() {
+ if (!CreateBPDataDirectory()) {
+ return false;
+ }
+
+ BPCalibrationStorageV2 storage = {0};
+ storage.magic = BP_CALIBRATION_MAGIC_V2;
+ storage.timestamp = calibrationTimestamp_;
+ storage.sbp1 = firstRoundSbp_;
+ storage.dbp1 = firstRoundDbp_;
+ storage.sbp2 = secondRoundSbp_;
+ storage.dbp2 = secondRoundDbp_;
+
+ FILE *fp = fopen(BP_CALIBRATION_FILE, "wb");
+ if (fp == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPModel SaveCalibration fopen fail");
+ return false;
+ }
+
+ size_t writeSize = fwrite(&storage, sizeof(BPCalibrationStorageV2), 1, fp);
+ fclose(fp);
+ return writeSize == 1;
+}
+
+/** 校准完成后写入当前时间戳与校准值 */
+bool BPModel::SaveCalibrationResult() {
+ uint32_t now = 0;
+ if (uapi_get_timestampe_ss(&now) != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPModel::SaveCalibrationResult get timestamp fail");
+ return false;
+ }
+
+ calibrationTimestamp_ = static_cast<int32_t>(now);
+ if (!SaveCalibrationToStorage()) {
+ return false;
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "BPModel calibration saved ts=%d sbp1=%u dbp1=%u sbp2=%u dbp2=%u",
+ calibrationTimestamp_, firstRoundSbp_, firstRoundDbp_, secondRoundSbp_, secondRoundDbp_);
+ return true;
+}
+
+int32_t BPModel::GetCalibrationTimestamp() const { return calibrationTimestamp_; }
+
+/** 设置校准有效期(秒),默认 30 天,与 fd230 needMeasure 一致 */
+void BPModel::SetCalibrationTimeoutSec(uint32_t seconds) {
+ if (seconds > 0) {
+ calibrationTimeoutSec_ = seconds;
+ }
+}
+
+/** 获取校准有效期(秒) */
+uint32_t BPModel::GetCalibrationTimeoutSec() const { return calibrationTimeoutSec_; }
+
+/** 获取当前校准状态 */
+int8_t BPModel::GetCurrentstate() {
+ if (calibrationTimestamp_ == BP_CALIBRATION_INVALID_TS) {
+ LoadCalibrationFromStorage();
+ }
+
+ if (calibrationTimestamp_ == BP_CALIBRATION_INVALID_TS) {
+ return BP_CALIB_STATE_NOT_CALIBRATED;
+ }
+
+ uint32_t now = 0;
+ if (uapi_get_timestampe_ss(&now) != ERRCODE_SUCC) {
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "BPModel::GetCurrentstate get timestamp fail");
+ return BP_CALIB_STATE_NOT_CALIBRATED;
+ }
+
+ uint32_t calibTime = static_cast<uint32_t>(calibrationTimestamp_);
+ if (now < calibTime) {
+ return BP_CALIB_STATE_VALID;
+ }
+
+ uint32_t elapsed = now - calibTime;
+ if (elapsed > calibrationTimeoutSec_) {
+ return BP_CALIB_STATE_EXPIRED;
+ }
+
+ return BP_CALIB_STATE_VALID;
+}
+
+/** 获取校准剩余天数,-1 表示未校准或已过期 */
+int32_t BPModel::GetCalibrationRemainingDays() const {
+ if (calibrationTimestamp_ == BP_CALIBRATION_INVALID_TS) {
+ return -1;
+ }
+
+ uint32_t now = 0;
+ if (uapi_get_timestampe_ss(&now) != ERRCODE_SUCC) {
+ return -1;
+ }
+
+ uint32_t calibTime = static_cast<uint32_t>(calibrationTimestamp_);
+ if (now < calibTime) {
+ return static_cast<int32_t>((calibrationTimeoutSec_ + 86399U) / 86400U);
+ }
+
+ uint32_t elapsed = now - calibTime;
+ if (elapsed >= calibrationTimeoutSec_) {
+ return -1;
+ }
+
+ uint32_t remaining = calibrationTimeoutSec_ - elapsed;
+ return static_cast<int32_t>((remaining + 86399U) / 86400U);
+}
+
+/** 校准即将过期(剩余天数 <= warnDays) */
+bool BPModel::IsCalibrationExpiringSoon(uint32_t warnDays) const {
+ int32_t days = GetCalibrationRemainingDays();
+ return days >= 0 && static_cast<uint32_t>(days) <= warnDays;
+}
+
+/** 从存储中加载历史记录 */
+bool BPModel::LoadHistoryFromStorage() {
+ historyRecords_.clear();
+ lastRecord_ = {0, 0, 0};
+
+ FILE *fp = fopen(BP_HISTORY_FILE, "rb");
+ if (fp == nullptr) {
+ return false;
+ }
+
+ BPHistoryStorage storage = {0};
+ size_t readSize = fread(&storage, sizeof(BPHistoryStorage), 1, fp);
+ fclose(fp);
+
+ if (readSize != 1 || storage.magic != BP_HISTORY_MAGIC || storage.count == 0 ||
+ storage.count > BP_HISTORY_MAX_COUNT) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < storage.count; ++i) {
+ historyRecords_.push_back(storage.records[i]);
+ }
+ if (!historyRecords_.empty()) {
+ lastRecord_ = historyRecords_.front();
+ }
+ return true;
+}
+
+/** 将历史记录保存到存储 */
+bool BPModel::SaveHistoryToStorage() {
+ if (!CreateBPDataDirectory()) {
+ return false;
+ }
+
+ BPHistoryStorage storage = {0};
+ storage.magic = BP_HISTORY_MAGIC;
+ storage.count = static_cast<uint32_t>(historyRecords_.size());
+ if (storage.count > BP_HISTORY_MAX_COUNT) {
+ storage.count = BP_HISTORY_MAX_COUNT;
+ }
+ for (uint32_t i = 0; i < storage.count; ++i) {
+ storage.records[i] = historyRecords_[i];
+ }
+
+ FILE *fp = fopen(BP_HISTORY_FILE, "wb");
+ if (fp == nullptr) {
+ return false;
+ }
+ size_t writeSize = fwrite(&storage, sizeof(BPHistoryStorage), 1, fp);
+ fclose(fp);
+ return writeSize == 1;
+}
+
+/** 加载数据 */
+void BPModel::LoadData() {
+ if (LoadHistoryFromStorage()) {
+ return;
+ }
+
+ historyRecords_.clear();
+ lastRecord_ = {0, 0, 0};
+}
+
+/** 获取最后一次测量记录 */
+BPRecord BPModel::GetLastRecord() const { return lastRecord_; }
+
+/** 获取历史记录 */
+const std::deque<BPRecord> &BPModel::GetHistoryRecords() const { return historyRecords_; }
+
+/** 格式化时间戳 */
+std::string BPModel::FormatTimestamp(uint32_t timestamp) const {
+ if (timestamp == 0) {
+ return "--";
+ }
+ struct tm timeInfo = {0};
+ time_t ts = static_cast<time_t>(timestamp);
+ if (localtime_r(&ts, &timeInfo) == nullptr) {
+ return "--";
+ }
+ char buf[32] = {0};
+ if (sprintf_s(buf, sizeof(buf), "%04d/%02d/%02d %02d:%02d", timeInfo.tm_year + 1900,
+ timeInfo.tm_mon + 1, timeInfo.tm_mday, timeInfo.tm_hour,
+ timeInfo.tm_min) < 0) {
+ return "--";
+ }
+ return std::string(buf);
+}
+
+/** 格式化短时间戳 */
+std::string BPModel::FormatShortTimestamp(uint32_t timestamp) const {
+ if (timestamp == 0) {
+ return "--";
+ }
+ struct tm timeInfo = {0};
+ time_t ts = static_cast<time_t>(timestamp);
+ if (localtime_r(&ts, &timeInfo) == nullptr) {
+ return "--";
+ }
+ char buf[32] = {0};
+ if (sprintf_s(buf, sizeof(buf), "%02d-%02d %02d:%02d", timeInfo.tm_mon + 1,
+ timeInfo.tm_mday, timeInfo.tm_hour, timeInfo.tm_min) < 0) {
+ return "--";
+ }
+ return std::string(buf);
+}
+
+/** 记忆第一轮输入值 */
+void BPModel::ResetInputValues() {
+ if (calibrationRound_ == 2 && firstRoundSaved_) {
+ inputSbp_ = firstRoundSbp_;
+ inputDbp_ = firstRoundDbp_;
+ } else {
+ inputSbp_ = 120;
+ inputDbp_ = 80;
+ }
+}
+
+/** 调整输入值 */
+void BPModel::AdjustInputValue(BPValueType type, bool increase) {
+ uint8_t minVal = (type == BPValueType::SBP) ? 60 : 40;
+ uint8_t maxVal = (type == BPValueType::SBP) ? 250 : 150;
+ uint8_t *value = (type == BPValueType::SBP) ? &inputSbp_ : &inputDbp_;
+ if (increase) {
+ if (*value < maxVal) {
+ (*value)++;
+ }
+ } else if (*value > minVal) {
+ (*value)--;
+ }
+}
+
+/** 设置收缩压输入值 */
+void BPModel::SetInputSbp(uint8_t sbp) {
+ if (sbp < 60) {
+ inputSbp_ = 60;
+ } else if (sbp > 250) {
+ inputSbp_ = 250;
+ } else {
+ inputSbp_ = sbp;
+ }
+}
+
+/** 设置舒张压输入值 */
+void BPModel::SetInputDbp(uint8_t dbp) {
+ if (dbp < 40) {
+ inputDbp_ = 40;
+ } else if (dbp > 150) {
+ inputDbp_ = 150;
+ } else {
+ inputDbp_ = dbp;
+ }
+}
+
+/** 获取收缩压输入值 */
+uint8_t BPModel::GetInputSbp() const { return inputSbp_; }
+
+/** 获取舒张压输入值 */
+uint8_t BPModel::GetInputDbp() const { return inputDbp_; }
+
+/** 设置测量结果 */
+void BPModel::SetMeasureResult(uint8_t sbp, uint8_t dbp) {
+ resultSbp_ = sbp;
+ resultDbp_ = dbp;
+}
+
+/** 获取收缩压测量结果 */
+uint8_t BPModel::GetResultSbp() const { return resultSbp_; }
+
+/** 获取舒张压测量结果 */
+uint8_t BPModel::GetResultDbp() const { return resultDbp_; }
+
+/** 设置校准轮次 */
+void BPModel::SetCalibrationRound(uint8_t round) { calibrationRound_ = round; }
+
+/** 获取校准轮次 */
+uint8_t BPModel::GetCalibrationRound() const { return calibrationRound_; }
+
+/** 保存第一轮输入值 */
+void BPModel::SaveFirstRoundInput() {
+ firstRoundSbp_ = inputSbp_;
+ firstRoundDbp_ = inputDbp_;
+ firstRoundSaved_ = true;
+}
+
+/** 设置第二轮输入值 */
+void BPModel::SetSecondRoundInput(uint8_t sbp, uint8_t dbp) {
+ secondRoundSbp_ = sbp;
+ secondRoundDbp_ = dbp;
+}
+
+/** 判断校准差异是否过大 */
+ bool BPModel::IsCalibrationDifferenceTooLarge() const {
+ int sbpDiff = static_cast<int>(firstRoundSbp_) - static_cast<int>(secondRoundSbp_);
+ int dbpDiff = static_cast<int>(firstRoundDbp_) - static_cast<int>(secondRoundDbp_);
+ if (sbpDiff < 0) {
+ sbpDiff = -sbpDiff;
+ }
+ if (dbpDiff < 0) {
+ dbpDiff = -dbpDiff;
+ }
+ return sbpDiff > BP_CALIB_DIFF_THRESHOLD || dbpDiff > BP_CALIB_DIFF_THRESHOLD;
+}
+
+/** 保存测量记录 */
+bool BPModel::SaveMeasureRecord(uint8_t sbp, uint8_t dbp) {
+ uint32_t now = 0;
+ if (uapi_get_timestampe_ss(&now) != ERRCODE_SUCC) {
+ return false;
+ }
+
+ BPRecord record = {now, sbp, dbp};
+ historyRecords_.push_front(record);
+ while (historyRecords_.size() > BP_HISTORY_MAX_COUNT) {
+ historyRecords_.pop_back();
+ }
+ lastRecord_ = record;
+ return SaveHistoryToStorage();
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPValueConfirmView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPModel.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "common/screen.h"
+#include "ui_resource_string.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+namespace {
+static constexpr uint32_t BP_COLOR_WHITE = 0xffffffff;
+static constexpr uint32_t BP_COLOR_GREY_UNIT = 0xff666666;
+static constexpr uint32_t BP_COLOR_ERROR_TEXT = 0xffff4444;
+static constexpr uint32_t BP_COLOR_SBP_ERROR = 0xffff4444;
+static constexpr uint32_t BP_COLOR_BTN_GREY = 0xff333333;
+static constexpr uint32_t BP_COLOR_BTN_BLUE = 0xff0176ff;
+static constexpr uint32_t BP_COLOR_BTN_ORANGE = 0xffff5a36;
+static constexpr uint32_t BP_COLOR_LINE = 0xff333333;
+static constexpr int16_t BP_CONFIRM_BTN_Y = 320;
+static constexpr int16_t BP_CONFIRM_BTN_H = 90;
+static constexpr int16_t BP_CONFIRM_BTN_W = 170;
+static constexpr int16_t BP_CONFIRM_BTN_LEFT_X = 58;
+static constexpr int16_t BP_CONFIRM_BTN_RIGHT_X = 238;
+static constexpr int16_t BP_CONFIRM_ERROR_OK_X = 93;
+static constexpr int16_t BP_CONFIRM_ERROR_OK_W = 280;
+} // namespace
+
+static BPValueConfirmView *g_instance = nullptr;
+
+BPValueConfirmView::BPValueConfirmView() { g_instance = this; }
+
+BPValueConfirmView::~BPValueConfirmView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete errorLabel_;
+ delete hlineTop_;
+ delete vlineMid_;
+ delete sbpTitle_;
+ delete dbpTitle_;
+ delete sbpValue_;
+ delete dbpValue_;
+ delete sbpUnit_;
+ delete dbpUnit_;
+ delete modifyButton_;
+ delete startCalibButton_;
+ delete errorOkButton_;
+ delete group_;
+ titleLabel_ = nullptr;
+ errorLabel_ = nullptr;
+ hlineTop_ = vlineMid_ = nullptr;
+ sbpTitle_ = dbpTitle_ = nullptr;
+ sbpValue_ = dbpValue_ = nullptr;
+ sbpUnit_ = dbpUnit_ = nullptr;
+ modifyButton_ = startCalibButton_ = errorOkButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPValueConfirmView *BPValueConfirmView::GetInstance() { return g_instance; }
+
+
+void BPValueConfirmView::InitDividerLines() {
+ hlineTop_ = new UIImageView();
+ if (hlineTop_ != nullptr) {
+ hlineTop_->SetPosition(83, 128, 300, 2);
+ hlineTop_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ hlineTop_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ hlineTop_->SetVisible(true);
+ }
+ vlineMid_ = new UIImageView();
+ if (vlineMid_ != nullptr) {
+ vlineMid_->SetPosition(233, 140, 2, 130);
+ vlineMid_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_LINE);
+ vlineMid_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ vlineMid_->SetVisible(true);
+ }
+}
+
+void BPValueConfirmView::InitActionButtons() {
+ auto *listener = static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance());
+
+ InitLabelButton(modifyButton_, BP_CONFIRM_BTN_LEFT_X, BP_CONFIRM_BTN_Y, BP_CONFIRM_BTN_W,
+ BP_CONFIRM_BTN_H, BP_CLICK_STOP, "修改", 26);
+ modifyButton_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_BTN_GREY);
+ modifyButton_->SetStyleForState(STYLE_BACKGROUND_COLOR, BP_COLOR_BTN_GREY, UIButton::PRESSED);
+ modifyButton_->SetStyle(STYLE_BORDER_RADIUS, 45);
+ modifyButton_->SetOnClickListener(listener);
+
+ InitLabelButton(startCalibButton_, BP_CONFIRM_BTN_RIGHT_X, BP_CONFIRM_BTN_Y, BP_CONFIRM_BTN_W,
+ BP_CONFIRM_BTN_H, BP_CLICK_VALUE_CONFIRM, "开始校准", 26);
+ startCalibButton_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_BTN_BLUE);
+ startCalibButton_->SetStyleForState(STYLE_BACKGROUND_COLOR, BP_COLOR_BTN_BLUE, UIButton::PRESSED);
+ startCalibButton_->SetStyle(STYLE_BORDER_RADIUS, 45);
+ startCalibButton_->SetOnClickListener(listener);
+
+ InitLabelButton(errorOkButton_, BP_CONFIRM_ERROR_OK_X, 330, BP_CONFIRM_ERROR_OK_W,
+ BP_CONFIRM_BTN_H, BP_CLICK_VALUE_ERROR_OK, "确定", 30);
+ errorOkButton_->SetStyle(STYLE_BACKGROUND_COLOR, BP_COLOR_BTN_ORANGE);
+ errorOkButton_->SetStyleForState(STYLE_BACKGROUND_COLOR, BP_COLOR_BTN_ORANGE, UIButton::PRESSED);
+ errorOkButton_->SetStyle(STYLE_BORDER_RADIUS, 45);
+ errorOkButton_->SetOnClickListener(listener);
+}
+
+/**
+ * @description: 有效血压值布局
+ */
+void BPValueConfirmView::ApplyValidLayout() {
+ isValid_ = true;
+ if (errorLabel_ != nullptr) {
+ errorLabel_->SetVisible(false);
+ }
+ if (sbpValue_ != nullptr) {
+ sbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ }
+ if (dbpValue_ != nullptr) {
+ dbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ }
+ if (modifyButton_ != nullptr) {
+ modifyButton_->SetVisible(true);
+ }
+ if (startCalibButton_ != nullptr) {
+ startCalibButton_->SetVisible(true);
+ }
+ if (errorOkButton_ != nullptr) {
+ errorOkButton_->SetVisible(false);
+ }
+}
+
+/**
+ * @description: 无效血压值布局
+ */
+void BPValueConfirmView::ApplyInvalidLayout(uint8_t sbp, uint8_t dbp) {
+ isValid_ = false;
+ if (errorLabel_ != nullptr) {
+ errorLabel_->SetVisible(true);
+ }
+ if (sbpValue_ != nullptr) {
+ sbpValue_->SetText(std::to_string(sbp).c_str());
+ sbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff));
+ }
+ if (dbpValue_ != nullptr) {
+ dbpValue_->SetText(std::to_string(dbp).c_str());
+ dbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ }
+ if (modifyButton_ != nullptr) {
+ modifyButton_->SetVisible(false);
+ }
+ if (startCalibButton_ != nullptr) {
+ startCalibButton_->SetVisible(false);
+ }
+ if (errorOkButton_ != nullptr) {
+ errorOkButton_->SetVisible(true);
+ }
+}
+
+/**
+ * @description: 更新视图
+ */
+void BPValueConfirmView::UpdateView() {
+ BPModel *model = BPModel::GetInstance();
+ uint8_t sbp = model->GetInputSbp();
+ uint8_t dbp = model->GetInputDbp();
+
+ if (sbpValue_ != nullptr) {
+ sbpValue_->SetText(std::to_string(sbp).c_str());
+ }
+ if (dbpValue_ != nullptr) {
+ dbpValue_->SetText(std::to_string(dbp).c_str());
+ }
+
+ if (sbp > dbp) {
+ ApplyValidLayout();
+ } else {
+ ApplyInvalidLayout(sbp, dbp);
+ }
+}
+
+UIViewGroup *BPValueConfirmView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPValueConfirmView InitView group fail");
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ InitLabelExt(titleLabel_, 83, 50, 300, 35, "bp_value_confirm_title", STR_CONFIRM,
+ DEFAULT_VECTOR_FONT_FILENAME, 28);
+ titleLabel_->SetText("确认输入值");
+ titleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabelExt(errorLabel_, 53, 88, 360, 32, "bp_value_confirm_error", STR_CONFIRM,
+ DEFAULT_VECTOR_FONT_FILENAME, 22);
+ errorLabel_->SetText("收缩压应大于舒张压");
+ errorLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x44, 0x44, 0xff));
+ errorLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ errorLabel_->SetVisible(false);
+
+ InitDividerLines();
+
+ InitLabel(sbpTitle_, BP_CONFIRM_BTN_LEFT_X, 140, BP_CONFIRM_BTN_W, 30, "bp_confirm_sbp_title", "收缩压",
+ DEFAULT_VECTOR_FONT_FILENAME, 22);
+ InitLabel(dbpTitle_, BP_CONFIRM_BTN_RIGHT_X, 140, BP_CONFIRM_BTN_W, 30, "bp_confirm_dbp_title", "舒张压",
+ DEFAULT_VECTOR_FONT_FILENAME, 22);
+ sbpTitle_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpTitle_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(sbpValue_, BP_CONFIRM_BTN_LEFT_X, 178, BP_CONFIRM_BTN_W, 64, "bp_confirm_sbp", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 56);
+ InitUILabel(dbpValue_, BP_CONFIRM_BTN_RIGHT_X, 178, BP_CONFIRM_BTN_W, 64, "bp_confirm_dbp", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 56);
+ sbpValue_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpValue_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ sbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ dbpValue_->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+
+ InitUILabel(sbpUnit_, BP_CONFIRM_BTN_LEFT_X, 248, BP_CONFIRM_BTN_W, 28, "bp_confirm_sbp_unit",
+ "mmHg", DEFAULT_VECTOR_FONT_FILENAME, 22);
+ InitUILabel(dbpUnit_, BP_CONFIRM_BTN_RIGHT_X, 248, BP_CONFIRM_BTN_W, 28, "bp_confirm_dbp_unit",
+ "mmHg", DEFAULT_VECTOR_FONT_FILENAME, 22);
+ sbpUnit_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpUnit_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ sbpUnit_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ dbpUnit_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+
+ InitActionButtons();
+
+ group_->Add(titleLabel_);
+ group_->Add(errorLabel_);
+ if (hlineTop_ != nullptr) {
+ group_->Add(hlineTop_);
+ }
+ if (vlineMid_ != nullptr) {
+ group_->Add(vlineMid_);
+ }
+ group_->Add(sbpTitle_);
+ group_->Add(dbpTitle_);
+ group_->Add(sbpValue_);
+ group_->Add(dbpValue_);
+ group_->Add(sbpUnit_);
+ group_->Add(dbpUnit_);
+ group_->Add(modifyButton_);
+ group_->Add(startCalibButton_);
+ group_->Add(errorOkButton_);
+
+ UpdateView();
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "bloodpressure/include/BPValueInputView.h"
+#include "bloodpressure/include/BPMainPresenter.h"
+#include "bloodpressure/include/BPModel.h"
+#include "bloodpressure/include/BPViewClickIds.h"
+#include "common/screen.h"
+#include "graphic_config.h"
+#include "traing/Training_UiConfig.h"
+#include "ui_resource_string.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+namespace {
+static constexpr int16_t BP_PICKER_ITEM_HEIGHT = 70;
+static constexpr uint8_t BP_PICKER_BG_FONT = 40;
+static constexpr uint8_t BP_PICKER_HIGHLIGHT_FONT = 60;
+static constexpr int16_t BP_PICKER_Y = 126;
+static constexpr int16_t BP_PICKER_H = 181;
+static constexpr int16_t BP_SBP_PICKER_X = 79;
+static constexpr int16_t BP_SBP_PICKER_W = 120;
+static constexpr int16_t BP_DBP_PICKER_X = 267;
+static constexpr int16_t BP_DBP_PICKER_W = 120;
+static constexpr int16_t BP_UNIT_LABEL_Y = 324;
+} // namespace
+
+static BPValueInputView *g_instance = nullptr;
+
+BPValueInputView::BPValueInputView() { g_instance = this; }
+
+BPValueInputView::~BPValueInputView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete sbpLabel_;
+ delete dbpLabel_;
+ delete sbpPicker_;
+ delete dbpPicker_;
+ delete okButton_;
+ delete group_;
+ titleLabel_ = nullptr;
+ sbpLabel_ = nullptr;
+ dbpLabel_ = nullptr;
+ sbpPicker_ = nullptr;
+ dbpPicker_ = nullptr;
+ okButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+BPValueInputView *BPValueInputView::GetInstance() { return g_instance; }
+
+void BPValueInputView::InitBpPicker(UIPicker *&picker, int16_t x, int16_t y, int16_t width,
+ int16_t height, const char *viewId, int16_t start,
+ int16_t end) {
+ if (picker == nullptr) {
+ picker = new UIPicker();
+ }
+ if (picker == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "BPValueInputView InitBpPicker fail");
+ return;
+ }
+ picker->SetPosition(x, y);
+ picker->Resize(width, height);
+ picker->SetViewId(viewId);
+ picker->SetItemHeight(BP_PICKER_ITEM_HEIGHT);
+ picker->SetLoopState(true);
+ picker->SetTextColor(Color::GetColorFromRGBA(0x66, 0x66, 0x66, 0xff),
+ Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0xff));
+ picker->SetBackgroundFont(DEFAULT_VECTOR_FONT_FILENAME, BP_PICKER_BG_FONT);
+ picker->SetHighlightFont(DEFAULT_VECTOR_FONT_FILENAME, BP_PICKER_HIGHLIGHT_FONT);
+ picker->SetValues(start, end);
+ picker->SetVisible(true);
+}
+
+
+void BPValueInputView::SyncPickerFromModel() {
+ BPModel *model = BPModel::GetInstance();
+ if (sbpPicker_ != nullptr) {
+ int16_t sbp = static_cast<int16_t>(model->GetInputSbp());
+ if (sbp < SBP_MIN) {
+ sbp = SBP_MIN;
+ } else if (sbp > SBP_MAX) {
+ sbp = SBP_MAX;
+ }
+ sbpPicker_->SetSelected(static_cast<uint16_t>(sbp - SBP_MIN));
+ }
+ if (dbpPicker_ != nullptr) {
+ int16_t dbp = static_cast<int16_t>(model->GetInputDbp());
+ if (dbp < DBP_MIN) {
+ dbp = DBP_MIN;
+ } else if (dbp > DBP_MAX) {
+ dbp = DBP_MAX;
+ }
+ dbpPicker_->SetSelected(static_cast<uint16_t>(dbp - DBP_MIN));
+ }
+}
+
+UIViewGroup *BPValueInputView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ InitLabel(titleLabel_, 128, 50, 210, 35, "bp_value_input_title", "输入血压计值",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitBpPicker(sbpPicker_, BP_SBP_PICKER_X, BP_PICKER_Y, BP_SBP_PICKER_W, BP_PICKER_H,
+ "bp_sbp_picker", SBP_MIN, SBP_MAX);
+ InitBpPicker(dbpPicker_, BP_DBP_PICKER_X, BP_PICKER_Y, BP_DBP_PICKER_W, BP_PICKER_H,
+ "bp_dbp_picker", DBP_MIN, DBP_MAX);
+ InitLabel(sbpLabel_, 114, BP_UNIT_LABEL_Y, 60, 23, "bp_sbp_unit", "收缩压",
+ DEFAULT_VECTOR_FONT_FILENAME, 20);
+ InitLabel(dbpLabel_, 292, BP_UNIT_LABEL_Y, 60, 23, "bp_dbp_unit", "舒张压",
+ DEFAULT_VECTOR_FONT_FILENAME, 20);
+ sbpLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ dbpLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitButtonExt(okButton_, 0, 366, VERTICAL_RESOLUTION, 100, BP_CLICK_VALUE_NEXT, STR_OK, 30);
+ okButton_->SetImageSrc(BUTTONOK_IMAGE, BUTTONOK_IMAGE);
+ okButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(BPMainPresenter::GetInstance()));
+
+ group_->Add(titleLabel_);
+ group_->Add(sbpPicker_);
+ group_->Add(dbpPicker_);
+ group_->Add(sbpLabel_);
+ group_->Add(dbpLabel_);
+ group_->Add(okButton_);
+ SyncPickerFromModel();
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#ifndef ECG_ABNORMAL_VIEW_H
+#define ECG_ABNORMAL_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+#include "ecg/include/ECGViewClickIds.h"
+
+#define ECG_PATH_ABNORMAL "/user/res/bloodpressure/nonet.bin"
+
+class ECGAbnormalView {
+public:
+ ECGAbnormalView();
+ ~ECGAbnormalView();
+ static ECGAbnormalView *GetInstance();
+ UIViewGroup *InitView();
+
+private:
+ UIViewGroup *group_{nullptr};
+ UILabel *titleLabel_{nullptr};
+ UIImageView *iconView_{nullptr};
+ UILabel *noteLabel_{nullptr};
+ UILabelButton *exitButton_{nullptr};
+ UILabelButton *retryButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_ENTRY_VIEW_H
+#define ECG_ENTRY_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_view.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+#include "ecg/include/ECGViewClickIds.h"
+
+class ECGEntryView {
+public:
+ ECGEntryView();
+ ~ECGEntryView();
+ static ECGEntryView *GetInstance();
+ UIViewGroup *InitView();
+ void InitPageIndicator();
+ void InitEntryView();
+
+private:
+ void ClearContent();
+
+
+ UIViewGroup *group_{nullptr};
+ UILabel *titleLabel_{nullptr};
+ UILabel *noteLabel_{nullptr};
+ UILabelButton *measureButton_{nullptr};
+ UIView *dotEntry_{nullptr};
+ UIView *dotHistory_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_HISTORY_VIEW_H
+#define ECG_HISTORY_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_scroll_view.h"
+#include "components/ui_view.h"
+#include "components/ui_view_group.h"
+#include <vector>
+
+namespace OHOS {
+
+#include "ecg/include/ECGViewClickIds.h"
+
+#define ECG_PATH_HEART_RED_SMALL_ICON "/user/res/ecg/heart_red_s.bin"
+#define ECG_PATH_HEART_RED_ICON "/user/res/ecg/heart_red.bin"
+#define ECG_PATH_HEART_GREEN_SMALL_ICON "/user/res/ecg/heart_green_s.bin"
+#define ECG_NO_RECORD_IMAGE "/user/res/Training/modules/Norecord.bin"
+
+class ECGHistoryView {
+public:
+ ECGHistoryView();
+ ~ECGHistoryView();
+ static ECGHistoryView *GetInstance();
+ UIViewGroup *InitView();
+ void RefreshView();
+ void ShowRecentPage();
+ void ShowListPage();
+ bool IsShowingListPage() const;
+
+private:
+ void ClearListItems();
+ void RebuildList();
+ void UpdateRecentPage();
+ void InitRecentPage();
+ void InitListPage();
+ void InitEmptyPage();
+ void InitPageIndicator();
+ void UpdatePageVisibility();
+
+ UIViewGroup *group_{nullptr};
+ UIViewGroup *recentPage_{nullptr};
+ UIViewGroup *listPage_{nullptr};
+ UIViewGroup *emptyPage_{nullptr};
+
+ UILabel *recentTitle_{nullptr};
+ UILabel *recentTime_{nullptr};
+ UILabel *recentResult_{nullptr};
+ UIImageView *recentHeart_{nullptr};
+ UILabel *recentBpm_{nullptr};
+ UILabel *recentBpmUnit_{nullptr};
+ UILabelButton *viewAllButton_{nullptr};
+
+ UILabel *listTitle_{nullptr};
+ UIScrollView *scrollView_{nullptr};
+ UIViewGroup *listContainer_{nullptr};
+
+ UILabel *emptyTitleLabel_{nullptr};
+ UIImageView *emptyImage_{nullptr};
+ UILabel *emptyHintLabel_{nullptr};
+ UILabelButton *startMeasureButton_{nullptr};
+ UIView *dotEntry_{nullptr};
+ UIView *dotHistory_{nullptr};
+ std::vector<UIView *> listItems_;
+ bool showingListPage_{false};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_INSTRUCTION_VIEW_H
+#define ECG_INSTRUCTION_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+#include "ecg/include/ECGViewClickIds.h"
+
+#define ECG_PATH_WAIT_CONTACT "/user/res/ecg/wait_contact.bin"
+#define ECG_PATH_CONTACT_GOOD "/user/res/ecg/contact_good.bin"
+#define ECG_PATH_DISCLAIMER "/user/res/ecg/disclaimer.bin"
+
+typedef enum : uint8_t {
+ ECG_INSTRUCTION_TYPE_DISCLAIMER = 0,
+ ECG_INSTRUCTION_TYPE_WAIT_CONTACT,
+ ECG_INSTRUCTION_TYPE_CONTACT_GOOD,
+ MAX_ECG_INSTRUCTION_TYPE,
+} ECGInstructionType;
+
+class ECGInstructionView {
+public:
+ ECGInstructionView();
+ ~ECGInstructionView();
+ static ECGInstructionView *GetInstance();
+
+ UIViewGroup *InitView(ECGInstructionType type);
+ void UpdateForType(ECGInstructionType type);
+ ECGInstructionType GetCurrentType() const { return currentType_; }
+
+private:
+ void ClearContent();
+ void InitDisclaimer();
+ void InitWaitContact();
+ void InitContactGood();
+
+ UIViewGroup *group_{nullptr};
+ UILabel *titleLabel_{nullptr};
+ UILabel *contentLabel_{nullptr};
+ UILabel *noteLabel_{nullptr};
+ UIImageView *imageView_{nullptr};
+ UILabelButton *agreeButton_{nullptr};
+ ECGInstructionType currentType_{ECG_INSTRUCTION_TYPE_DISCLAIMER};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_MAIN_PRESENTER_H
+#define ECG_MAIN_PRESENTER_H
+
+#include "Presenter.h"
+#include "ecg/include/ECGMainView.h"
+#include "components/ui_swipe_view.h"
+#include "common/key_code.h"
+#include "key/include/KeyInputListener.h"
+
+namespace OHOS {
+
+class ECGMainPresenter : public Presenter<ECGMainView>,
+ public UIView::OnClickListener,
+ public UIView::OnDragListener,
+ public UISwipeView::OnSwipeListener,
+ public RootView::OnKeyActListener {
+public:
+ ECGMainPresenter();
+ virtual ~ECGMainPresenter();
+ static ECGMainPresenter *GetInstance();
+
+ void OnStart() override;
+ void OnPause() override;
+ void OnResume() override;
+ void OnStop() override;
+
+ bool OnDragStart(UIView &view, const DragEvent &event) override;
+ bool OnDrag(UIView &view, const DragEvent &event) override;
+ bool OnDragEnd(UIView &view, const DragEvent &event) override;
+ void OnSwipe(UISwipeView &view) override;
+ bool OnClick(UIView &view, const ClickEvent &event) override;
+ bool OnKeyAct(UIView &view, const KeyEvent &event) override;
+
+ void StartMeasureTimer();
+ void StopMeasureTimer();
+ void StopContactGoodTimer();
+ void FinishMeasuringFlow(ECGMainView *mainView);
+ void OnBottomButtonPressed(ECGMainView *mainView);
+
+private:
+ void NavigateBack();
+ bool HandleSwipeBack();
+ bool IsFlowSubPage(int16_t viewId) const;
+ void StartMeasureFlow(ECGMainView *mainView);
+ void ShowWaitContact(ECGMainView *mainView);
+ void ShowContactGood(ECGMainView *mainView);
+ bool IsWaitingContact(ECGMainView *mainView) const;
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_MAIN_VIEW_H
+#define ECG_MAIN_VIEW_H
+
+#include "View.h"
+#include "ecg/include/ECGInstructionView.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+typedef enum : uint8_t {
+ ECG_ENTRY_VIEW = 0,
+ ECG_HISTORY_VIEW,
+ ECG_INSTRUCTION_VIEW,
+ ECG_ABNORMAL_VIEW,
+ ECG_MEASURING_VIEW,
+ ECG_MEASURE_COMPLETE_VIEW,
+ ECG_MEASURE_RESULT_VIEW,
+ MAX_ECGVIEW,
+} ECGVIEWID;
+
+class ECGMainPresenter;
+class ECGMainView : public View<ECGMainPresenter> {
+public:
+ ECGMainView();
+ virtual ~ECGMainView();
+ static ECGMainView *GetInstance();
+ void OnStart() override;
+ void OnStop() override;
+
+ bool ChangeECGEntryView(int16_t thisView, int16_t type);
+ bool ChangeECGHistoryView(int16_t thisView, int16_t type);
+ bool ChangeECGInstructionView(int16_t thisView, int16_t type);
+ bool ChangeECGAbnormalView(int16_t thisView, int16_t type);
+ bool ChangeECGMeasuringView(int16_t thisView, int16_t type);
+ bool ChangeECGMeasureCompleteView(int16_t thisView, int16_t type);
+ bool ChangeECGMeasureResultView(int16_t thisView, int16_t type);
+
+ bool ClickFunc(int16_t interface, int16_t type);
+ void ChangeView(int16_t hideView, int16_t showView, int16_t type = 0);
+ int16_t GetCurrentViewId() const;
+ int16_t GetInstructionSubType() const { return instructionSubType_; }
+ void RefreshCurrentView();
+
+private:
+ void DeInit();
+
+ class ECGEntryView *entryView_{nullptr};
+ class ECGHistoryView *historyView_{nullptr};
+ class ECGInstructionView *instructionView_{nullptr};
+ class ECGAbnormalView *abnormalView_{nullptr};
+ class ECGMeasuringView *measuringView_{nullptr};
+ class ECGMeasureCompleteView *measureCompleteView_{nullptr};
+ class ECGMeasureResultView *measureResultView_{nullptr};
+ UIViewGroup *container_{nullptr};
+ UIViewGroup *group_[MAX_ECGVIEW]{nullptr};
+ int16_t currentViewId_{ECG_ENTRY_VIEW};
+ int16_t instructionSubType_{ECG_INSTRUCTION_TYPE_DISCLAIMER};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_MEASURE_COMPLETE_VIEW_H
+#define ECG_MEASURE_COMPLETE_VIEW_H
+
+#include "components/ui_image_animator.h"
+#include "components/ui_label.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class ECGMeasureCompleteView {
+public:
+ ECGMeasureCompleteView();
+ ~ECGMeasureCompleteView();
+ static ECGMeasureCompleteView *GetInstance();
+ UIViewGroup *InitView();
+ void StartAnimation();
+ void StopAnimation();
+
+private:
+ bool InitAnimator();
+ void UnloadAnimatorFrames();
+
+ UIViewGroup *group_{nullptr};
+ UILabel *noteLabel_{nullptr};
+ UIImageAnimatorView *animatorView_{nullptr};
+ ImageAnimatorInfo frameInfo_[40]{};
+ bool animatorInited_{false};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_MEASURE_RESULT_VIEW_H
+#define ECG_MEASURE_RESULT_VIEW_H
+
+#include "components/ui_image_view.h"
+#include "components/ui_label.h"
+#include "components/ui_label_button.h"
+#include "components/ui_view_group.h"
+
+#define ECG_PATH_HEART_ICON "/user/res/heart_chart/hearticon.bin"
+#define ECG_PATH_RESULT_BACKGROUND "/user/res/ecg/result_background.bin"
+namespace OHOS {
+
+class ECGMeasureResultView {
+public:
+ ECGMeasureResultView();
+ ~ECGMeasureResultView();
+ static ECGMeasureResultView *GetInstance();
+ UIViewGroup *InitView();
+ void UpdateText();
+
+private:
+ UIViewGroup *group_{nullptr};
+ UIImageView *heartIcon_{nullptr};
+ UIImageView *backgroundIcon_{nullptr};
+ UILabel *bpmValueLabel_{nullptr};
+ UILabel *bpmUnitLabel_{nullptr};
+ UILabel *resultLabel_{nullptr};
+ UILabelButton *confirmButton_{nullptr};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_MEASURING_VIEW_H
+#define ECG_MEASURING_VIEW_H
+
+#include "components/ui_chart.h"
+#include "components/ui_label.h"
+#include "components/ui_view_group.h"
+
+namespace OHOS {
+
+class ECGMeasuringView {
+public:
+ ECGMeasuringView();
+ ~ECGMeasuringView();
+ static ECGMeasuringView *GetInstance();
+ UIViewGroup *InitView();
+ void ResetMeasurement();
+ /** @return true when countdown finished */
+ bool OnTimerTick();
+ void StopMeasurement();
+
+private:
+ void InitChart();
+ void UpdateWaveform();
+ void UpdateCountdownLabel();
+
+ UIViewGroup *group_{nullptr};
+ UILabel *titleLabel_{nullptr};
+ UIChartPolyline *chart_{nullptr};
+ UIChartDataSerial *dataSerial_{nullptr};
+ UILabel *countdownLabel_{nullptr};
+ UILabel *hintLabel_{nullptr};
+ Point wavePoints_[48]{};
+ float wavePhase_{0.0f};
+ int16_t countdownSec_{30};
+ static constexpr int16_t kWavePointCount = 48;
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_MODEL_H
+#define ECG_MODEL_H
+
+#include "gfx_utils/color.h"
+#include <cstdint>
+#include <deque>
+#include <string>
+
+namespace OHOS {
+
+enum class ECGResultType : uint8_t {
+ SINUS_RHYTHM = 0,
+ ATRIAL_FIBRILLATION = 1,
+ INCONCLUSIVE = 2,
+};
+
+struct ECGRecord {
+ uint32_t timestamp;
+ uint8_t bpm;
+ ECGResultType result;
+};
+
+class ECGModel {
+public:
+ ECGModel();
+ ~ECGModel();
+ static ECGModel *GetInstance();
+
+ void LoadData();
+ ECGRecord GetLastRecord() const;
+ bool HasHistory() const;
+ const std::deque<ECGRecord> &GetHistoryRecords() const;
+ std::string FormatShortTimestamp(uint32_t timestamp) const;
+ std::string GetResultText(ECGResultType type) const;
+ ColorType GetResultColor(ECGResultType type) const;
+
+ void SetMeasureResult(uint8_t bpm, ECGResultType result);
+ uint8_t GetResultBpm() const;
+ ECGResultType GetResultType() const;
+ void AddMeasureToHistory();
+
+private:
+ ECGRecord lastRecord_{0, 0, ECGResultType::INCONCLUSIVE};
+ std::deque<ECGRecord> historyRecords_;
+ uint8_t resultBpm_{0};
+ ECGResultType resultType_{ECGResultType::SINUS_RHYTHM};
+};
+
+} // namespace OHOS
+
+#endif
--- /dev/null
+#ifndef ECG_VIEW_CLICK_IDS_H
+#define ECG_VIEW_CLICK_IDS_H
+
+#define ECG_CLICK_HISTORY "ecg_click_history"
+#define ECG_CLICK_HISTORY_BACK "ecg_click_history_back"
+#define ECG_CLICK_VIEW_ALL "ecg_click_view_all"
+#define ECG_CLICK_INSTRUCTION "ecg_click_instruction"
+#define ECG_CLICK_INSTRUCTION_BACK "ecg_click_instruction_back"
+#define ECG_CLICK_MEASURE "ecg_click_measure"
+#define ECG_CLICK_STOP "ecg_click_stop"
+#define ECG_CLICK_RETRY "ecg_click_retry"
+#define ECG_CLICK_EXIT "ecg_click_exit"
+#define ECG_CLICK_CONFIRM "ecg_click_confirm"
+
+#endif
--- /dev/null
+#include "ecg/include/ECGAbnormalView.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "uicomponents/UIComponents.h"
+
+namespace OHOS {
+
+static ECGAbnormalView *g_instance = nullptr;
+
+ECGAbnormalView::ECGAbnormalView() { g_instance = this; }
+
+ECGAbnormalView::~ECGAbnormalView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete iconView_;
+ delete noteLabel_;
+ delete exitButton_;
+ delete retryButton_;
+ delete group_;
+ titleLabel_ = nullptr;
+ iconView_ = nullptr;
+ noteLabel_ = nullptr;
+ exitButton_ = nullptr;
+ retryButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+ECGAbnormalView *ECGAbnormalView::GetInstance() { return g_instance; }
+
+UIViewGroup *ECGAbnormalView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ InitUILabel(titleLabel_, 83, 50, 300, 35, "ecg_abnormal_title", "提示",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ InitImageView(iconView_, 193, 120, 80, 80, "ecg_abnormal_icon", ECG_PATH_ABNORMAL);
+ InitUILabel(noteLabel_, 53, 210, 360, 90, "ecg_abnormal_note",
+ "接触异常,请确保手指\n触摸电极后重试", DEFAULT_VECTOR_FONT_FILENAME, 26);
+ noteLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ noteLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabelButton(exitButton_, 58, 320, 170, 90, ECG_CLICK_EXIT, "退出", 26);
+ InitLabelButton(retryButton_, 238, 320, 170, 90, ECG_CLICK_RETRY, "重试", 26);
+ exitButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff191919);
+ retryButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffff5a36);
+
+ auto *listener = static_cast<UIView::OnClickListener *>(ECGMainPresenter::GetInstance());
+ exitButton_->SetOnClickListener(listener);
+ retryButton_->SetOnClickListener(listener);
+ group_->SetOnDragListener(static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance()));
+
+ group_->Add(titleLabel_);
+ group_->Add(iconView_);
+ group_->Add(noteLabel_);
+ group_->Add(exitButton_);
+ group_->Add(retryButton_);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGEntryView.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "ecg/include/ECGModel.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+static ECGEntryView *g_pECGEntryView = nullptr;
+
+ECGEntryView::ECGEntryView() { g_pECGEntryView = this; }
+
+ECGEntryView::~ECGEntryView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete noteLabel_;
+ delete measureButton_;
+ delete dotEntry_;
+ delete dotHistory_;
+ delete group_;
+ titleLabel_ = nullptr;
+ noteLabel_ = nullptr;
+ measureButton_ = nullptr;
+ dotEntry_ = nullptr;
+ dotHistory_ = nullptr;
+ group_ = nullptr;
+ }
+ g_pECGEntryView = nullptr;
+}
+
+ECGEntryView *ECGEntryView::GetInstance() { return g_pECGEntryView; }
+
+void ECGEntryView::ClearContent() {
+ if (group_ == nullptr) {
+ return;
+ }
+ if (titleLabel_ != nullptr) {
+ group_->Remove(titleLabel_);
+ delete titleLabel_;
+ titleLabel_ = nullptr;
+ }
+ if (noteLabel_ != nullptr) {
+ group_->Remove(noteLabel_);
+ delete noteLabel_;
+ noteLabel_ = nullptr;
+ }
+ if (measureButton_ != nullptr) {
+ group_->Remove(measureButton_);
+ delete measureButton_;
+ measureButton_ = nullptr;
+ }
+ if (dotEntry_ != nullptr) {
+ group_->Remove(dotEntry_);
+ delete dotEntry_;
+ dotEntry_ = nullptr;
+ }
+ if (dotHistory_ != nullptr) {
+ group_->Remove(dotHistory_);
+ delete dotHistory_;
+ dotHistory_ = nullptr;
+ }
+}
+
+//分页指示器
+void ECGEntryView::InitPageIndicator() {
+ if (dotEntry_ != nullptr || dotHistory_ != nullptr) {
+ return;
+ }
+ dotEntry_ = new UIView();
+ dotHistory_ = new UIView();
+ if (dotEntry_ == nullptr || dotHistory_ == nullptr) {
+ return;
+ }
+ dotEntry_->SetPosition(213, 430, 10, 10);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotEntry_->SetStyle(STYLE_BORDER_RADIUS, 5);
+ dotHistory_->SetPosition(243, 430, 10, 10);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff666666);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotHistory_->SetStyle(STYLE_BORDER_RADIUS, 5);
+ group_->Add(dotEntry_);
+ group_->Add(dotHistory_);
+}
+
+
+void ECGEntryView::InitEntryView() {
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "ecg_entry_title", "ECG测量",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(noteLabel_, 0, 216, 466, 35, "ecg_entry_note", "保持指尖清洁湿润",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ noteLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+
+ InitLabelButton(measureButton_, 93, 320, 280, 90, ECG_CLICK_MEASURE, "开始测量", 30);
+ measureButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff0176ff);
+ measureButton_->SetStyle(STYLE_BORDER_RADIUS, 100);
+ measureButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(ECGMainPresenter::GetInstance()));
+
+ group_->Add(titleLabel_);
+ group_->Add(noteLabel_);
+ group_->Add(measureButton_);
+}
+
+UIViewGroup *ECGEntryView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ InitPageIndicator();
+ InitEntryView();
+ return group_;
+}
+
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGHistoryView.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "ecg/include/ECGModel.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+namespace {
+static constexpr int16_t ECG_HISTORY_CARD_H = 160;
+static constexpr int16_t ECG_HISTORY_CARD_MARGIN = 40;
+static constexpr int16_t ECG_HISTORY_CARD_W = 380;
+static constexpr int16_t ECG_HISTORY_MAX_ITEMS = 10;
+static constexpr uint32_t ECG_CARD_BG_COLOR = 0xff1a1a1a;
+} // namespace
+
+static ECGHistoryView *g_pECGHistoryView = nullptr;
+
+ECGHistoryView::ECGHistoryView() { g_pECGHistoryView = this; }
+
+ECGHistoryView::~ECGHistoryView() {
+ ClearListItems();
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete recentPage_;
+ delete listPage_;
+ delete emptyPage_;
+ delete dotEntry_;
+ delete dotHistory_;
+ delete group_;
+ recentPage_ = nullptr;
+ listPage_ = nullptr;
+ emptyPage_ = nullptr;
+ dotEntry_ = nullptr;
+ dotHistory_ = nullptr;
+ group_ = nullptr;
+ }
+ recentTitle_ = nullptr;
+ recentTime_ = nullptr;
+ recentResult_ = nullptr;
+ recentHeart_ = nullptr;
+ recentBpm_ = nullptr;
+ recentBpmUnit_ = nullptr;
+ viewAllButton_ = nullptr;
+ listTitle_ = nullptr;
+ scrollView_ = nullptr;
+ listContainer_ = nullptr;
+ emptyTitleLabel_ = nullptr;
+ emptyImage_ = nullptr;
+ emptyHintLabel_ = nullptr;
+ startMeasureButton_ = nullptr;
+ g_pECGHistoryView = nullptr;
+}
+
+ECGHistoryView *ECGHistoryView::GetInstance() { return g_pECGHistoryView; }
+
+bool ECGHistoryView::IsShowingListPage() const { return showingListPage_; }
+
+//显示最近记录页面
+void ECGHistoryView::ShowRecentPage() {
+ showingListPage_ = false;
+ UpdatePageVisibility();
+ if (group_ != nullptr) {
+ group_->Invalidate();
+ }
+}
+
+//显示测量记录页面
+void ECGHistoryView::ShowListPage() {
+ showingListPage_ = true;
+ RebuildList();
+ UpdatePageVisibility();
+ if (group_ != nullptr) {
+ group_->Invalidate();
+ }
+}
+
+void ECGHistoryView::UpdatePageVisibility() {
+ bool hasRecords = ECGModel::GetInstance()->HasHistory();
+ if (emptyPage_ != nullptr) {
+ emptyPage_->SetVisible(!hasRecords);
+ }
+ if (recentPage_ != nullptr) {
+ recentPage_->SetVisible(hasRecords && !showingListPage_);
+ }
+ if (listPage_ != nullptr) {
+ listPage_->SetVisible(hasRecords && showingListPage_);
+ }
+ if (dotEntry_ != nullptr && dotHistory_ != nullptr) {
+ bool showDots = hasRecords && !showingListPage_;
+ dotEntry_->SetVisible(showDots);
+ dotHistory_->SetVisible(showDots);
+ }
+}
+
+void ECGHistoryView::ClearListItems() {
+ for (auto *item : listItems_) {
+ if (listContainer_ != nullptr) {
+ listContainer_->Remove(item);
+ }
+ delete item;
+ }
+ listItems_.clear();
+}
+
+void ECGHistoryView::InitPageIndicator() {
+ dotEntry_ = new UIView();
+ dotHistory_ = new UIView();
+ if (dotEntry_ == nullptr || dotHistory_ == nullptr) {
+ return;
+ }
+ dotEntry_->SetPosition(213, 430, 10, 10);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff666666);
+ dotEntry_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotEntry_->SetStyle(STYLE_BORDER_RADIUS, 5);
+ dotHistory_->SetPosition(243, 430, 10, 10);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);
+ dotHistory_->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dotHistory_->SetStyle(STYLE_BORDER_RADIUS, 5);
+}
+
+void ECGHistoryView::InitEmptyPage() {
+ emptyPage_ = new UIViewGroup();
+ if (emptyPage_ == nullptr) {
+ return;
+ }
+ emptyPage_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ emptyPage_->SetDraggable(true);
+ emptyPage_->SetTouchable(true);
+ emptyPage_->SetOnDragListener(static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance()));
+
+ InitUILabel(emptyTitleLabel_, 0, 50, 466, 35, "ecg_history_empty_title", "暂无测量记录",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ emptyTitleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ InitImageView(emptyImage_, 193, 120, 80, 80, "ecg_history_empty_image", ECG_NO_RECORD_IMAGE);
+ InitUILabel(emptyHintLabel_, 53, 260, 360, 70, "ecg_history_empty_hint",
+ "完成一次测量后\n可在这里查看记录", DEFAULT_VECTOR_FONT_FILENAME, 28);
+ emptyHintLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ emptyHintLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ InitLabelButton(startMeasureButton_, 93, 320, 280, 90, ECG_CLICK_MEASURE, "开始测量", 30);
+ startMeasureButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff0176ff);
+ startMeasureButton_->SetStyle(STYLE_BORDER_RADIUS, 100);
+ startMeasureButton_->SetOnClickListener(
+ static_cast<UIView::OnClickListener *>(ECGMainPresenter::GetInstance()));
+
+ emptyPage_->Add(emptyTitleLabel_);
+ emptyPage_->Add(emptyImage_);
+ emptyPage_->Add(emptyHintLabel_);
+ emptyPage_->Add(startMeasureButton_);
+}
+
+void ECGHistoryView::InitRecentPage() {
+ recentPage_ = new UIViewGroup();
+ if (recentPage_ == nullptr) {
+ return;
+ }
+ recentPage_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ recentPage_->SetDraggable(true);
+ recentPage_->SetTouchable(true);
+ recentPage_->SetOnDragListener(static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance()));
+
+ InitUILabel(recentTitle_, 0, 50, 466, 35, "ecg_recent_title", "最近记录",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ recentTitle_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(recentTime_, 0, 93, 466, 33, "ecg_recent_time", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 28);
+ recentTime_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ recentTime_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(recentResult_, 0, 182, 466, 35, "ecg_recent_result", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ recentResult_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitImageView(recentHeart_, 129, 182+35+12+8, 54, 54, "ecg_recent_heart", ECG_PATH_HEART_RED_ICON);
+
+ InitUILabel(recentBpm_, 129+54+12, 182+35+12, 66, 66, "ecg_recent_bpm", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 65);
+ recentBpm_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(recentBpmUnit_, 129+54+12+66+8, 258, 72, 30, "ecg_recent_bpm_unit", "次/分",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ recentBpmUnit_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ recentBpmUnit_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabelButton(viewAllButton_, 93, 320, 280, 90, ECG_CLICK_VIEW_ALL, "查看全部", 28);
+ viewAllButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff191919);
+ viewAllButton_->SetStyle(STYLE_BORDER_RADIUS, 100);
+ viewAllButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(ECGMainPresenter::GetInstance()));
+
+ recentPage_->Add(recentTitle_);
+ recentPage_->Add(recentTime_);
+ recentPage_->Add(recentResult_);
+ recentPage_->Add(recentHeart_);
+ recentPage_->Add(recentBpm_);
+ recentPage_->Add(recentBpmUnit_);
+ recentPage_->Add(viewAllButton_);
+}
+
+void ECGHistoryView::InitListPage() {
+ listPage_ = new UIViewGroup();
+ if (listPage_ == nullptr) {
+ return;
+ }
+ listPage_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ listPage_->SetDraggable(true);
+ listPage_->SetTouchable(true);
+ listPage_->SetOnDragListener(static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance()));
+
+ InitUILabel(listTitle_, 0, 50, 466, 35, "ecg_list_title", "测量记录",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ listTitle_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ scrollView_ = new UIScrollView();
+ listContainer_ = new UIViewGroup();
+ if (scrollView_ != nullptr && listContainer_ != nullptr) {
+ scrollView_->SetPosition(0, 100, Screen::GetInstance().GetWidth(), 330);
+ scrollView_->SetHorizontalScrollState(false);
+ scrollView_->SetThrowDrag(true);
+ listContainer_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), 330);
+ scrollView_->Add(listContainer_);
+ listPage_->Add(scrollView_);
+ }
+ listPage_->Add(listTitle_);
+}
+
+void ECGHistoryView::UpdateRecentPage() {
+ if (recentTitle_ == nullptr || recentTime_ == nullptr || recentResult_ == nullptr ||
+ recentBpm_ == nullptr || recentHeart_ == nullptr) {
+ return;
+ }
+ ECGModel *model = ECGModel::GetInstance();
+ if (!model->HasHistory()) {
+ return;
+ }
+ ECGRecord last = model->GetLastRecord();
+ recentTime_->SetText(model->FormatShortTimestamp(last.timestamp).c_str());
+ recentResult_->SetText(model->GetResultText(last.result).c_str());
+ recentResult_->SetTextColor(model->GetResultColor(last.result));
+ if (last.bpm > 0) {
+ recentBpm_->SetText(std::to_string(last.bpm).c_str());
+ } else {
+ recentBpm_->SetText("--");
+ }
+}
+
+void ECGHistoryView::RebuildList() {
+ if (listContainer_ == nullptr) {
+ return;
+ }
+ ClearListItems();
+ ECGModel *model = ECGModel::GetInstance();
+ const auto &records = model->GetHistoryRecords();
+ size_t count = records.size();
+ if (count > ECG_HISTORY_MAX_ITEMS) {
+ count = ECG_HISTORY_MAX_ITEMS;
+ }
+ int16_t y = 0;
+ for (size_t i = 0; i < count; i++) {
+ const ECGRecord &rec = records[i];
+
+ UIView *card = new UIView();
+ if (card == nullptr) {
+ continue;
+ }
+ card->SetPosition(ECG_HISTORY_CARD_MARGIN, y, ECG_HISTORY_CARD_W, ECG_HISTORY_CARD_H);
+ card->SetStyle(STYLE_BACKGROUND_COLOR, ECG_CARD_BG_COLOR);
+ card->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ card->SetStyle(STYLE_BORDER_RADIUS, 30);
+ listContainer_->Add(card);
+ listItems_.push_back(card);
+
+ UIImageView *heartIcon = new UIImageView();
+ if (heartIcon != nullptr) {
+ heartIcon->SetPosition(ECG_HISTORY_CARD_MARGIN + 40, y + 36, 38, 38);
+ heartIcon->SetSrc(ECG_PATH_HEART_RED_SMALL_ICON);
+ listContainer_->Add(heartIcon);
+ listItems_.push_back(heartIcon);
+ }
+
+ UILabel *resultLabel = nullptr;
+ InitUILabel(resultLabel, ECG_HISTORY_CARD_MARGIN + 40 + 38 + 8 + 4, y + 36 + 5, 120, 35, "ecg_list_result", "",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ resultLabel->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+ resultLabel->SetText(model->GetResultText(rec.result).c_str());
+ resultLabel->SetTextColor(model->GetResultColor(rec.result));
+
+ UILabel *bpmLabel = nullptr;
+ std::string bpmStr = rec.bpm > 0 ? (std::to_string(rec.bpm) + " 次/分") : "-- 次/分";
+ InitUILabel(bpmLabel, ECG_HISTORY_CARD_MARGIN + 40 + 38 + 8 + 8 + 120, y + 36 + 5 + 4, 119, 35, "ecg_list_bpm",
+ bpmStr.c_str(), DEFAULT_VECTOR_FONT_FILENAME, 28);
+ bpmLabel->SetAlign(TEXT_ALIGNMENT_RIGHT, TEXT_ALIGNMENT_CENTER);
+
+ UILabel *timeLabel = nullptr;
+ InitUILabel(timeLabel, ECG_HISTORY_CARD_MARGIN + 40, y + 36 + 58, 172, 30, "ecg_list_time",
+ model->FormatShortTimestamp(rec.timestamp).c_str(), DEFAULT_VECTOR_FONT_FILENAME, 28);
+ timeLabel->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+
+ listContainer_->Add(resultLabel);
+ listContainer_->Add(bpmLabel);
+ listContainer_->Add(timeLabel);
+ listItems_.push_back(resultLabel);
+ listItems_.push_back(bpmLabel);
+ listItems_.push_back(timeLabel);
+ y += ECG_HISTORY_CARD_H + 20;
+ }
+ listContainer_->Resize(466, y > 0 ? y : 330);
+}
+
+void ECGHistoryView::RefreshView() {
+ showingListPage_ = false;
+ UpdateRecentPage();
+ RebuildList();
+ UpdatePageVisibility();
+ if (group_ != nullptr) {
+ group_->Invalidate();
+ }
+}
+
+UIViewGroup *ECGHistoryView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+
+ InitRecentPage();
+ InitListPage();
+ InitEmptyPage();
+ InitPageIndicator();
+
+ if (recentPage_ != nullptr) {
+ group_->Add(recentPage_);
+ }
+ if (listPage_ != nullptr) {
+ group_->Add(listPage_);
+ }
+ if (emptyPage_ != nullptr) {
+ group_->Add(emptyPage_);
+ }
+ if (dotEntry_ != nullptr) {
+ group_->Add(dotEntry_);
+ }
+ if (dotHistory_ != nullptr) {
+ group_->Add(dotHistory_);
+ }
+ RefreshView();
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGInstructionView.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "uicomponents/UIComponents.h"
+
+namespace OHOS {
+
+static ECGInstructionView *g_instance = nullptr;
+
+ECGInstructionView::ECGInstructionView() { g_instance = this; }
+
+ECGInstructionView::~ECGInstructionView() {
+ ClearContent();
+ if (group_ != nullptr) {
+ delete group_;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+ECGInstructionView *ECGInstructionView::GetInstance() { return g_instance; }
+
+void ECGInstructionView::ClearContent() {
+ if (group_ == nullptr) {
+ return;
+ }
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete contentLabel_;
+ delete noteLabel_;
+ delete imageView_;
+ delete agreeButton_;
+ titleLabel_ = nullptr;
+ contentLabel_ = nullptr;
+ noteLabel_ = nullptr;
+ imageView_ = nullptr;
+ agreeButton_ = nullptr;
+}
+
+void ECGInstructionView::InitDisclaimer() {
+ InitImageView(imageView_, 193, 50, 80, 80, "ecg_disclaimer", ECG_PATH_DISCLAIMER);
+ InitUILabel(contentLabel_, 50, 147, 366, 70, "ecg_disclaimer_content",
+ "ECG 测量结果仅供健康参考\n不可替代专业医疗诊断", DEFAULT_VECTOR_FONT_FILENAME, 30);
+ contentLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ contentLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ InitUILabel(noteLabel_, 0, 248, 466, 35, "ecg_disclaimer_note", "如有心脏相关疑虑,请咨询医生",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ noteLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ InitLabelButton(agreeButton_, 93, 320, 280, 90, ECG_CLICK_MEASURE, "同意并继续", 26);
+ agreeButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff0176ff);
+ agreeButton_->SetStyle(STYLE_BORDER_RADIUS, 100);
+
+ auto *clickListener = static_cast<UIView::OnClickListener *>(ECGMainPresenter::GetInstance());
+ auto *dragListener = static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance());
+ agreeButton_->SetOnClickListener(clickListener);
+ group_->SetOnDragListener(dragListener);
+
+ group_->Add(imageView_);
+ group_->Add(contentLabel_);
+ group_->Add(noteLabel_);
+ group_->Add(agreeButton_);
+}
+
+void ECGInstructionView::InitWaitContact() {
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "ecg_wait_contact_title", "ECG测量",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ InitImageView(imageView_, 141, 36, 184, 184, "ecg_wait_contact", ECG_PATH_WAIT_CONTACT);
+ InitUILabel(contentLabel_, 0, 401, 466, 30, "ecg_wait_contact_content", "将手指放在电极上",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ contentLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ auto *dragListener = static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance());
+ group_->SetOnDragListener(dragListener);
+
+ group_->Add(titleLabel_);
+ group_->Add(imageView_);
+ group_->Add(contentLabel_);
+}
+
+void ECGInstructionView::InitContactGood() {
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "ecg_contact_good_title", "ECG测量",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ InitImageView(imageView_, 141, 36, 184, 184, "ecg_contact_good", ECG_PATH_CONTACT_GOOD);
+ InitUILabel(contentLabel_, 0, 401, 466, 30, "ecg_contact_good_content", "保持手指不动",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ contentLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ auto *dragListener = static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance());
+ group_->SetOnDragListener(dragListener);
+
+ group_->Add(titleLabel_);
+ group_->Add(imageView_);
+ group_->Add(contentLabel_);
+}
+
+void ECGInstructionView::UpdateForType(ECGInstructionType type) {
+ if (group_ == nullptr) {
+ return;
+ }
+ ClearContent();
+ currentType_ = type;
+ switch (type) {
+ case ECG_INSTRUCTION_TYPE_DISCLAIMER:
+ InitDisclaimer();
+ break;
+ case ECG_INSTRUCTION_TYPE_WAIT_CONTACT:
+ InitWaitContact();
+ break;
+ case ECG_INSTRUCTION_TYPE_CONTACT_GOOD:
+ InitContactGood();
+ break;
+ default:
+ break;
+ }
+ group_->Invalidate();
+}
+
+UIViewGroup *ECGInstructionView::InitView(ECGInstructionType type) {
+ if (group_ == nullptr) {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ }
+ UpdateForType(type);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGMainPresenter.h"
+#include "ecg/include/ECGEntryView.h"
+#include "ecg/include/ECGHistoryView.h"
+#include "ecg/include/ECGInstructionView.h"
+#include "ecg/include/ECGMeasuringView.h"
+#include "ecg/include/ECGMeasureCompleteView.h"
+#include "ecg/include/ECGModel.h"
+#include "ecg/include/ECGViewClickIds.h"
+#include "NativeRegisterManager.h"
+#include "NativeAbility.h"
+#include "UiConfig.h"
+#include "power_display_service.h"
+#include "wearable_log.h"
+#include <cmsis_os2.h>
+#include <custom_utils.h>
+
+namespace OHOS {
+
+static ECGMainPresenter *g_pECGMainPresenter = nullptr;
+static osTimerId_t g_ecgMeasureTimer = nullptr;
+static osTimerId_t g_ecgContactGoodTimer = nullptr;
+
+REGIST_MENU_PRO(VIEW_ECG, ECGMainView, ECGMainPresenter, PNG_APPLIST_ECG_IMAGE_BIG,
+ PNG_APPLIST_ECG_IMAGE, PNG_APPLIST_ECG_IMAGE, "心电", STR_ECG);
+
+static void ECGMeasureTimerCallback(void *arg) {
+ UNUSED(arg);
+ ECGMeasuringView *view = ECGMeasuringView::GetInstance();
+ if (view == nullptr) {
+ return;
+ }
+ if (view->OnTimerTick()) {
+ ECGMainPresenter *presenter = ECGMainPresenter::GetInstance();
+ ECGMainView *mainView = ECGMainView::GetInstance();
+ if (presenter != nullptr && mainView != nullptr) {
+ presenter->FinishMeasuringFlow(mainView);
+ }
+ }
+}
+
+static void ECGCompleteTimerCallback(void *arg) {
+ UNUSED(arg);
+ ECGMainView *mainView = ECGMainView::GetInstance();
+ if (mainView != nullptr && mainView->GetCurrentViewId() == ECG_MEASURE_COMPLETE_VIEW) {
+ mainView->ChangeView(ECG_MEASURE_COMPLETE_VIEW, ECG_MEASURE_RESULT_VIEW);
+ }
+}
+
+static void ECGContactGoodTimerCallback(void *arg) {
+ UNUSED(arg);
+ ECGMainPresenter *presenter = ECGMainPresenter::GetInstance();
+ ECGMainView *mainView = ECGMainView::GetInstance();
+ if (presenter == nullptr || mainView == nullptr) {
+ return;
+ }
+ if (mainView->GetCurrentViewId() == ECG_INSTRUCTION_VIEW &&
+ mainView->GetInstructionSubType() == ECG_INSTRUCTION_TYPE_CONTACT_GOOD) {
+ mainView->ChangeView(ECG_INSTRUCTION_VIEW, ECG_MEASURING_VIEW);
+ }
+ presenter->StopContactGoodTimer();
+}
+
+ECGMainPresenter::ECGMainPresenter() { g_pECGMainPresenter = this; }
+
+ECGMainPresenter::~ECGMainPresenter() {
+ StopMeasureTimer();
+ StopContactGoodTimer();
+ g_pECGMainPresenter = nullptr;
+}
+
+ECGMainPresenter *ECGMainPresenter::GetInstance() { return g_pECGMainPresenter; }
+
+void ECGMainPresenter::OnStart() { Presenter<ECGMainView>::OnStart(); }
+
+void ECGMainPresenter::OnPause(void) {}
+
+void ECGMainPresenter::OnResume(void) {
+ RootView::GetInstance()->SetOnKeyActListener(this);
+}
+
+void ECGMainPresenter::OnStop(void) {
+ StopMeasureTimer();
+ StopContactGoodTimer();
+}
+
+void ECGMainPresenter::StopContactGoodTimer() {
+ if (g_ecgContactGoodTimer != nullptr) {
+ osTimerStop(g_ecgContactGoodTimer);
+ osTimerDelete(g_ecgContactGoodTimer);
+ g_ecgContactGoodTimer = nullptr;
+ }
+}
+
+bool ECGMainPresenter::IsWaitingContact(ECGMainView *mainView) const {
+ return mainView != nullptr && mainView->GetCurrentViewId() == ECG_INSTRUCTION_VIEW &&
+ mainView->GetInstructionSubType() == ECG_INSTRUCTION_TYPE_WAIT_CONTACT;
+}
+
+void ECGMainPresenter::ShowWaitContact(ECGMainView *mainView) {
+ int16_t hide = mainView->GetCurrentViewId();
+ if (hide == ECG_INSTRUCTION_VIEW) {
+ mainView->ChangeView(ECG_INSTRUCTION_VIEW, ECG_INSTRUCTION_VIEW, ECG_INSTRUCTION_TYPE_WAIT_CONTACT);
+ } else {
+ mainView->ChangeView(hide, ECG_INSTRUCTION_VIEW, ECG_INSTRUCTION_TYPE_WAIT_CONTACT);
+ }
+}
+
+void ECGMainPresenter::ShowContactGood(ECGMainView *mainView) {
+ StopContactGoodTimer();
+ mainView->ChangeView(ECG_INSTRUCTION_VIEW, ECG_INSTRUCTION_VIEW, ECG_INSTRUCTION_TYPE_CONTACT_GOOD);
+ g_ecgContactGoodTimer =
+ osTimerNew((osTimerFunc_t)ECGContactGoodTimerCallback, osTimerOnce, nullptr, nullptr);
+ if (g_ecgContactGoodTimer != nullptr) {
+ osTimerStart(g_ecgContactGoodTimer, 2000);
+ }
+}
+
+void ECGMainPresenter::OnBottomButtonPressed(ECGMainView *mainView) {
+ if (!IsWaitingContact(mainView)) {
+ return;
+ }
+ ShowContactGood(mainView);
+}
+
+void ECGMainPresenter::NavigateBack() {
+ NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);
+}
+
+bool ECGMainPresenter::IsFlowSubPage(int16_t viewId) const {
+ return viewId != ECG_ENTRY_VIEW && viewId != ECG_HISTORY_VIEW;
+}
+
+bool ECGMainPresenter::HandleSwipeBack() {
+ StopContactGoodTimer();
+ ECGMainView *mainView = ECGMainView::GetInstance();
+ if (mainView == nullptr) {
+ NavigateBack();
+ return true;
+ }
+ int16_t current = mainView->GetCurrentViewId();
+ if (current == ECG_ENTRY_VIEW) {
+ NavigateBack();
+ } else if (current == ECG_HISTORY_VIEW) {
+ ECGHistoryView *historyView = ECGHistoryView::GetInstance();
+ if (historyView != nullptr && historyView->IsShowingListPage()) {
+ historyView->ShowRecentPage();
+ } else {
+ mainView->ChangeView(ECG_HISTORY_VIEW, ECG_ENTRY_VIEW);
+ }
+ } else if (IsFlowSubPage(current)) {
+ mainView->ChangeView(current, ECG_ENTRY_VIEW);
+ } else {
+ mainView->ChangeView(current, ECG_ENTRY_VIEW);
+ }
+ return true;
+}
+
+void ECGMainPresenter::StartMeasureTimer() {
+ StopMeasureTimer();
+ g_ecgMeasureTimer =
+ osTimerNew((osTimerFunc_t)ECGMeasureTimerCallback, osTimerPeriodic, nullptr, nullptr);
+ if (g_ecgMeasureTimer != nullptr) {
+ osTimerStart(g_ecgMeasureTimer, 200);
+ }
+}
+
+void ECGMainPresenter::StopMeasureTimer() {
+ if (g_ecgMeasureTimer != nullptr) {
+ osTimerStop(g_ecgMeasureTimer);
+ osTimerDelete(g_ecgMeasureTimer);
+ g_ecgMeasureTimer = nullptr;
+ }
+}
+
+void ECGMainPresenter::StartMeasureFlow(ECGMainView *mainView) {
+ ShowWaitContact(mainView);
+}
+
+void ECGMainPresenter::FinishMeasuringFlow(ECGMainView *mainView) {
+ StopMeasureTimer();
+ ECGModel *model = ECGModel::GetInstance();
+ model->SetMeasureResult(76, ECGResultType::SINUS_RHYTHM);
+ model->AddMeasureToHistory();
+ mainView->ChangeView(ECG_MEASURING_VIEW, ECG_MEASURE_COMPLETE_VIEW);
+ osTimerId_t completeTimer =
+ osTimerNew((osTimerFunc_t)ECGCompleteTimerCallback, osTimerOnce, nullptr, nullptr);
+ if (completeTimer != nullptr) {
+ osTimerStart(completeTimer, 3000);
+ }
+}
+
+bool ECGMainPresenter::OnDragStart(UIView &view, const DragEvent &event) {
+ UNUSED(view);
+ UNUSED(event);
+ return false;
+}
+
+bool ECGMainPresenter::OnDrag(UIView &view, const DragEvent &event) {
+ UNUSED(view);
+ UNUSED(event);
+ return false;
+}
+
+bool ECGMainPresenter::OnDragEnd(UIView &view, const DragEvent &event) {
+ ECGMainView *mainView = ECGMainView::GetInstance();
+ if (mainView == nullptr) {
+ return false;
+ }
+ int16_t current = mainView->GetCurrentViewId();
+ uint8_t direction = event.GetDragDirection();
+
+ if (direction == DragEvent::DIRECTION_LEFT_TO_RIGHT) {
+ HandleSwipeBack();
+ return true;
+ }
+ if (current == ECG_ENTRY_VIEW && direction == DragEvent::DIRECTION_RIGHT_TO_LEFT) {
+ mainView->ChangeView(ECG_ENTRY_VIEW, ECG_HISTORY_VIEW);
+ return true;
+ }
+ return false;
+}
+
+void ECGMainPresenter::OnSwipe(UISwipeView &view) { UNUSED(view); }
+
+bool ECGMainPresenter::OnClick(UIView &view, const ClickEvent &event) {
+ UNUSED(event);
+ std::string viewId = view.GetViewId();
+ ECGMainView *mainView = ECGMainView::GetInstance();
+ if (mainView == nullptr) {
+ return true;
+ }
+ int16_t current = mainView->GetCurrentViewId();
+
+ if (viewId == ECG_CLICK_HISTORY) {
+ mainView->ChangeView(ECG_ENTRY_VIEW, ECG_HISTORY_VIEW);
+ return true;
+ }
+ if (viewId == ECG_CLICK_HISTORY_BACK) {
+ mainView->ChangeView(ECG_HISTORY_VIEW, ECG_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == ECG_CLICK_VIEW_ALL) {
+ ECGHistoryView *historyView = ECGHistoryView::GetInstance();
+ if (historyView != nullptr) {
+ historyView->ShowListPage();
+ }
+ return true;
+ }
+ if (viewId == ECG_CLICK_INSTRUCTION_BACK) {
+ mainView->ChangeView(ECG_INSTRUCTION_VIEW, ECG_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == ECG_CLICK_INSTRUCTION) {
+ mainView->ChangeView(ECG_ENTRY_VIEW, ECG_INSTRUCTION_VIEW, ECG_INSTRUCTION_TYPE_DISCLAIMER);
+ return true;
+ }
+ if (viewId == ECG_CLICK_EXIT) {
+ mainView->ChangeView(current, ECG_ENTRY_VIEW);
+ return true;
+ }
+ if (viewId == ECG_CLICK_RETRY) {
+ if (current == ECG_ABNORMAL_VIEW) {
+ ShowWaitContact(mainView);
+ }
+ return true;
+ }
+ if (viewId == ECG_CLICK_CONFIRM) {
+ if (current == ECG_MEASURE_RESULT_VIEW) {
+ mainView->ChangeView(ECG_MEASURE_RESULT_VIEW, ECG_ENTRY_VIEW);
+ }
+ return true;
+ }
+ if (viewId == ECG_CLICK_STOP) {
+ StopMeasureTimer();
+ StopContactGoodTimer();
+ if (current == ECG_MEASURING_VIEW) {
+ mainView->ChangeView(ECG_MEASURING_VIEW, ECG_ENTRY_VIEW);
+ } else {
+ mainView->ChangeView(current, ECG_ENTRY_VIEW);
+ }
+ return true;
+ }
+ if (viewId == ECG_CLICK_MEASURE) {
+ if (current == ECG_INSTRUCTION_VIEW &&
+ mainView->GetInstructionSubType() == ECG_INSTRUCTION_TYPE_DISCLAIMER) {
+ ShowWaitContact(mainView);
+ return true;
+ }
+ if (current == ECG_ENTRY_VIEW || current == ECG_HISTORY_VIEW) {
+ StartMeasureFlow(mainView);
+ return true;
+ }
+ }
+ return true;
+}
+
+bool ECGMainPresenter::OnKeyAct(UIView &view, const KeyEvent &event) {
+ UNUSED(view);
+ unsigned short keyID = event.GetKeyId();
+ unsigned short keyState = event.GetState();
+ ECGMainView *mainView = ECGMainView::GetInstance();
+
+ if (keyID == static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_FUNC) && keyState == 1 && mainView != nullptr) {
+ OnBottomButtonPressed(mainView);
+ return true;
+ }
+ if (keyID == static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_POWER) && keyState == 1 &&
+ uapi_get_screen_state() == 0) {
+ HandleSwipeBack();
+ }
+ return true;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGMainView.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "ecg/include/ECGModel.h"
+#include "ecg/include/ECGEntryView.h"
+#include "ecg/include/ECGHistoryView.h"
+#include "ecg/include/ECGInstructionView.h"
+#include "ecg/include/ECGAbnormalView.h"
+#include "ecg/include/ECGMeasuringView.h"
+#include "ecg/include/ECGMeasureCompleteView.h"
+#include "ecg/include/ECGMeasureResultView.h"
+#include "wearable_log.h"
+#include "common/screen.h"
+
+namespace OHOS {
+
+static ECGMainView *g_pECGMainView = nullptr;
+
+ECGMainView::ECGMainView() { g_pECGMainView = this; }
+
+ECGMainView::~ECGMainView() {
+ DeInit();
+ g_pECGMainView = nullptr;
+}
+
+ECGMainView *ECGMainView::GetInstance() { return g_pECGMainView; }
+
+void ECGMainView::DeInit() {
+ delete entryView_;
+ delete historyView_;
+ delete instructionView_;
+ delete abnormalView_;
+ delete measuringView_;
+ delete measureCompleteView_;
+ delete measureResultView_;
+ entryView_ = nullptr;
+ historyView_ = nullptr;
+ instructionView_ = nullptr;
+ abnormalView_ = nullptr;
+ measuringView_ = nullptr;
+ measureCompleteView_ = nullptr;
+ measureResultView_ = nullptr;
+}
+
+void ECGMainView::OnStart() {
+ View<ECGMainPresenter>::OnStart();
+ if (container_ == nullptr) {
+ container_ = new UIViewGroup();
+ if (container_ == nullptr) {
+ return;
+ }
+ container_->SetPosition(0, 0, Screen::GetInstance().GetWidth(),
+ Screen::GetInstance().GetHeight());
+ container_->SetDraggable(true);
+ container_->SetTouchable(true);
+ container_->SetOnDragListener(presenter_);
+ AddViewToRootContainer(container_);
+ }
+ ECGModel::GetInstance()->LoadData();
+ ChangeView(MAX_ECGVIEW, ECG_ENTRY_VIEW);
+ RootView::GetInstance()->SetOnKeyActListener(
+ static_cast<RootView::OnKeyActListener *>(ECGMainPresenter::GetInstance()));
+ Draw();
+}
+
+void ECGMainView::OnStop() {
+ View<ECGMainPresenter>::OnStop();
+ if (container_ != nullptr) {
+ container_->RemoveAll();
+ delete container_;
+ container_ = nullptr;
+ }
+}
+
+bool ECGMainView::ChangeECGEntryView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ entryView_ = new ECGEntryView();
+ if (entryView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = entryView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool ECGMainView::ChangeECGHistoryView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (historyView_ != nullptr) {
+ historyView_->RefreshView();
+ }
+ return true;
+ }
+ historyView_ = new ECGHistoryView();
+ if (historyView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = historyView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool ECGMainView::ChangeECGInstructionView(int16_t thisView, int16_t type) {
+ instructionSubType_ = type;
+ if (group_[thisView] != nullptr) {
+ if (instructionView_ != nullptr) {
+ instructionView_->UpdateForType(static_cast<ECGInstructionType>(type));
+ }
+ return true;
+ }
+ instructionView_ = new ECGInstructionView();
+ if (instructionView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = instructionView_->InitView(static_cast<ECGInstructionType>(type));
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool ECGMainView::ChangeECGAbnormalView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ abnormalView_ = new ECGAbnormalView();
+ if (abnormalView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = abnormalView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool ECGMainView::ChangeECGMeasuringView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (measuringView_ != nullptr) {
+ measuringView_->ResetMeasurement();
+ }
+ return true;
+ }
+ measuringView_ = new ECGMeasuringView();
+ if (measuringView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = measuringView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool ECGMainView::ChangeECGMeasureCompleteView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ return true;
+ }
+ measureCompleteView_ = new ECGMeasureCompleteView();
+ if (measureCompleteView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = measureCompleteView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+bool ECGMainView::ChangeECGMeasureResultView(int16_t thisView, int16_t type) {
+ UNUSED(type);
+ if (group_[thisView] != nullptr) {
+ if (measureResultView_ != nullptr) {
+ measureResultView_->UpdateText();
+ }
+ return true;
+ }
+ measureResultView_ = new ECGMeasureResultView();
+ if (measureResultView_ == nullptr) {
+ return false;
+ }
+ group_[thisView] = measureResultView_->InitView();
+ if (group_[thisView] == nullptr) {
+ return false;
+ }
+ container_->Add(group_[thisView]);
+ return true;
+}
+
+struct ECGViewMapper {
+ const int16_t viewId;
+ bool (ECGMainView::*func)(int16_t, int16_t);
+};
+
+static const ECGViewMapper ViewMapper[] = {
+ {ECG_ENTRY_VIEW, &ECGMainView::ChangeECGEntryView},
+ {ECG_HISTORY_VIEW, &ECGMainView::ChangeECGHistoryView},
+ {ECG_INSTRUCTION_VIEW, &ECGMainView::ChangeECGInstructionView},
+ {ECG_ABNORMAL_VIEW, &ECGMainView::ChangeECGAbnormalView},
+ {ECG_MEASURING_VIEW, &ECGMainView::ChangeECGMeasuringView},
+ {ECG_MEASURE_COMPLETE_VIEW, &ECGMainView::ChangeECGMeasureCompleteView},
+ {ECG_MEASURE_RESULT_VIEW, &ECGMainView::ChangeECGMeasureResultView},
+};
+
+bool ECGMainView::ClickFunc(int16_t interface, int16_t type) {
+ int funcSize = sizeof(ViewMapper) / sizeof(ViewMapper[0]);
+ for (int i = 0; i < funcSize; i++) {
+ if (interface == ViewMapper[i].viewId) {
+ return (this->*(ViewMapper[i].func))(interface, type);
+ }
+ }
+ return false;
+}
+
+void ECGMainView::ChangeView(int16_t hideView, int16_t showView, int16_t type) {
+ if (showView >= MAX_ECGVIEW) {
+ return;
+ }
+ if (hideView == showView && showView == ECG_INSTRUCTION_VIEW) {
+ instructionSubType_ = type;
+ if (instructionView_ != nullptr) {
+ instructionView_->UpdateForType(static_cast<ECGInstructionType>(type));
+ }
+ container_->Invalidate();
+ return;
+ }
+ if (hideView != MAX_ECGVIEW) {
+ if (hideView >= MAX_ECGVIEW || group_[hideView] == nullptr) {
+ return;
+ }
+ if (hideView == ECG_MEASURING_VIEW && measuringView_ != nullptr) {
+ measuringView_->StopMeasurement();
+ ECGMainPresenter::GetInstance()->StopMeasureTimer();
+ }
+ if (hideView == ECG_MEASURE_COMPLETE_VIEW && measureCompleteView_ != nullptr) {
+ measureCompleteView_->StopAnimation();
+ }
+ group_[hideView]->SetVisible(false);
+ }
+ if (!ClickFunc(showView, type)) {
+ return;
+ }
+ group_[showView]->SetVisible(true);
+ currentViewId_ = showView;
+ if (showView == ECG_MEASURING_VIEW && measuringView_ != nullptr) {
+ measuringView_->ResetMeasurement();
+ ECGMainPresenter::GetInstance()->StartMeasureTimer();
+ }
+ if (showView == ECG_MEASURE_COMPLETE_VIEW && measureCompleteView_ != nullptr) {
+ measureCompleteView_->StartAnimation();
+ }
+ if (showView == ECG_HISTORY_VIEW && historyView_ != nullptr) {
+ historyView_->RefreshView();
+ }
+ if (showView == ECG_ENTRY_VIEW && entryView_ != nullptr) {
+ entryView_->InitEntryView();
+ }
+ RefreshCurrentView();
+ container_->Invalidate();
+}
+
+void ECGMainView::RefreshCurrentView() {
+ if (currentViewId_ == ECG_MEASURE_RESULT_VIEW && measureResultView_ != nullptr) {
+ measureResultView_->UpdateText();
+ }
+}
+
+int16_t ECGMainView::GetCurrentViewId() const { return currentViewId_; }
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGMeasureCompleteView.h"
+#include "common/image_cache_manager.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+
+namespace OHOS {
+
+static constexpr int16_t ECG_COMPLETE_ANIM_X = 133;
+static constexpr int16_t ECG_COMPLETE_ANIM_Y = 105;
+static constexpr int16_t ECG_COMPLETE_ANIM_W = 200;
+static constexpr int16_t ECG_COMPLETE_ANIM_H = 200;
+static constexpr int16_t ECG_COMPLETE_ANIM_FRAME_COUNT = 40;
+static constexpr int16_t ECG_COMPLETE_ANIM_DELAY = 70;
+
+static const char *kCompleteAnimPaths[ECG_COMPLETE_ANIM_FRAME_COUNT] = {
+ "/user/res/AIVoice/AIVoiceThinking/1.bin",
+ "/user/res/AIVoice/AIVoiceThinking/2.bin",
+ "/user/res/AIVoice/AIVoiceThinking/3.bin",
+ "/user/res/AIVoice/AIVoiceThinking/4.bin",
+ "/user/res/AIVoice/AIVoiceThinking/5.bin",
+ "/user/res/AIVoice/AIVoiceThinking/6.bin",
+ "/user/res/AIVoice/AIVoiceThinking/7.bin",
+ "/user/res/AIVoice/AIVoiceThinking/8.bin",
+ "/user/res/AIVoice/AIVoiceThinking/9.bin",
+ "/user/res/AIVoice/AIVoiceThinking/10.bin",
+ "/user/res/AIVoice/AIVoiceThinking/11.bin",
+ "/user/res/AIVoice/AIVoiceThinking/12.bin",
+ "/user/res/AIVoice/AIVoiceThinking/13.bin",
+ "/user/res/AIVoice/AIVoiceThinking/14.bin",
+ "/user/res/AIVoice/AIVoiceThinking/15.bin",
+ "/user/res/AIVoice/AIVoiceThinking/16.bin",
+ "/user/res/AIVoice/AIVoiceThinking/17.bin",
+ "/user/res/AIVoice/AIVoiceThinking/18.bin",
+ "/user/res/AIVoice/AIVoiceThinking/19.bin",
+ "/user/res/AIVoice/AIVoiceThinking/20.bin",
+ "/user/res/AIVoice/AIVoiceThinking/21.bin",
+ "/user/res/AIVoice/AIVoiceThinking/22.bin",
+ "/user/res/AIVoice/AIVoiceThinking/23.bin",
+ "/user/res/AIVoice/AIVoiceThinking/24.bin",
+ "/user/res/AIVoice/AIVoiceThinking/25.bin",
+ "/user/res/AIVoice/AIVoiceThinking/26.bin",
+ "/user/res/AIVoice/AIVoiceThinking/27.bin",
+ "/user/res/AIVoice/AIVoiceThinking/28.bin",
+ "/user/res/AIVoice/AIVoiceThinking/29.bin",
+ "/user/res/AIVoice/AIVoiceThinking/30.bin",
+ "/user/res/AIVoice/AIVoiceThinking/31.bin",
+ "/user/res/AIVoice/AIVoiceThinking/32.bin",
+ "/user/res/AIVoice/AIVoiceThinking/33.bin",
+ "/user/res/AIVoice/AIVoiceThinking/34.bin",
+ "/user/res/AIVoice/AIVoiceThinking/35.bin",
+ "/user/res/AIVoice/AIVoiceThinking/36.bin",
+ "/user/res/AIVoice/AIVoiceThinking/37.bin",
+ "/user/res/AIVoice/AIVoiceThinking/38.bin",
+ "/user/res/AIVoice/AIVoiceThinking/39.bin",
+ "/user/res/AIVoice/AIVoiceThinking/40.bin",
+};
+
+static ECGMeasureCompleteView *g_instance = nullptr;
+
+ECGMeasureCompleteView::ECGMeasureCompleteView() { g_instance = this; }
+
+ECGMeasureCompleteView::~ECGMeasureCompleteView() {
+ StopAnimation();
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete noteLabel_;
+ delete animatorView_;
+ delete group_;
+ noteLabel_ = nullptr;
+ animatorView_ = nullptr;
+ group_ = nullptr;
+ }
+ UnloadAnimatorFrames();
+ g_instance = nullptr;
+}
+
+ECGMeasureCompleteView *ECGMeasureCompleteView::GetInstance() { return g_instance; }
+
+bool ECGMeasureCompleteView::InitAnimator() {
+ if (animatorInited_) {
+ return animatorView_ != nullptr;
+ }
+ animatorView_ = new UIImageAnimatorView();
+ if (animatorView_ == nullptr) {
+ return false;
+ }
+ for (int16_t i = 0; i < ECG_COMPLETE_ANIM_FRAME_COUNT; i++) {
+ frameInfo_[i].imageInfo =
+ ImageCacheManager::GetInstance().LoadSingleRes(kCompleteAnimPaths[i]);
+ frameInfo_[i].imageType = IMG_SRC_IMAGE_INFO;
+ frameInfo_[i].pos.x = ECG_COMPLETE_ANIM_X;
+ frameInfo_[i].pos.y = ECG_COMPLETE_ANIM_Y;
+ frameInfo_[i].width = ECG_COMPLETE_ANIM_W;
+ frameInfo_[i].height = ECG_COMPLETE_ANIM_H;
+ }
+ animatorView_->SetPosition(ECG_COMPLETE_ANIM_X, ECG_COMPLETE_ANIM_Y, ECG_COMPLETE_ANIM_W,
+ ECG_COMPLETE_ANIM_H);
+ animatorView_->SetImageAnimatorSrc(frameInfo_, ECG_COMPLETE_ANIM_FRAME_COUNT,
+ ECG_COMPLETE_ANIM_DELAY);
+ animatorView_->SetRepeat(true);
+ animatorView_->SetAutoEnable(false);
+ animatorView_->SetResizeMode(UIImageView::FILL);
+ animatorInited_ = true;
+ return true;
+}
+
+void ECGMeasureCompleteView::UnloadAnimatorFrames() {
+ if (!animatorInited_) {
+ return;
+ }
+ for (int16_t i = 0; i < ECG_COMPLETE_ANIM_FRAME_COUNT; i++) {
+ ImageCacheManager::GetInstance().UnloadSingleRes(kCompleteAnimPaths[i]);
+ frameInfo_[i].imageInfo = nullptr;
+ }
+ animatorInited_ = false;
+}
+
+void ECGMeasureCompleteView::StartAnimation() {
+ if (!InitAnimator() || animatorView_ == nullptr) {
+ return;
+ }
+ animatorView_->Start();
+}
+
+void ECGMeasureCompleteView::StopAnimation() {
+ if (animatorView_ != nullptr) {
+ animatorView_->Stop();
+ }
+}
+
+UIViewGroup *ECGMeasureCompleteView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ if (InitAnimator() && animatorView_ != nullptr) {
+ group_->Add(animatorView_);
+ }
+ InitUILabel(noteLabel_, 0, ECG_COMPLETE_ANIM_Y + ECG_COMPLETE_ANIM_H + 20, 466, 35,
+ "ecg_complete_note", "正在分析数据...", DEFAULT_VECTOR_FONT_FILENAME, 30);
+ noteLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ group_->Add(noteLabel_);
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGMeasureResultView.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "ecg/include/ECGModel.h"
+#include "ecg/include/ECGEntryView.h"
+#include "ecg/include/ECGViewClickIds.h"
+#include "uicomponents/UIComponents.h"
+
+namespace OHOS {
+
+static ECGMeasureResultView *g_instance = nullptr;
+
+ECGMeasureResultView::ECGMeasureResultView() { g_instance = this; }
+
+ECGMeasureResultView::~ECGMeasureResultView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete heartIcon_;
+ delete bpmValueLabel_;
+ delete bpmUnitLabel_;
+ delete resultLabel_;
+ delete confirmButton_;
+ delete backgroundIcon_;
+ delete group_;
+ heartIcon_ = nullptr;
+ backgroundIcon_ = nullptr;
+ bpmValueLabel_ = nullptr;
+ bpmUnitLabel_ = nullptr;
+ resultLabel_ = nullptr;
+ backgroundIcon_ = nullptr;
+ confirmButton_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+ECGMeasureResultView *ECGMeasureResultView::GetInstance() { return g_instance; }
+
+void ECGMeasureResultView::UpdateText() {
+ ECGModel *model = ECGModel::GetInstance();
+ ECGResultType type = model->GetResultType();
+ uint8_t bpm = model->GetResultBpm();
+
+ if (bpmValueLabel_ != nullptr) {
+ if (type == ECGResultType::INCONCLUSIVE || bpm == 0) {
+ bpmValueLabel_->SetText("--");
+ } else {
+ bpmValueLabel_->SetText(std::to_string(bpm).c_str());
+ }
+ }
+ if (resultLabel_ != nullptr) {
+ resultLabel_->SetText(model->GetResultText(type).c_str());
+ resultLabel_->SetTextColor(model->GetResultColor(type));
+ }
+}
+
+UIViewGroup *ECGMeasureResultView::InitView() {
+ group_ = new UIViewGroup();
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ group_->SetOnDragListener(static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance()));
+
+ InitImageView(backgroundIcon_, 0, 43, 466, 380, "ecg_result_background", ECG_PATH_RESULT_BACKGROUND);
+
+ InitImageView(heartIcon_, 206, 100, 54, 50, "ecg_result_heart", ECG_PATH_HEART_ICON);
+
+
+ InitUILabel(bpmValueLabel_, 0, 170, 466, 90, "ecg_result_bpm", "--",
+ DEFAULT_VECTOR_FONT_FILENAME, 72);
+ bpmValueLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(bpmUnitLabel_, 0, 250, 466, 30, "ecg_result_bpm_unit", "bpm",
+ DEFAULT_VECTOR_FONT_FILENAME, 26);
+ bpmUnitLabel_->SetTextColor(Color::GetColorFromRGBA(0x99, 0x99, 0x99, 0xff));
+ bpmUnitLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(resultLabel_, 0, 290, 466, 40, "ecg_result_type", "窦性心律",
+ DEFAULT_VECTOR_FONT_FILENAME, 32);
+ resultLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitLabelButton(confirmButton_, 93, 330, 280, 90, ECG_CLICK_CONFIRM, "完成", 30);
+ confirmButton_->SetStyle(STYLE_BACKGROUND_COLOR, 0xff191919);
+ confirmButton_->SetStyle(STYLE_BORDER_RADIUS, 45);
+ confirmButton_->SetOnClickListener(static_cast<UIView::OnClickListener *>(ECGMainPresenter::GetInstance()));
+
+ group_->Add(backgroundIcon_);
+ group_->Add(heartIcon_);
+ group_->Add(bpmValueLabel_);
+ group_->Add(bpmUnitLabel_);
+ group_->Add(resultLabel_);
+ group_->Add(confirmButton_);
+
+ UpdateText();
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGMeasuringView.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "ecg/include/ECGViewClickIds.h"
+#include "uicomponents/UIComponents.h"
+#include "wearable_log.h"
+#include <cmath>
+
+#ifndef sinf
+#define sinf(x) static_cast<float>(sin(x))
+#endif
+
+namespace OHOS {
+
+static ECGMeasuringView *g_instance = nullptr;
+
+ECGMeasuringView::ECGMeasuringView() { g_instance = this; }
+
+ECGMeasuringView::~ECGMeasuringView() {
+ if (group_ != nullptr) {
+ group_->RemoveAll();
+ delete titleLabel_;
+ delete chart_;
+ delete dataSerial_;
+ delete countdownLabel_;
+ delete hintLabel_;
+ delete group_;
+ titleLabel_ = nullptr;
+ chart_ = nullptr;
+ dataSerial_ = nullptr;
+ countdownLabel_ = nullptr;
+ hintLabel_ = nullptr;
+ group_ = nullptr;
+ }
+ g_instance = nullptr;
+}
+
+ECGMeasuringView *ECGMeasuringView::GetInstance() { return g_instance; }
+
+void ECGMeasuringView::InitChart() {
+ if (chart_ != nullptr) {
+ return;
+ }
+ chart_ = new UIChartPolyline();
+ if (chart_ == nullptr) {
+ return;
+ }
+ chart_->SetPosition(76, 150, 313, 122);
+ chart_->SetStyle(STYLE_BACKGROUND_OPA, 0x00);
+
+ UIXAxis &xAxis = chart_->GetXAxis();
+ xAxis.SetMarkNum(1);
+ xAxis.SetDataRange(0, kWavePointCount - 1);
+ xAxis.SetLineColor(Color::GetColorFromRGBA(0, 0, 0, 0));
+
+ UIYAxis &yAxis = chart_->GetYAxis();
+ yAxis.SetMarkNum(1);
+ yAxis.SetDataRange(0, 220);
+ yAxis.SetLineColor(Color::GetColorFromRGBA(0, 0, 0, 0));
+
+ chart_->SetGradientOpacity(78, 64);
+
+ dataSerial_ = new UIChartDataSerial();
+ if (dataSerial_ != nullptr) {
+ dataSerial_->SetMaxDataCount(kWavePointCount);
+ dataSerial_->SetLineColor(Color::GetColorFromRGBA(0xff, 0x3a, 0x3a, 0xff));
+ dataSerial_->SetFillColor(Color::GetColorFromRGBA(0xff, 0x3a, 0x3a, 0xff));
+ dataSerial_->EnableGradient(true);
+ chart_->AddDataSerial(dataSerial_);
+ }
+}
+
+void ECGMeasuringView::UpdateWaveform() {
+ if (dataSerial_ == nullptr || chart_ == nullptr) {
+ return;
+ }
+ wavePhase_ += 0.35f;
+ for (int i = 0; i < kWavePointCount; i++) {
+ float t = wavePhase_ + static_cast<float>(i) * 0.28f;
+ wavePoints_[i].x = static_cast<int16_t>(i);
+ int16_t base = 110;
+ int16_t v = base + static_cast<int16_t>(35.0f * sinf(t)) +
+ static_cast<int16_t>(18.0f * sinf(t * 4.1f));
+ if (v < 20) {
+ v = 20;
+ } else if (v > 200) {
+ v = 200;
+ }
+ wavePoints_[i].y = v;
+ }
+ dataSerial_->ClearData();
+ dataSerial_->AddPoints(wavePoints_, kWavePointCount);
+ chart_->Invalidate();
+}
+
+void ECGMeasuringView::UpdateCountdownLabel() {
+ if (countdownLabel_ == nullptr) {
+ return;
+ }
+ std::string text = std::to_string(countdownSec_) + "s";
+ countdownLabel_->SetText(text.c_str());
+ countdownLabel_->Invalidate();
+}
+
+void ECGMeasuringView::ResetMeasurement() {
+ countdownSec_ = 30;
+ wavePhase_ = 0.0f;
+ UpdateCountdownLabel();
+ UpdateWaveform();
+}
+
+bool ECGMeasuringView::OnTimerTick() {
+ UpdateWaveform();
+ if (countdownSec_ > 0) {
+ countdownSec_--;
+ UpdateCountdownLabel();
+ }
+ return countdownSec_ <= 0;
+}
+
+void ECGMeasuringView::StopMeasurement() {}
+
+UIViewGroup *ECGMeasuringView::InitView() {
+ group_ = new UIViewGroup();
+ if (group_ == nullptr) {
+ return nullptr;
+ }
+ group_->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+ group_->SetOnDragListener(static_cast<UIView::OnDragListener *>(ECGMainPresenter::GetInstance()));
+
+ InitUILabel(titleLabel_, 0, 50, 466, 35, "ecg_measuring_title", "ECG",
+ DEFAULT_VECTOR_FONT_FILENAME, 30);
+ titleLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitChart();
+ if (chart_ != nullptr) {
+ group_->Add(chart_);
+ }
+
+ InitUILabel(countdownLabel_, 183, 300, 100, 50, "ecg_measuring_countdown", "30s",
+ DEFAULT_VECTOR_FONT_FILENAME, 40);
+ countdownLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ InitUILabel(hintLabel_, 53, 360, 360, 35, "ecg_measuring_hint",
+ "请保持手指接触电极", DEFAULT_VECTOR_FONT_FILENAME, 26);
+ hintLabel_->SetTextColor(Color::GetColorFromRGBA(0xff, 0x3a, 0x3a, 0xff));
+ hintLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+
+ group_->Add(titleLabel_);
+ group_->Add(countdownLabel_);
+ group_->Add(hintLabel_);
+
+ ResetMeasurement();
+ return group_;
+}
+
+} // namespace OHOS
--- /dev/null
+#include "ecg/include/ECGModel.h"
+#include "securec.h"
+#include "wearable_log.h"
+#include <ctime>
+
+namespace OHOS {
+
+ECGModel::ECGModel() { LoadData(); }
+
+ECGModel::~ECGModel() {}
+
+ECGModel *ECGModel::GetInstance() {
+ static ECGModel instance;
+ return &instance;
+}
+
+void ECGModel::LoadData() {
+ historyRecords_.clear();
+ time_t now = time(nullptr);
+ uint32_t ts = now > 0 ? static_cast<uint32_t>(now) : 0;
+
+ ECGRecord r1{ts - 3600, 76, ECGResultType::SINUS_RHYTHM};
+ ECGRecord r2{ts - 86400, 82, ECGResultType::ATRIAL_FIBRILLATION};
+ ECGRecord r3{ts - 172800, 0, ECGResultType::INCONCLUSIVE};
+ historyRecords_.push_back(r1);
+ historyRecords_.push_back(r2);
+ historyRecords_.push_back(r3);
+ lastRecord_ = r1;
+}
+
+ECGRecord ECGModel::GetLastRecord() const { return lastRecord_; }
+
+bool ECGModel::HasHistory() const { return !historyRecords_.empty(); }
+
+const std::deque<ECGRecord> &ECGModel::GetHistoryRecords() const { return historyRecords_; }
+
+std::string ECGModel::FormatShortTimestamp(uint32_t timestamp) const {
+ if (timestamp == 0) {
+ return "--";
+ }
+ time_t t = static_cast<time_t>(timestamp);
+ tm *timeInfo = localtime(&t);
+ if (timeInfo == nullptr) {
+ return "--";
+ }
+ char buf[32] = {0};
+ if (sprintf_s(buf, sizeof(buf), "%02d-%02d %02d:%02d", timeInfo->tm_mon + 1,
+ timeInfo->tm_mday, timeInfo->tm_hour, timeInfo->tm_min) < 0) {
+ return "--";
+ }
+ return std::string(buf);
+}
+
+std::string ECGModel::GetResultText(ECGResultType type) const {
+ switch (type) {
+ case ECGResultType::SINUS_RHYTHM:
+ return "窦性心律";
+ case ECGResultType::ATRIAL_FIBRILLATION:
+ return "房颤";
+ case ECGResultType::INCONCLUSIVE:
+ default:
+ return "不确定";
+ }
+}
+
+ColorType ECGModel::GetResultColor(ECGResultType type) const {
+ Color32 color;
+ switch (type) {
+ case ECGResultType::SINUS_RHYTHM:
+ color.full = 0xff4fc34c;
+ break;
+ case ECGResultType::ATRIAL_FIBRILLATION:
+ color.full = 0xffe79644;
+ break;
+ case ECGResultType::INCONCLUSIVE:
+ default:
+ color.full = 0xff999999;
+ break;
+ }
+ return color;
+}
+
+void ECGModel::SetMeasureResult(uint8_t bpm, ECGResultType result) {
+ resultBpm_ = bpm;
+ resultType_ = result;
+}
+
+uint8_t ECGModel::GetResultBpm() const { return resultBpm_; }
+
+ECGResultType ECGModel::GetResultType() const { return resultType_; }
+
+void ECGModel::AddMeasureToHistory() {
+ time_t now = time(nullptr);
+ uint32_t ts = now > 0 ? static_cast<uint32_t>(now) : 0;
+ ECGRecord record{ts, resultBpm_, resultType_};
+ historyRecords_.push_front(record);
+ if (historyRecords_.size() > 10) {
+ historyRecords_.pop_back();
+ }
+ lastRecord_ = record;
+}
+
+} // namespace OHOS