Merge #1711 into sdk650 from refs/changes/1711/head 1713/target/1
author罗正 <1732036298@qq.com>
Wed, 26 Mar 2025 08:37:38 +0000 (08:37 +0000)
committer袁清梦 <3449750381@qq.com>
Wed, 26 Mar 2025 08:37:38 +0000 (08:37 +0000)
Code review from 罗正

* refs/changes/1711/head: (9 commits squashed)

  - 查找手机slice合并提交

  - 计时器slice合并提交

  - 日历slice合并提交

  - 日程slice合并提交

  - 订阅消息Topic文件提交

  - 遥控拍照slice合并提交

  - 呼吸训练slice合并提交

  - Merge branch &#39;sdk650&#39; of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650

  - 秒表slice合并提交

Signed-off-by: 罗正 <1732036298@qq.com>
Merged-by: Caisking <3449750381@qq.com>
link: https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230/change/1711
25 files changed:
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingCompletedView.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingDurationSelectionView.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingModel.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingPresenter.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingView.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingCompletedView.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingDurationSelectionView.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingModel.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingPresenter.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingView.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/MainStopWatchPresenter.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/MainStopWatchView.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchAdapter.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchItemView.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchModel.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchRecordCycleToLimitView.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchRecordTimeToLimitView.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/MainStopWatchPresenter.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/MainStopWatchView.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchAdapter.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchItemView.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchModel.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchRecordCycleToLimitView.cpp [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchRecordTimeToLimitView.cpp [new file with mode: 0644]
code/sdk/build/config/target_config/brandy/config.py

diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingCompletedView.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingCompletedView.h
new file mode 100644 (file)
index 0000000..3fb267a
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-06-08 18:24:24
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-06-11 14:41:30
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/respiratorytraining/include/RespiratoryTrainingCompletedCompletedView.h
+ */
+
+#ifndef RESPIRATORY_TRAINING_COMPLETED_VIEW_H
+#define RESPIRATORY_TRAINING_COMPLETED_VIEW_H
+
+
+#include "View.h"
+#include "components/ui_scroll_view.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_label_button_ext.h"
+#include "components/ui_image_view.h"
+
+namespace OHOS
+{
+    static constexpr int MAX_NUM = 3;
+    static constexpr const char *RTCLABELBUTTON = "RTCLabelButton";
+    static constexpr const char *RTCVectorImagePath = "/user/res/RespiratoryTraining/RTCVector.bin";
+    static constexpr const char *RTCGroupListImagePath = "/user/res/RespiratoryTraining/RTCGroupList.bin";
+    class RespiratoryTrainingCompletedView
+    {
+    public:
+        struct LabelConfig
+        {
+            int16_t left;
+            int16_t top;
+            int16_t width;
+            int16_t height;
+            int16_t fontSize;  // 字体大小
+            int16_t lineHeigh; // 行高
+            const char *text;
+            int16_t textId;
+            uint8_t lineBreakMode; // 断行模式。
+        };
+
+        struct ImageConfig
+        {
+            int16_t x;
+            int16_t y;
+            int16_t w;
+            int16_t h;
+        };
+
+        RespiratoryTrainingCompletedView();
+        ~RespiratoryTrainingCompletedView();
+        static RespiratoryTrainingCompletedView *GetInstance(void);
+        UIScrollView *RespiratoryTrainingCompletedViewInit(void);
+
+    private:
+        void ClearAll(void);
+        bool CreateRespiratoryTrainingCompletedScrollView();
+        bool CreateRespiratoryTrainingCompletedLabelView();
+        bool CreateRespiratoryTrainingCompletedImageView();
+        bool CreateRespiratoryTrainingCompletedLabelButtonView();
+        UILabelExt *LabelExtViewInit(const LabelConfig &labelconfig);
+        UILabel *LabelViewInit(const LabelConfig &labelconfig);
+        UIImageView *ImageViewInit(ImageConfig &imageconfig);
+        UIScrollView *ScrollView{nullptr};
+        UILabelExt *Title{nullptr};
+        UILabelExt *RespiratoryTrainingLabel{nullptr};
+        UIImageView *RespiratoryTrainingImage{nullptr};
+        UILabelExt *RTCStaticLabelView[MAX_NUM]{nullptr};
+        UILabel *RTCDynamicLabelView[MAX_NUM]{nullptr};
+        UIImageView *RTCImageView[MAX_NUM]{nullptr};
+        UILabelButtonExt *RTCLabelButton{nullptr};
+    };
+}
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingDurationSelectionView.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingDurationSelectionView.h
new file mode 100644 (file)
index 0000000..7955be9
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-09-29 17:51:20
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-10-04 17:47:43
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/respiratorytraining/include/RespiratoryTrainingDurationSelectionView.h
+ */
+#ifndef RESPIRATORY_TRAINING_DURATION_SELECTION_VIEW_H
+#define RESPIRATORY_TRAINING_DURATION_SELECTION_VIEW_H
+#include "components/ui_scroll_view.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_label_button_ext.h"
+
+#include "components/ui_image_view.h"
+namespace OHOS
+{
+#define MAX_NUM_TRAINING_DURATION 5
+
+    static constexpr const char *RESPIRATORYTRAINING_DURATIONSELECTION_VIEW = "RespiratoryTrainingDurationSelectionView";
+    static constexpr const char *TRAINING_DURATION_ONE_MIN = "training_duration_one_min";
+    static constexpr const char *TRAINING_DURATION_TWO_MIN = "training_duration_two_min";
+    static constexpr const char *TRAINING_DURATION_THREE_MIN = "training_duration_three_min";
+    static constexpr const char *TRAINING_DURATION_FOUR_MIN = "training_duration_four_min";
+    static constexpr const char *TRAINING_DURATION_FIVE_MIN = "training_duration_five_min";
+    class RespiratoryTrainingDurationSelectionView
+    {
+    public:
+        struct LabelButtonConfig
+        {
+            int16_t left;
+            int16_t top;
+            int16_t width;
+            int16_t height;
+            const char *viewId;
+        };
+        struct LabelConfig
+        {
+            int16_t left;
+            int16_t top;
+            int16_t width;
+            int16_t height;
+            int16_t fontSize;
+            int16_t lineHeigh;
+            const char *text;
+            int16_t textId;
+            uint8_t lineBreakMode;
+        };
+        RespiratoryTrainingDurationSelectionView(/* args */);
+        ~RespiratoryTrainingDurationSelectionView();
+        static RespiratoryTrainingDurationSelectionView *GetInstance(void);
+        UIScrollView *RespiratoryTrainingDurationSelectionViewInit(void);
+        void UpdateRespiratoryTrainingDurationSelectionView(void);
+
+    private:
+        UILabelExt *LabelViewinit(LabelConfig &labelconfig);
+        UILabelButtonExt *LabelButtonViewInit(LabelButtonConfig &labelbuttonconfig);
+        void ClearAll(void);
+        UIScrollView *scrollView = nullptr;
+        UILabelExt *TitleLabel = nullptr;                                     // 标题
+        UILabelExt *DurationLabel[MAX_NUM_TRAINING_DURATION]{nullptr};        // 亮屏时长标签
+        UILabelButtonExt *DurationButton[MAX_NUM_TRAINING_DURATION]{nullptr}; // 放置组件的按钮
+        UIImageView *temp{nullptr};
+    };
+
+}
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingModel.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingModel.h
new file mode 100644 (file)
index 0000000..a6ad2c5
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-06-11 16:26:13
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-10-09 12:22:31
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/respiratorytraining/include/RespiratoryTrainingModel.h
+ */
+#ifndef RESPIRATORY_TRAINING_MODEL_H
+#define RESPIRATORY_TRAINING_MODEL_H
+
+#include <cstdint>
+#include <iostream>
+#include "string"
+namespace OHOS
+{
+    class RespiratoryTrainingModel
+    {
+    public:
+        static RespiratoryTrainingModel &GetInstance(void);
+        void AddRespiratoryTrainingTimes(void);
+        std::string GetRespiratoryTrainingTotalTime(void);
+        int GetAverageHeartRate(void);
+        void calculateHeartrateTotal(void);
+        void ResumeStatus();
+
+        void RT_StartTimer(uint64_t interval);
+        void RT_StopTimer(void);
+
+        int GetRespiratoryTrainingSelectTime();
+        void SetRespiratoryTrainingSelectTime(int time);
+        uint64_t GetRespiratoryTrainingTime();
+        void ClearTotalRespiratoryTrainingTime(void);
+
+    private:
+        RespiratoryTrainingModel();
+        ~RespiratoryTrainingModel();
+        RespiratoryTrainingModel(const RespiratoryTrainingModel &) = delete;
+        RespiratoryTrainingModel &operator=(const RespiratoryTrainingModel &) = delete;
+
+        int RespiratoryTrainingTimes{0}; // 呼吸训练次数
+        int RespiratoryTrainingTime{0};  
+        std::string HeartRateValue{nullptr};
+        int count{0};
+        float heartTotal{0};
+        int selectTime{1};
+    };
+}
+
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingPresenter.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingPresenter.h
new file mode 100644 (file)
index 0000000..913e5fe
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-06-08 09:36:33
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-10-05 12:29:42
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/respiratorytraining/include/RespiratoryTrainingPresenter.h
+ */
+
+#ifndef RESPIRATORY_TRAINING_PRESENTER_H
+#define RESPIRATORY_TRAINING_PRESENTER_H
+
+#include "Presenter.h"
+#include "View.h"
+
+namespace OHOS
+{
+
+    class RespiratoryTrainingView;
+    class RespiratoryTrainingPresenter : public Presenter<RespiratoryTrainingView>,
+                                         public UIView::OnClickListener,
+                                         public RootView::OnKeyActListener,
+                                         public UIView::OnDragListener
+    {
+    public:
+        RespiratoryTrainingPresenter();
+        virtual ~RespiratoryTrainingPresenter();
+
+        void OnStart() override;
+        void OnPause() override;
+        void OnResume() override;
+        void OnStop() override;
+
+        static RespiratoryTrainingPresenter *GetInstance();
+
+        int GetRespiratoryTrainingSelectTime();
+        std::string GetRespiratoryTrainingSelectTime1();
+
+    private:
+        bool OnClick(UIView &view, const ClickEvent &event) override;
+        bool OnDrag(UIView &view, const DragEvent &event) override;
+        bool OnDragEnd(UIView &view, const DragEvent &event) override;
+        bool OnKeyAct(UIView &view, const KeyEvent &event) override; // 增加按键事件
+
+        bool X_DRAG_FLAG{false};
+    };
+}
+
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingView.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/include/RespiratoryTrainingView.h
new file mode 100644 (file)
index 0000000..09d1549
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-06-08 09:36:01
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2025-02-24 17:35:35
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/respiratorytraining/include/RespiratoryTrainingView.h
+ */
+
+#ifndef RESPIRATORY_TRAINING_VIEW_H
+#define RESPIRATORY_TRAINING_VIEW_H
+
+
+#include "View.h"
+#include "components/ui_label_ext.h"
+#include <components/ui_label_button_ext.h>
+#include "components/ui_image_animator.h"
+#include "components/ui_image_view.h"
+#include "components/root_view.h"
+#include "hal_tick.h"
+#include "components/ui_scroll_view.h"
+#include "respiratorytraining/include/RespiratoryTrainingCompletedView.h"
+#include "respiratorytraining/include/RespiratoryTrainingDurationSelectionView.h"
+
+namespace OHOS
+{
+    typedef enum
+    {
+        RESPIRATORYTRAINING = 0,
+        RESPIRATORYTRAININGCOMPLETED,
+        RESPIRATORY_TRAINING_DURATION_SELECTION, // 训练时长选择
+        MAX_RESPIRATORYTRAININGID,
+    } RESPIRATORYTRAININGVIEWID;
+
+    class RespiratoryTrainingCallback : public AnimatorCallback
+    {
+    public:
+        explicit RespiratoryTrainingCallback(UIImageView *img) : img_(img) {}
+        ~RespiratoryTrainingCallback() override {}
+        void Callback(UIView *view) override;
+        void SetTextView(UILabelExt *view)
+        {
+            breath = view;
+        }
+
+        uint32_t startTimestamp = 0;
+
+    private:
+        UIImageView *img_ = nullptr;
+        UILabelExt *breath = nullptr;
+        bool isBreathIn = false;
+        int16_t angle = 0;
+        int16_t step = -1;
+        float scale = 0;
+        uint16_t curTime = 0;
+        uint16_t prepareTiem = 0;
+        uint16_t durationTime = 5 * 1000; // 5000: means 5000ms
+        int16_t startVal = durationTime;
+        int16_t endVal = durationTime / 2; // 2: half
+        bool isprepare{false};
+        int prepare_count = 0;
+    };
+
+    static constexpr int RESPIRATORY_TRAINING_IMAGE_ANIMATOR_VIEW_X = 193;
+    static constexpr int RESPIRATORY_TRAINING_IMAGE_ANIMATOR_VIEW_Y = 110;
+    static constexpr int RESPIRATORY_TRAINING_IMAGE_ANIMATOR_VIEW_W = 80;
+    static constexpr int RESPIRATORY_TRAINING_IMAGE_ANIMATOR_VIEW_H = 80;
+    static constexpr int RESPIRATORY_TRAINING_IMAGE_ANIMATOR_VIEW_TIMEOFUPDATE = 100;
+    static constexpr int RESPIRATORY_TRAINING_IMAGE_ANIMATOR_VIEW_NUM_FRAMES = 25; // 呼吸训练动画帧数
+    static ImageAnimatorInfo RespiratoryTrainingImageInfo[RESPIRATORY_TRAINING_IMAGE_ANIMATOR_VIEW_NUM_FRAMES];
+
+    class RespiratoryTrainingPresenter;
+    class RespiratoryTrainingView : public View<RespiratoryTrainingPresenter>, public UIView::OnClickListener
+    {
+
+    public:
+        RespiratoryTrainingView();
+        virtual ~RespiratoryTrainingView();
+        static RespiratoryTrainingView *GetInstance(void);
+        bool ClickFunc(int16_t interface);
+        void ChangeView(int16_t hideView, int16_t showView);
+        bool ChangeRespiratoryTrainingView(int16_t thisView);
+        bool ChangeRespiratoryTrainingCompletedView(int16_t thisView);
+        bool ChangeRespiratoryTrainingDurationSelectionView(int16_t thisView);
+        Animator *GetRespiratoryTrainingAnimator_();
+        bool StartRespiratoryTraining();
+        void OnStart() override;
+        bool OnClick(UIView &view, const ClickEvent &event) override;
+
+    private:
+        void RespiratoryTrainingViewInit(void);
+        void ClearAll(void);
+        bool InitRespiratoryTrainingElements();
+        bool InitRespiratoryTrainingElements2();
+        bool InitRespiratoryTrainingCallBackAndAnimator();
+        bool InitRespiratoryTrainingScrollView();
+        void HandStartRespiratoryTraining();
+        UIScrollView *RespiratoryTrainingScrollView[MAX_RESPIRATORYTRAININGID]{nullptr};
+        RespiratoryTrainingCompletedView *p_RespiratoryTrainingCompletedView{nullptr};
+        RespiratoryTrainingDurationSelectionView *p_RespiratoryTrainingDurationSelectionView{nullptr};
+        RespiratoryTrainingCallback *RespiratoryTrainingCallBack_ = nullptr;
+        Animator *RespiratoryTrainingAnimator_ = nullptr;
+        UIImageView *RespiratoryTrainingImg_ = nullptr;
+        UILabelExt *RespiratoryTrainingStartNote_ = nullptr;
+        UILabelExt *RespiratoryTrainingLabel_ = nullptr;
+        bool animateStart_ = false;
+        /* -------------------------------------------------------------------------- */
+        UILabelExt *titlelabel{nullptr};                          // 标题
+        UILabelExt *TrainingDurationSelectiontitlelabel{nullptr}; // 选择的训练时长
+        UIImageView *Image{nullptr};
+        UILabelButton *RespiratoryTrainingStartButton = nullptr; // 训练开始按钮
+        UILabelExt *RespiratoryTrainingStartLabel = nullptr;  // 训练开始标签
+        /* -------------------------------------------------------------------------- */
+    };
+
+}
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingCompletedView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingCompletedView.cpp
new file mode 100644 (file)
index 0000000..962b347
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-05-07 11:25:58
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2025-02-25 15:09:29
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/RespiratoryTrainingCompleted/src/RespiratoryTrainingCompletedView.cpp
+ */
+
+#include "respiratorytraining/include/RespiratoryTrainingCompletedView.h"
+#include "wearable_log.h"
+#include "ui_resource_string.h"
+#include "respiratorytraining/include/RespiratoryTrainingPresenter.h"
+#include "common/image_cache_manager.h"
+#include "respiratorytraining/include/RespiratoryTrainingModel.h"
+
+namespace OHOS
+{
+
+    static RespiratoryTrainingCompletedView *g_pRespiratoryTrainingCompletedView{nullptr};
+
+    RespiratoryTrainingCompletedView::RespiratoryTrainingCompletedView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::RespiratoryTrainingCompletedView");
+        g_pRespiratoryTrainingCompletedView = this;
+    }
+
+    RespiratoryTrainingCompletedView::~RespiratoryTrainingCompletedView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::~RespiratoryTrainingCompletedView");
+        ClearAll();
+        g_pRespiratoryTrainingCompletedView = nullptr;
+    }
+
+    RespiratoryTrainingCompletedView *RespiratoryTrainingCompletedView::GetInstance()
+    {
+        return g_pRespiratoryTrainingCompletedView;
+    }
+
+    void RespiratoryTrainingCompletedView::ClearAll(void)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::ClearAll Start!!");
+        if (ScrollView != nullptr)
+        {
+            ScrollView->RemoveAll();
+            ImageCacheManager::GetInstance().UnloadSingleRes(RTCVectorImagePath);
+            ImageCacheManager::GetInstance().UnloadSingleRes(RTCGroupListImagePath);
+
+            if (Title != nullptr)
+            {
+                delete Title;
+                Title = nullptr;
+            }
+
+            if (RespiratoryTrainingImage != nullptr)
+            {
+                delete RespiratoryTrainingImage;
+                RespiratoryTrainingImage = nullptr;
+            }
+
+            if (RespiratoryTrainingLabel != nullptr)
+            {
+                delete RespiratoryTrainingLabel;
+                RespiratoryTrainingLabel = nullptr;
+            }
+            for (int i = 0; i < MAX_NUM; i++)
+            {
+                if (RTCStaticLabelView[i] != nullptr)
+                {
+                    delete RTCStaticLabelView[i];
+                    RTCStaticLabelView[i] = nullptr;
+                }
+
+                if (RTCDynamicLabelView[i] != nullptr)
+                {
+                    delete RTCDynamicLabelView[i];
+                    RTCDynamicLabelView[i] = nullptr;
+                }
+
+                if (RTCImageView[i] != nullptr)
+                {
+                    delete RTCImageView[i];
+                    RTCImageView[i] = nullptr;
+                }
+            }
+
+            if (RTCLabelButton != nullptr)
+            {
+                delete RTCLabelButton;
+                RTCLabelButton = nullptr;
+            }
+
+            delete ScrollView;
+            ScrollView = nullptr;
+        }
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::ClearAll End!!");
+    }
+
+    UILabelExt *RespiratoryTrainingCompletedView::LabelExtViewInit(const LabelConfig &labelconfig)
+    {
+        UILabelExt *label = new UILabelExt();
+        if (label == nullptr)
+        {
+            return nullptr;
+        }
+        label->SetPosition(labelconfig.left, labelconfig.top, labelconfig.width, labelconfig.height); // 位置大小
+        label->SetStyle(STYLE_TEXT_FONT, labelconfig.fontSize);                                       // 字体大小
+        label->SetStyle(STYLE_LINE_HEIGHT, labelconfig.lineHeigh);                                    // 字体行高
+        label->SetTextId(labelconfig.textId);                                                         // 内容
+        label->SetLineBreakMode(labelconfig.lineBreakMode);                                           // 断行模式。
+        label->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+        label->SetFont(DEFAULT_VECTOR_FONT_FILENAME, labelconfig.fontSize);
+        return label;
+    }
+
+    UILabel *RespiratoryTrainingCompletedView::LabelViewInit(const LabelConfig &labelconfig)
+    {
+        UILabel *label = new UILabel();
+        if (label == nullptr)
+        {
+            return nullptr;
+        }
+        label->SetPosition(labelconfig.left, labelconfig.top, labelconfig.width, labelconfig.height); // 位置大小
+        label->SetStyle(STYLE_TEXT_FONT, labelconfig.fontSize);                                       // 字体大小
+        label->SetStyle(STYLE_LINE_HEIGHT, labelconfig.lineHeigh);                                    // 字体行高
+        label->SetText(labelconfig.text);                                                             // 内容
+        label->SetLineBreakMode(labelconfig.lineBreakMode);                                           // 断行模式。
+        label->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+        label->SetFont(DEFAULT_VECTOR_FONT_FILENAME, labelconfig.fontSize);
+        return label;
+    }
+
+    UIImageView *RespiratoryTrainingCompletedView::ImageViewInit(ImageConfig &imageconfig)
+    {
+        UIImageView *Image = new UIImageView();
+        if (Image == nullptr)
+        {
+            return nullptr;
+        }
+        Image->SetPosition(imageconfig.x, imageconfig.y, imageconfig.w, imageconfig.h);
+        return Image;
+    }
+
+    bool RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedScrollView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedScrollView");
+        ScrollView = new UIScrollView();
+        if (ScrollView == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ScrollView new fail!");
+            return false;
+        }
+        ScrollView->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+        ScrollView->SetScrollBlankSize(75);
+        ScrollView->SetReboundSize(35);
+        ScrollView->SetHorizontalScrollState(false);
+        ScrollView->SetTouchable(true);
+        ScrollView->SetDraggable(true);
+        ScrollView->RequestFocus();
+        ScrollView->SetRotateFactor(1.0);
+
+        return true;
+    }
+
+    bool RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedLabelView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedLabelView");
+        /* ---------------------------------- Title --------------------------------- */
+        LabelConfig labelconfig = {
+            .left = 83,
+            .top = 52,
+            .width = 300,
+            .height = 35,
+            .fontSize = 30,
+            .lineHeigh = 35,
+            .textId = STR_TRAINING_COMPLETE, // 训练完成
+            .lineBreakMode = UILabel::LINE_BREAK_MARQUEE,
+        };
+        Title = LabelExtViewInit(labelconfig);
+        if (nullptr == Title)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Title is nullptr!");
+            return false;
+        }
+        Title->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+        ScrollView->Add(Title);
+        /* ------------------------ RespiratoryTrainingLabel ------------------------ */
+        labelconfig.left = 147;
+        labelconfig.top = 150;
+        labelconfig.width = 240;
+        labelconfig.textId = STR_BREATH_TRAINING; // 呼吸训练
+        RespiratoryTrainingLabel = LabelExtViewInit(labelconfig);
+        if (nullptr == RespiratoryTrainingLabel)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingLabel is nullptr!");
+            return false;
+        }
+        ScrollView->Add(RespiratoryTrainingLabel);
+        /* -------------------------------------------------------------------------- */
+        int y1 = 248, y2 = 286;
+        const int textId[MAX_NUM] = {STR_THIS_BREATH, STR_MEAN_HEART_RATE_MIN, STR_TODAYS_BREATHING_TRAINING};
+        int value = RespiratoryTrainingModel::GetInstance().GetAverageHeartRate();
+        std::string select_time;
+        if (RespiratoryTrainingPresenter::GetInstance())
+        {
+            select_time = RespiratoryTrainingPresenter::GetInstance()->GetRespiratoryTrainingSelectTime1();
+        }
+        std::string heartRateValue = (value == 0 ? "--" : std::to_string(value));
+        std::string trainingTotalTime = RespiratoryTrainingModel::GetInstance().GetRespiratoryTrainingTotalTime();
+        const char *text[MAX_NUM] = {select_time.c_str(), heartRateValue.c_str(), trainingTotalTime.c_str()};
+        OHOS::ColorType RTCDynamicLabelTextColor[MAX_NUM] = {
+            Color::GetColorFromRGBA(0x1d, 0xda, 0xc4, 0xff),
+            Color::GetColorFromRGBA(0xff, 0x5a, 0x36, 0xff),
+            Color::GetColorFromRGBA(0xff, 0x8a, 0x36, 0xff),
+        };
+        for (int i = 0; i < MAX_NUM; i++)
+        {
+            labelconfig.left = 138;
+            labelconfig.top = y1 + 130 * i;
+            labelconfig.width = 240;
+            labelconfig.height = 35;
+            labelconfig.fontSize = 30;
+            labelconfig.lineHeigh = 35;
+            labelconfig.textId = textId[i];
+            labelconfig.lineBreakMode = UILabel::LINE_BREAK_MARQUEE;
+            RTCStaticLabelView[i] = LabelExtViewInit(labelconfig);
+            if (nullptr == RTCStaticLabelView[i])
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RTCStaticLabelView[%d] is nullptr!", i);
+                return false;
+            }
+            RTCStaticLabelView[i]->SetTextColor(Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0x7f));
+            ScrollView->Add(RTCStaticLabelView[i]);
+            /* -------------------------------------------------------------------------- */
+            labelconfig.left = 138;
+            labelconfig.top = y2 + 130 * i;
+            labelconfig.height = 47;
+            labelconfig.fontSize = 40;
+            labelconfig.lineHeigh = 47;
+            labelconfig.text = text[i];
+            RTCDynamicLabelView[i] = LabelViewInit(labelconfig);
+            if (nullptr == RTCDynamicLabelView[i])
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RTCDynamicLabelView[%d] is nullptr!", i);
+                return false;
+            }
+            RTCDynamicLabelView[i]->SetTextColor(RTCDynamicLabelTextColor[i]);
+            RTCDynamicLabelView[i]->SetFont(DEFAULT_VECTOR_FONT_FILENAME_EN, 30);
+            ScrollView->Add(RTCDynamicLabelView[i]);
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedImageView()
+    {
+        RespiratoryTrainingImage = new UIImageView();
+        if (RespiratoryTrainingImage == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingImage is nullptr!!");
+            return false;
+        }
+        RespiratoryTrainingImage->SetPosition(61, 122, 90, 90);
+        RespiratoryTrainingImage->SetAutoEnable(false);
+        RespiratoryTrainingImage->SetResizeMode(UIImageView::CENTER);
+        ImageInfo *imgInfo = ImageCacheManager::GetInstance().LoadSingleRes(RTCVectorImagePath);
+        if (imgInfo == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Load image failed!");
+            return false;
+        }
+        RespiratoryTrainingImage->SetSrc(imgInfo);
+        ScrollView->Add(RespiratoryTrainingImage);
+        /* -------------------------------------------------------------------------- */
+        ImageConfig imageconfig = {
+            .x = 89,
+            .y = 212,
+            .w = 42,
+            .h = 130,
+        };
+        // 加载呼吸训练Vector图片
+        imgInfo = ImageCacheManager::GetInstance().LoadSingleRes(RTCGroupListImagePath);
+        for (int i = 0; i < MAX_NUM; i++)
+        {
+            imageconfig.y = 212 + (i * 130);
+            RTCImageView[i] = ImageViewInit(imageconfig);
+            if (RTCImageView[i] == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RTCImageView[%d] is nullptr!", i);
+                return false;
+            }
+            RTCImageView[i]->SetSrc(imgInfo);
+            ScrollView->Add(RTCImageView[i]);
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedLabelButtonView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedLabelButtonView");
+        RTCLabelButton = new UILabelButtonExt();
+        if (RTCLabelButton == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RTCLabelButton is nullptr!");
+            return false;
+        }
+        RTCLabelButton->SetPosition(43, 622);
+        RTCLabelButton->Resize(380, 100);
+        RTCLabelButton->SetViewId(RTCLABELBUTTON);
+        RTCLabelButton->SetStyle(STYLE_BACKGROUND_COLOR, 0xff191919);
+        // RTCLabelButton->SetText("关闭");
+        RTCLabelButton->SetTextId(STR_CLOSE);
+        RTCLabelButton->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+        RTCLabelButton->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xff000000, UIButton::PRESSED);
+        RTCLabelButton->SetStyleForState(STYLE_BORDER_COLOR, 0xffffffff, UIButton::PRESSED);
+        RTCLabelButton->SetStyleForState(STYLE_BORDER_OPA, 0x00, UIButton::PRESSED);
+        UIView::OnClickListener *labelButtonClick = static_cast<UIView::OnClickListener *>(RespiratoryTrainingPresenter::GetInstance());
+        if (labelButtonClick == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "labelButtonClick is nullptr!");
+            return false;
+        }
+        RTCLabelButton->SetOnClickListener(labelButtonClick);
+        ScrollView->Add(RTCLabelButton);
+        return true;
+    }
+
+    // 呼吸训练完成视图初始化
+    UIScrollView *RespiratoryTrainingCompletedView::RespiratoryTrainingCompletedViewInit(void)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::RespiratoryTrainingCompletedViewInit");
+        if (!CreateRespiratoryTrainingCompletedScrollView() || !CreateRespiratoryTrainingCompletedLabelView() ||
+            !CreateRespiratoryTrainingCompletedImageView() || !CreateRespiratoryTrainingCompletedLabelButtonView())
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedViewInit fail!!");
+            return nullptr;
+        }
+        return ScrollView;
+    }
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingDurationSelectionView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingDurationSelectionView.cpp
new file mode 100644 (file)
index 0000000..eea5598
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-09-29 15:57:59
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-11-05 14:40:12
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/respiratorytraining/src/RespiratoryTrainingDurationSelectionView.cpp
+ */
+#include "respiratorytraining/include/RespiratoryTrainingDurationSelectionView.h"
+#include "respiratorytraining/include/RespiratoryTrainingPresenter.h"
+#include "respiratorytraining/include/RespiratoryTrainingModel.h"
+#include "wearable_log.h"
+#include "UiConfig.h"
+#include "dock/focus_manager.h"
+#include "ui_resource_string.h"
+namespace OHOS
+{
+
+    static RespiratoryTrainingDurationSelectionView *g_pRespiratoryTrainingDurationSelectionView{nullptr};
+    RespiratoryTrainingDurationSelectionView::RespiratoryTrainingDurationSelectionView(/* args */)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingDurationSelectionView::RespiratoryTrainingDurationSelectionView");
+        g_pRespiratoryTrainingDurationSelectionView = this;
+    }
+
+    RespiratoryTrainingDurationSelectionView::~RespiratoryTrainingDurationSelectionView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingDurationSelectionView::~RespiratoryTrainingDurationSelectionView");
+        ClearAll();
+        g_pRespiratoryTrainingDurationSelectionView = nullptr;
+    }
+
+    RespiratoryTrainingDurationSelectionView *RespiratoryTrainingDurationSelectionView::GetInstance(void)
+    {
+        return g_pRespiratoryTrainingDurationSelectionView;
+    }
+
+    void RespiratoryTrainingDurationSelectionView::ClearAll(void)
+    {
+        if (scrollView)
+        {
+            scrollView->RemoveAll();
+            delete TitleLabel;
+            TitleLabel = nullptr;
+            for (int i = 0; i < MAX_NUM_TRAINING_DURATION; i++)
+            {
+                delete DurationLabel[i];
+                DurationLabel[i] = nullptr;
+                delete DurationButton[i];
+                DurationButton[i] = nullptr;
+            }
+            delete temp;
+            temp = nullptr;
+            delete scrollView;
+            scrollView = nullptr;
+        }
+    }
+
+    UILabelExt *RespiratoryTrainingDurationSelectionView::LabelViewinit(LabelConfig &labelconfig)
+    {
+        UILabelExt *label = new UILabelExt();
+        if (label == nullptr)
+        {
+            return nullptr;
+        }
+        label->SetPosition(labelconfig.left, labelconfig.top, labelconfig.width, labelconfig.height);
+        label->SetStyle(STYLE_TEXT_FONT, labelconfig.fontSize);
+        label->SetStyle(STYLE_LINE_HEIGHT, labelconfig.lineHeigh);
+        label->SetTextId(labelconfig.textId);
+        // label->SetText(labelconfig.text);
+        label->SetLineBreakMode(labelconfig.lineBreakMode);
+        label->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+        label->SetRollSpeed(23);
+        label->SetFont(DEFAULT_VECTOR_FONT_FILENAME, labelconfig.fontSize);
+        return label;
+    }
+
+    UILabelButtonExt *RespiratoryTrainingDurationSelectionView::LabelButtonViewInit(LabelButtonConfig &labelbuttonconfig)
+    {
+        UILabelButtonExt *labelButton = new UILabelButtonExt();
+        if (labelButton == nullptr)
+        {
+            return nullptr;
+        }
+        labelButton->SetPosition(labelbuttonconfig.left, labelbuttonconfig.top);
+        labelButton->Resize(labelbuttonconfig.width, labelbuttonconfig.height);
+        labelButton->SetViewId(labelbuttonconfig.viewId);
+        labelButton->SetStyle(STYLE_BACKGROUND_COLOR, 0xff191919);
+        labelButton->SetImagePosition(300, 30);
+        labelButton->SetImageSrc(PNG_CHECKBOX_OFF_IMAGE, PNG_CHECKBOX_OFF_IMAGE);
+        // labelButton->SetTextId(labelbuttonconfig.textId);
+        // labelButton->SetAlign(TEXT_ALIGNMENT_LEFT);
+        // labelButton->SetLabelPosition(100, 0);
+        // labelButton->SetFont(DEFAULT_VECTOR_FONT_FILENAME, labelbuttonconfig.fontSize);
+        // labelButton->SetStyle(STYLE_LINE_HEIGHT, labelbuttonconfig.lineHeigh);
+        labelButton->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xff000000, UIButton::PRESSED);
+        return labelButton;
+    }
+
+    UIScrollView *RespiratoryTrainingDurationSelectionView::RespiratoryTrainingDurationSelectionViewInit(void)
+    {
+        if (scrollView == nullptr)
+        {
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThemeStyleView::InitThemeStyleScrollView");
+            scrollView = new UIScrollView();
+            if (scrollView == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "scrollView new fail!");
+                return nullptr;
+            }
+            scrollView->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+            scrollView->SetViewId(RESPIRATORYTRAINING_DURATIONSELECTION_VIEW);
+            scrollView->SetScrollBlankSize(30);
+            scrollView->SetReboundSize(30);
+            scrollView->SetDirection(1);
+            scrollView->SetTouchable(true);
+            scrollView->SetDraggable(true);
+            scrollView->SetThrowDrag(true);         // 设置手指抬起屏幕后是否继续滚动。
+            scrollView->SetMaxScrollDistance(1000); // 设置手指抬起屏幕后的最大滚动距离。
+            scrollView->SetSwipeACCLevel(200);      /*设置手指抬起屏幕后的补偿距离。*/
+            scrollView->SetRotateFactor(1.0);
+            FocusManager::GetInstance()->RequestFocus(scrollView);
+            FocusManager::GetInstance()->RequestFocusByDirection(FOCUS_DIRECTION_UP);
+            UIView::OnDragListener *dragListener = static_cast<UIView::OnDragListener *>(RespiratoryTrainingPresenter::GetInstance());
+            if (dragListener == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "dragListener is nullptr!");
+                return nullptr;
+            }
+            scrollView->SetOnDragListener(dragListener);
+            /* -------------------------------------------------------------------------- */
+            TitleLabel = new UILabelExt();
+            if (TitleLabel == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TitleLabel new fail!");
+                return nullptr;
+            }
+            TitleLabel->SetPosition(83, 50, 300, 35);
+            TitleLabel->SetTextId(STR_DURATION_SETTINGS); // 时长设置
+            TitleLabel->SetStyle(STYLE_TEXT_FONT, 30);
+            TitleLabel->SetStyle(STYLE_LINE_HEIGHT, 35);
+            TitleLabel->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);
+            TitleLabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+            TitleLabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+            scrollView->Add(TitleLabel);
+            /* -------------------------------------------------------------------------- */
+            UIView::OnClickListener *clickListener = static_cast<UIView::OnClickListener *>(RespiratoryTrainingPresenter::GetInstance());
+            if (clickListener == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "clickListener is nullptr!");
+                return nullptr;
+            }
+            LabelButtonConfig labelbuttonconfig;
+            const char *viewId[MAX_NUM_TRAINING_DURATION] = {TRAINING_DURATION_ONE_MIN, TRAINING_DURATION_TWO_MIN, TRAINING_DURATION_THREE_MIN, TRAINING_DURATION_FOUR_MIN, TRAINING_DURATION_FIVE_MIN};
+            for (int i = 0; i < MAX_NUM_TRAINING_DURATION; i++)
+            {
+                labelbuttonconfig.left = 43;
+                labelbuttonconfig.top = 120 + 120 * i;
+                labelbuttonconfig.width = 380;
+                labelbuttonconfig.height = 100;
+                labelbuttonconfig.viewId = viewId[i];
+                DurationButton[i] = LabelButtonViewInit(labelbuttonconfig);
+                if (DurationButton[i] == nullptr)
+                {
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "DurationButton[%d] is nullptr", i);
+                    return nullptr;
+                }
+                DurationButton[i]->SetOnClickListener(clickListener);
+                scrollView->Add(DurationButton[i]);
+            }
+            /* -------------------------------------------------------------------------- */
+            int16_t textId[MAX_NUM_TRAINING_DURATION] = {STR_1_MIN, STR_2_MIN,STR_3_MIN,STR_4_MIN, STR_5_MIN};
+            // const char *text[MAX_NUM_TRAINING_DURATION] = {"1分钟", "2分钟", "3分钟", "4分钟", "5分钟"};
+            LabelConfig labelConfig = {
+                .left = 103,
+                .top = 153,
+                .width = 210,
+                .height = 35,
+                .fontSize = 30,
+                .lineHeigh = 35,
+                .textId = 0,
+                .lineBreakMode = UILabel::LINE_BREAK_OSCILLATION,
+            };
+            for (int16_t i = 0; i < MAX_NUM_TRAINING_DURATION; i++)
+            {
+                labelConfig.top = 153 + 120 * i;
+                labelConfig.textId = textId[i];
+                // labelConfig.text = text[i];
+                DurationLabel[i] = LabelViewinit(labelConfig);
+                if (DurationLabel[i] == nullptr)
+                {
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "DurationLabel[%d] is nullptr", i);
+                    return nullptr;
+                }
+                scrollView->Add(DurationLabel[i]);
+            }
+            /* -------------------------------------------------------------------------- */
+            if (temp == nullptr)
+            {
+                temp = new UIImageView();
+                if (temp == nullptr)
+                {
+                    return nullptr;
+                }
+                int y = DurationButton[MAX_NUM_TRAINING_DURATION - 1]->GetY() + DurationButton[MAX_NUM_TRAINING_DURATION - 1]->GetHeight();
+                temp->SetPosition(43, y, 380, 40);
+                temp->SetVisible(false);
+                scrollView->Add(temp);
+            }
+        }
+        UpdateRespiratoryTrainingDurationSelectionView();
+        return scrollView;
+    }
+
+    void RespiratoryTrainingDurationSelectionView::UpdateRespiratoryTrainingDurationSelectionView(void)
+    {
+        for (int i = 0; i < MAX_NUM_TRAINING_DURATION; i++)
+        {
+            if (i == RespiratoryTrainingModel::GetInstance().GetRespiratoryTrainingSelectTime() - 1)
+            {
+                DurationButton[i]->SetImageSrc(PNG_CHECKBOX_ON_IMAGE, PNG_CHECKBOX_ON_IMAGE);
+            }
+            else
+            {
+                DurationButton[i]->SetImageSrc(PNG_CHECKBOX_OFF_IMAGE, PNG_CHECKBOX_OFF_IMAGE);
+            }
+            DurationButton[i]->Invalidate();
+        }
+    }
+}
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingModel.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingModel.cpp
new file mode 100644 (file)
index 0000000..044b28e
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-06-11 16:26:20
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-11-05 14:40:25
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/respiratorytraining/src/RespiratoryTrainingModel.cpp
+ */
+#include "respiratorytraining/include/RespiratoryTrainingModel.h"
+#include "wearable_log.h"
+#include <cmsis_os2.h>
+#include <cmsis_os.h>
+#include <ohos_types.h>
+#include <service/graphic_service.h>
+#include <iostream>
+#include "respiratorytraining/include/RespiratoryTrainingView.h"
+#include <custom_utils.h>
+#include "gfx_utils/mem_check.h"
+#include "ConfigModel.h"
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <thread>
+#include <unordered_map>
+#include <custom_runtime.h>
+#ifdef FD230_650
+#include "heartrate/include/HeartModel.h"
+#endif
+namespace OHOS
+{
+    // 声明定时器变量
+    static osTimerId_t g_timer = NULL;
+    static constexpr uint64_t ONE_MIN = 60000 * 1;   // 1分钟
+    static constexpr uint64_t TWO_MIN = 60000 * 2;   // 2分钟
+    static constexpr uint64_t THREE_MIN = 60000 * 3; // 3分钟
+    static constexpr uint64_t FOUR_MIN = 60000 * 4;  // 4分钟
+    static constexpr uint64_t FIVE_MIN = 60000 * 5;  // 5分钟
+
+    RespiratoryTrainingModel::RespiratoryTrainingModel() {}
+
+    RespiratoryTrainingModel::~RespiratoryTrainingModel() {}
+
+    RespiratoryTrainingModel &RespiratoryTrainingModel::GetInstance()
+    {
+        static RespiratoryTrainingModel instance;
+        return instance;
+    }
+
+    // 获取训练时长,单位min
+    int RespiratoryTrainingModel::GetRespiratoryTrainingSelectTime()
+    {
+        return ConfigModel::GetInstance()->get_configData_int(ConfigModel::CONFIGMODEL_ITEM_RESPIRATORY_TRAINING_DURATION_SELECTION);
+    }
+
+    // 设置训练时长,单位min
+    void RespiratoryTrainingModel::SetRespiratoryTrainingSelectTime(int time)
+    {
+        ConfigModel::GetInstance()->set_condigData_Int(ConfigModel::CONFIGMODEL_ITEM_RESPIRATORY_TRAINING_DURATION_SELECTION, time);
+    }
+
+    uint64_t RespiratoryTrainingModel::GetRespiratoryTrainingTime()
+    {
+        int time = RespiratoryTrainingModel::GetInstance().GetRespiratoryTrainingSelectTime();
+        uint64_t time_;
+        switch (time)
+        {
+        case 1:
+            time_ = ONE_MIN;
+            break;
+        case 2:
+            time_ = TWO_MIN;
+            break;
+        case 3:
+            time_ = THREE_MIN;
+            break;
+        case 4:
+            time_ = FOUR_MIN;
+            break;
+        case 5:
+            time_ = FIVE_MIN;
+            break;
+        default:
+            time_ = ONE_MIN;
+            break;
+        }
+        return time_;
+    }
+
+    // 每进行一次呼吸训练,呼吸训练次数自加1
+    void RespiratoryTrainingModel::AddRespiratoryTrainingTimes()
+    {
+        RespiratoryTrainingTimes += 1;
+    }
+
+    // 获取呼吸训练总时间
+    std::string RespiratoryTrainingModel::GetRespiratoryTrainingTotalTime(void)
+    {
+        RespiratoryTrainingTime += GetRespiratoryTrainingSelectTime();
+        return (std::to_string(RespiratoryTrainingTime) + "min");
+    }
+
+    // 获取平均心率值
+    int RespiratoryTrainingModel::GetAverageHeartRate(void)
+    {
+        if (count != 0)
+        {
+            return heartTotal / count;
+        }
+        return 0;
+    }
+
+    // 清除当日的呼吸训练时间
+    void RespiratoryTrainingModel::ClearTotalRespiratoryTrainingTime(void)
+    {
+        if (RespiratoryTrainingTime != 0)
+        {
+            RespiratoryTrainingTime = 0;
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ClearTotalRespiratoryTrainingTime success!!");
+        }
+    }
+
+    void RespiratoryTrainingModel::calculateHeartrateTotal(void)
+    {
+        // 获取当前时间(以微秒为单位)
+        uint32_t currentTime = static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
+
+        // 记录和检查上一次调用的时间
+        static uint32_t lastCallTime = 0;
+        if (currentTime - lastCallTime < 1000000)
+        {
+            // 小于1秒
+            return;
+        }
+        lastCallTime = currentTime; // 更新最后调用时间
+
+#ifdef FD230_650
+        uint16_t heartrate = HeartModel::GetInstance()->GetShowHeartdata();
+
+        if (heartrate != 0)
+        {
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "heartrate = %d", heartrate);
+            heartTotal += heartrate;
+            count++;
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "heartTotal = %0.f,count = %d,h_rate = %d", heartTotal, count, heartrate);
+        }
+#endif
+    }
+
+    void RespiratoryTrainingModel::ResumeStatus()
+    {
+        heartTotal = 0;
+        count = 0;
+#ifdef FD230_650
+        HeartModel::GetInstance()->Heart_Control(HeartState::End, HeartCheck::INITIATIVE); // 关闭心率检测
+#endif
+        uapi_dev_runtime_set_respiratory_training_status(0); // 如果直接退出也要设置为0
+    }
+    /* -------------------------------------------------------------------------- */
+    /*定时器到期时的处理*/
+    static void RT_TimerCallbackProc(int data)
+    {
+        RespiratoryTrainingView *p_RespiratoryTrainingView = RespiratoryTrainingView::GetInstance();
+        if (p_RespiratoryTrainingView == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "p_RespiratoryTrainingView is nullptr!!");
+            return;
+        }
+        uapi_dev_runtime_set_respiratory_training_status(0);                   // 训练结束runtime设置为0
+        RespiratoryTrainingModel::GetInstance().RT_StopTimer();                // 停止定时器
+        RespiratoryTrainingModel::GetInstance().AddRespiratoryTrainingTimes(); // 呼吸训练次数加1
+        p_RespiratoryTrainingView->GetRespiratoryTrainingAnimator_()->Stop();  // 关闭呼吸动画
+#ifdef FD230_650
+        HeartModel::GetInstance()->Heart_Control(HeartState::End, HeartCheck::INITIATIVE); // 关闭心率检测
+#endif
+        p_RespiratoryTrainingView->ChangeView(RESPIRATORYTRAINING, RESPIRATORYTRAININGCOMPLETED);
+        ScreenAlwaysOnCtr(CANCEL_SCREEN_ALWAYS_ON); /*取消常亮屏*/
+        return;
+    }
+
+    /*
+     * @brief 回调函数
+     */
+    static void RT_TimerCallback(int data)
+    {
+        GraphicService::GetInstance()->PostGraphicEvent(std::bind(RT_TimerCallbackProc, data));
+    }
+
+    /*
+     *  @brief 开启定时器函数
+     *  @param interval 时间间隔,单位ms
+     */
+    void RespiratoryTrainingModel::RT_StartTimer(uint64_t interval)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingModel::RT_StartTimer");
+        /* -------------------------- 1.检查定时器是否已经存在,如果存在则返回 ------------------------- */
+        if (g_timer != NULL)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Timer already exists!!");
+            return;
+        }
+
+        /* --------------------------------- 2.创建定时器 -------------------------------- */
+        g_timer = osTimerNew((osTimerFunc_t)RT_TimerCallback, osTimerPeriodic, NULL, NULL);
+        // 检查定时器创建是否成功
+        if (g_timer == NULL)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Timer create failed!!");
+            return;
+        }
+
+        /* ----------------------------- 3.将毫秒转换为系统时钟节拍数 ---------------------------- */
+        int tick = osMs2Tick(interval);
+
+        /* --------------------------------- 4.启动定时器 -------------------------------- */
+        osStatus_t ret = osTimerStart(g_timer, tick);
+        /* -------------------- 5.检查定时器启动是否成功,如果启动失败,删除定时器并置为 NULL -------------------- */
+        if (ret != osOK)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "osTimerDelete failed, ret = %lx!!", ret);
+            if (g_timer != NULL)
+            {
+                ret = osTimerDelete(g_timer);
+            }
+            g_timer = NULL;
+            return;
+        }
+    }
+
+    /*
+     * @brief 停止定时器
+     */
+    void RespiratoryTrainingModel::RT_StopTimer(void)
+    {
+        // 检查定时器是否存在
+        if (g_timer != NULL)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingModel::RT_StopTimer");
+            osTimerStop(g_timer);
+            // 删除定时器并置为 NULL
+            osStatus_t ret = osTimerDelete(g_timer);
+            if (ret != osOK)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "osTimerDelete failed, ret = %lx!!", ret);
+            }
+            g_timer = NULL;
+        }
+    }
+}
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingPresenter.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingPresenter.cpp
new file mode 100644 (file)
index 0000000..26ba322
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-05-07 11:26:06
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-11-05 14:52:23
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/RespiratoryTraining/src/RespiratoryTrainingPresenter.cpp
+ */
+
+#include "AppViewIDs.h"
+#include "NativeAbility.h"
+#include "respiratorytraining/include/RespiratoryTrainingPresenter.h"
+#include "respiratorytraining/include/RespiratoryTrainingView.h"
+#include "common/key_code.h"
+#include <custom_utils.h>
+#include "UiConfig.h"
+#include <iostream>
+#include <string>
+#include "respiratorytraining/include/RespiratoryTrainingModel.h"
+#include "gfx_utils/mem_check.h"
+#include "map"
+#include "ui_resource_string.h"
+#include "NativeRegisterManager.h"
+namespace OHOS
+{
+    REGIST_MENU_PRO(VIEW_RESPIRATORY_TRAINING, RespiratoryTrainingView, RespiratoryTrainingPresenter,
+                    PNG_APPLIST_BREATHE_IMAGE_BIG, PNG_APPLIST_BREATHE_IMAGE, PNG_APPLIST_BREATHE_IMAGE, "呼吸训练", STR_BREATH_TRAINING);
+
+    // 当前Presenter属性实例化的内存指针
+    static RespiratoryTrainingPresenter *g_pRespiratoryTrainingPresenter = nullptr;
+
+    RespiratoryTrainingPresenter::RespiratoryTrainingPresenter()
+    {
+        g_pRespiratoryTrainingPresenter = this;
+#ifdef MEM_CHECK
+        MemCheck::GetInstance()->EnableLeakCheck(true);
+#endif
+    }
+
+    RespiratoryTrainingPresenter::~RespiratoryTrainingPresenter()
+    {
+        g_pRespiratoryTrainingPresenter = nullptr;
+#ifdef MEM_CHECK
+        MemCheck::GetInstance()->EnableLeakCheck(false);
+#endif
+    }
+
+    RespiratoryTrainingPresenter *RespiratoryTrainingPresenter::GetInstance()
+    {
+        return g_pRespiratoryTrainingPresenter;
+    }
+
+    void RespiratoryTrainingPresenter::OnStart()
+    {
+        // ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON);
+    }
+
+    void RespiratoryTrainingPresenter::OnPause(void)
+    {
+        Presenter<RespiratoryTrainingView>::OnPause();
+    }
+
+    void RespiratoryTrainingPresenter::OnResume(void)
+    {
+        Presenter<RespiratoryTrainingView>::OnResume();
+    }
+
+    void RespiratoryTrainingPresenter::OnStop(void)
+    {
+        ScreenAlwaysOnCtr(CANCEL_SCREEN_ALWAYS_ON);
+        RespiratoryTrainingModel::GetInstance().RT_StopTimer();
+        RespiratoryTrainingModel::GetInstance().ResumeStatus();
+    }
+
+    bool RespiratoryTrainingPresenter::OnClick(UIView &view, const ClickEvent &event)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingPresenter::OnClick");
+        std::string viewId = view.GetViewId();
+        if (viewId == RTCLABELBUTTON)
+        {
+            NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);
+            return true;
+        }
+        std::map<std::string, int> viewMap = {
+            {TRAINING_DURATION_ONE_MIN, 1},
+            {TRAINING_DURATION_TWO_MIN, 2},
+            {TRAINING_DURATION_THREE_MIN, 3},
+            {TRAINING_DURATION_FOUR_MIN, 4},
+            {TRAINING_DURATION_FIVE_MIN, 5},
+        };
+        auto it = viewMap.find(viewId);
+        if (it != viewMap.end())
+        {
+            if (viewId == it->first)
+            {
+                WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "select time = %d", it->second);
+                RespiratoryTrainingModel::GetInstance().SetRespiratoryTrainingSelectTime(it->second);
+                if (RespiratoryTrainingDurationSelectionView::GetInstance())
+                {
+                    RespiratoryTrainingDurationSelectionView::GetInstance()->UpdateRespiratoryTrainingDurationSelectionView();
+                }
+            }
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingPresenter::OnDrag(UIView &view, const DragEvent &event)
+    {
+        UNUSED(view);
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingPresenter::OnDrag");
+        if ((event.GetDeltaX() > X_DRAG_OFFSET) && (event.GetDeltaX() > (DOUBLES * MATH_ABS(event.GetDeltaY())))) // 拖动过程中判断是否是水平滑动
+        {
+            RespiratoryTrainingPresenter::X_DRAG_FLAG = true;
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingPresenter::OnDragEnd(UIView &view, const DragEvent &event)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingPresenter::OnDragEnd");
+        if (event.GetDragDirection() == DragEvent::DIRECTION_LEFT_TO_RIGHT && RespiratoryTrainingPresenter::X_DRAG_FLAG)
+        {
+            std::string viewId = view.GetViewId();
+            if (viewId == RESPIRATORYTRAINING_DURATIONSELECTION_VIEW)
+            {
+                view_->ChangeView(RESPIRATORY_TRAINING_DURATION_SELECTION, RESPIRATORYTRAINING);
+            }
+            else
+            {
+                NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);
+            }
+            RespiratoryTrainingPresenter::X_DRAG_FLAG = false;
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingPresenter::OnKeyAct(UIView &view, const KeyEvent &event) // 增加按键事件
+    {
+        UNUSED(view);
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingPresenter::OnKeyAct");
+        uint16_t keyID = event.GetKeyId();
+        uint16_t keyState = event.GetState();
+        // 如果是power键,且是亮屏状态,按键释放就返回上一级
+        if (keyID == static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_POWER) && keyState == 1 && uapi_get_screen_state() == 0)
+        {
+            NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);
+        }
+        return true;
+    }
+
+    int RespiratoryTrainingPresenter::GetRespiratoryTrainingSelectTime()
+    {
+        int time = RespiratoryTrainingModel::GetInstance().GetRespiratoryTrainingSelectTime();
+        int textId;
+        switch (time)
+        {
+        case 1:
+            textId = STR_ONE_MIN;
+            break;
+        case 2:
+            textId = STR_TWO_MIN;
+            break;
+        case 3:
+            textId = STR_THREE_MIN;
+            break;
+        case 4:
+            textId = STR_FOUR_MIN;
+            break;
+        case 5:
+            textId = STR_FIVE_MIN;
+            break;
+        default:
+            textId = STR_TWO_MIN;
+            break;
+        }
+        return textId;
+    }
+
+    std::string RespiratoryTrainingPresenter::GetRespiratoryTrainingSelectTime1()
+    {
+        int time = RespiratoryTrainingModel::GetInstance().GetRespiratoryTrainingSelectTime();
+        std::string text;
+        switch (time)
+        {
+        case 1:
+            text = "1min";
+            break;
+        case 2:
+            text = "2min";
+            break;
+        case 3:
+            text = "3min";
+            break;
+        case 4:
+            text = "4min";
+            break;
+        case 5:
+            text = "5min";
+            break;
+        default:
+            text = "1min";
+            break;
+        }
+        return text;
+    }
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/respiratorytraining/src/RespiratoryTrainingView.cpp
new file mode 100644 (file)
index 0000000..f3acf6b
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-05-07 11:25:58
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2025-02-24 17:34:43
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/RespiratoryTraining/src/RespiratoryTrainingView.cpp
+ */
+
+#include "respiratorytraining/include/RespiratoryTrainingPresenter.h"
+#include "respiratorytraining/include/RespiratoryTrainingView.h"
+#include "wearable_log.h"
+#include "common/image_cache_manager.h"
+#include "animator/easing_equation.h"
+#include "ui_resource_string.h"
+#include "respiratorytraining/include/RespiratoryTrainingModel.h"
+#include "heartrate/include/HeartModel.h"
+
+#include <custom_runtime.h>
+#include <custom_utils.h>
+
+#include "vibrator_service.h"
+
+namespace OHOS
+{
+
+    static constexpr uint16_t ONE_MIN = 60000; // 1分钟
+    static RespiratoryTrainingView *g_pRespiratoryTrainingView{nullptr};
+
+    RespiratoryTrainingView::RespiratoryTrainingView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::RespiratoryTrainingView");
+        g_pRespiratoryTrainingView = this;
+    }
+
+    RespiratoryTrainingView::~RespiratoryTrainingView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::~RespiratoryTrainingView");
+        ClearAll();
+        g_pRespiratoryTrainingView = nullptr;
+    }
+
+    RespiratoryTrainingView *RespiratoryTrainingView::GetInstance()
+    {
+        return g_pRespiratoryTrainingView;
+    }
+
+    bool RespiratoryTrainingView::ChangeRespiratoryTrainingView(int16_t thisView)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::ChangeRespiratoryTrainingView");
+        if (RespiratoryTrainingScrollView[thisView] == nullptr)
+        {
+            AddViewToRootContainer(RespiratoryTrainingScrollView[thisView]);
+        }
+        if (TrainingDurationSelectiontitlelabel)
+        {
+            TrainingDurationSelectiontitlelabel->SetTextId(presenter_->GetRespiratoryTrainingSelectTime());
+            TrainingDurationSelectiontitlelabel->ForceResetText();
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingView::ChangeRespiratoryTrainingCompletedView(int16_t thisView)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::ChangeRespiratoryTrainingCompletedView");
+        if (RespiratoryTrainingScrollView[thisView] == nullptr)
+        {
+            p_RespiratoryTrainingCompletedView = new RespiratoryTrainingCompletedView();
+            if (p_RespiratoryTrainingCompletedView == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "new scrollView[%d] fail", thisView);
+                return false;
+            }
+            UIScrollView *scrollView = p_RespiratoryTrainingCompletedView->RespiratoryTrainingCompletedViewInit();
+            if (scrollView == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedViewInit fail!!");
+                return false;
+            }
+            RespiratoryTrainingScrollView[thisView] = scrollView;
+            AddViewToRootContainer(RespiratoryTrainingScrollView[thisView]);
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingView::ChangeRespiratoryTrainingDurationSelectionView(int16_t thisView)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::ChangeRespiratoryTrainingCompletedView");
+        if (RespiratoryTrainingScrollView[thisView] == nullptr)
+        {
+            p_RespiratoryTrainingDurationSelectionView = new RespiratoryTrainingDurationSelectionView();
+            if (p_RespiratoryTrainingDurationSelectionView == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "new scrollView[%d] fail", thisView);
+                return false;
+            }
+            UIScrollView *scrollView = p_RespiratoryTrainingDurationSelectionView->RespiratoryTrainingDurationSelectionViewInit();
+            if (scrollView == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingDurationSelectionViewInit fail!!");
+                return false;
+            }
+            RespiratoryTrainingScrollView[thisView] = scrollView;
+            AddViewToRootContainer(RespiratoryTrainingScrollView[thisView]);
+        }
+        return true;
+    }
+
+    struct RespiratoryTrainingViewMapper
+    {
+        const int16_t viewId;
+        bool (RespiratoryTrainingView::*func)(int16_t thisView);
+    };
+
+    static const RespiratoryTrainingViewMapper ViewMapper[] = {
+        {RESPIRATORYTRAINING, &RespiratoryTrainingView::ChangeRespiratoryTrainingView},
+        {RESPIRATORYTRAININGCOMPLETED, &RespiratoryTrainingView::ChangeRespiratoryTrainingCompletedView},
+        {RESPIRATORY_TRAINING_DURATION_SELECTION, &RespiratoryTrainingView::ChangeRespiratoryTrainingDurationSelectionView},
+    };
+
+    bool RespiratoryTrainingView::ClickFunc(int16_t interface)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::ClickFunc");
+        int funcSize = sizeof(ViewMapper) / sizeof(ViewMapper[0]);
+        for (int i = 0; i < funcSize; i++)
+        {
+            if (interface == ViewMapper[i].viewId)
+            {
+                bool jug = (this->*(ViewMapper[i].func))(interface);
+                if (jug == false)
+                {
+                    return false;
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void RespiratoryTrainingView::ChangeView(int16_t hideView, int16_t showView)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::ChangeView");
+        if (hideView > MAX_RESPIRATORYTRAININGID)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Overstep ones bounds");
+            return;
+        }
+        RespiratoryTrainingScrollView[hideView]->SetVisible(false);
+        ClickFunc(showView);
+        RespiratoryTrainingScrollView[showView]->SetVisible(true);
+        Draw();
+    }
+
+    Animator *RespiratoryTrainingView::GetRespiratoryTrainingAnimator_()
+    {
+        if (RespiratoryTrainingAnimator_ == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingAnimator_ is nullptr!!");
+            return nullptr;
+        }
+        return RespiratoryTrainingAnimator_;
+    }
+
+    const static int16_t BUTTON_WIDTH = 320;
+    const static int16_t BUTTON_HEIGHT = 100;
+    const static int16_t INTERVAL_STEP = 80;
+    const Vector2<float> VIEW_CENTER = {120, 120};
+    static constexpr const char *RespiratoryTrainingImagePath = "/user/res/RespiratoryTraining/RespiratoryTraining.bin";
+    static constexpr const char *RespiratoryTrainingImageBGPath = "/user/res/RespiratoryTraining/RespiratoryTraining_BG.bin";
+
+    void RespiratoryTrainingCallback::Callback(UIView *view)
+    {
+        RespiratoryTrainingModel::GetInstance().calculateHeartrateTotal();
+        // 1.计算当前时间与开始时间的差值,用于控制动画的时间。
+        uint32_t currentTime = HALTick::GetInstance().GetTime();
+        curTime = currentTime - startTimestamp;
+
+        // 2.判断当前时间是否大于等于训练总时长,若是则进行下一步操作。
+        if (curTime >= durationTime)
+        {
+            prepare_count++;
+            // 计算动画缩放比例
+            scale = static_cast<float>(EasingEquation::QuadEaseInOut(startVal, endVal, durationTime, durationTime)) / durationTime;
+
+            // 切换起始值和结束值,更新下一次呼吸的状态。
+            std::swap(startVal, endVal);
+            startTimestamp = currentTime;
+
+            if (prepare_count >= 2)
+            {
+                // 只有在准备时间结束后才切换呼吸状态和文本
+                isBreathIn = !isBreathIn;
+                if (isBreathIn)
+                {
+                    // breath->SetText("现在吸气");
+                    breath->SetTextId(STR_INHALE_NOW);
+                }
+                else
+                {
+                    // breath->SetText("现在呼气");
+                    breath->SetTextId(STR_EXHALE_NOW);
+                }
+                // 震动提醒
+                ui_api_vibrate_once_func();
+                breath->ForceResetText();
+            }
+        }
+        else
+        {
+            // 继续进行呼吸动画的变化
+            scale = static_cast<float>(EasingEquation::QuadEaseInOut(startVal, endVal, curTime, durationTime)) / durationTime;
+        }
+
+        // 根据动画步长 step 更新图片的旋转角度 angle
+        angle += step;
+        if (angle <= -360)
+        {
+            angle = 0;
+        }
+
+        // 根据计算得到的缩放比例 scale 对图片进行缩放操作,并根据角度对图片进行旋转操作
+        img_->Scale({scale, scale}, VIEW_CENTER);
+        img_->Rotate(angle, VIEW_CENTER);
+        img_->Invalidate();
+    }
+
+    void RespiratoryTrainingView::HandStartRespiratoryTraining()
+    {
+        if (titlelabel && TrainingDurationSelectiontitlelabel && Image && RespiratoryTrainingStartButton && RespiratoryTrainingStartLabel)
+        {
+            titlelabel->SetVisible(false);
+            TrainingDurationSelectiontitlelabel->SetVisible(false);
+            Image->SetVisible(false);
+            RespiratoryTrainingStartButton->SetVisible(false);
+            RespiratoryTrainingStartLabel->SetVisible(false);
+        }
+
+        if (RespiratoryTrainingStartNote_ && RespiratoryTrainingImg_)
+        {
+            RespiratoryTrainingStartNote_->SetVisible(true);
+            RespiratoryTrainingImg_->SetVisible(true);
+        }
+        ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON);            // 开始训练后常亮屏
+        uapi_dev_runtime_set_respiratory_training_status(1); // 训练开始runtime设置为1
+        StartRespiratoryTraining();                          // 开始呼吸训练
+#ifdef FD230_650
+        HeartModel::GetInstance()->Heart_Control(HeartState::Start, HeartCheck::INITIATIVE); // 开启心率检测
+#endif
+        RespiratoryTrainingModel::GetInstance().RT_StartTimer(RespiratoryTrainingModel::GetInstance().GetRespiratoryTrainingTime()); // 开启定时器
+        ui_api_vibrate_once_func();                                                                                                  // 震动
+    }
+
+    bool RespiratoryTrainingView::OnClick(UIView &view, const ClickEvent &event)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::OnClick");
+        UNUSED(event);
+
+        if (&view == RespiratoryTrainingStartButton)
+        {
+            HandStartRespiratoryTraining();
+        }
+        else if (&view == TrainingDurationSelectiontitlelabel)
+        {
+            ChangeView(RESPIRATORYTRAINING, RESPIRATORY_TRAINING_DURATION_SELECTION);
+        }
+        return true;
+    }
+
+    // 开始呼吸训练
+    bool RespiratoryTrainingView::StartRespiratoryTraining()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::StartRespiratoryTraining");
+        if (!animateStart_)
+        {
+            RespiratoryTrainingAnimator_->Start();
+            animateStart_ = true;
+            if (RespiratoryTrainingStartNote_ != nullptr)
+            {
+                RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Remove(RespiratoryTrainingStartNote_);
+                delete RespiratoryTrainingStartNote_;
+                RespiratoryTrainingStartNote_ = nullptr;
+            }
+            RespiratoryTrainingLabel_ = new UILabelExt();
+            if (RespiratoryTrainingLabel_ == nullptr)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingLabel_ new fail");
+                return false;
+            }
+            RespiratoryTrainingLabel_->SetPosition(53, 320, 360, 70);
+            RespiratoryTrainingLabel_->SetTextId(STR_KEEP_STILL_FOCUS_ON_BREATHING); // 准备
+            RespiratoryTrainingLabel_->SetStyle(STYLE_TEXT_FONT, 30);
+            RespiratoryTrainingLabel_->SetStyle(STYLE_LINE_HEIGHT, 35);
+            RespiratoryTrainingLabel_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+            RespiratoryTrainingLabel_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+            RespiratoryTrainingLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+            RespiratoryTrainingLabel_->SetStyle(STYLE_BACKGROUND_OPA, OPA_TRANSPARENT);
+            RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(RespiratoryTrainingLabel_);
+            RespiratoryTrainingCallBack_->SetTextView(RespiratoryTrainingLabel_);
+            RespiratoryTrainingCallBack_->startTimestamp = HALTick::GetInstance().GetTime();
+            RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Invalidate();
+        }
+        return true;
+    }
+
+    bool RespiratoryTrainingView::InitRespiratoryTrainingElements()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::InitRespiratoryTrainingElements");
+        // 加载呼吸训练图片
+        ImageInfo *imgInfo = ImageCacheManager::GetInstance().LoadSingleRes(RespiratoryTrainingImagePath);
+        if (imgInfo == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Load image failed!");
+            return false;
+        }
+
+        // 创建并设置呼吸训练图片
+        RespiratoryTrainingImg_ = new UIImageView();
+        if (RespiratoryTrainingImg_ == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingImg_ new failed!");
+            return false;
+        }
+        RespiratoryTrainingImg_->SetSrc(imgInfo);
+        RespiratoryTrainingImg_->SetPosition(113, 70, 240, 240);
+        RespiratoryTrainingImg_->SetStyle(STYLE_IMAGE_OPA, OPA_OPAQUE);
+        RespiratoryTrainingImg_->SetStyle(STYLE_BACKGROUND_OPA, OPA_TRANSPARENT);
+        RespiratoryTrainingImg_->SetAutoEnable(false);
+        RespiratoryTrainingImg_->SetResizeMode(UIImageView::ImageResizeMode::CENTER);
+        RespiratoryTrainingImg_->SetVisible(false);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(RespiratoryTrainingImg_);
+        /* -------------------------------------------------------------------------- */
+        RespiratoryTrainingStartNote_ = new UILabelExt();
+        if (RespiratoryTrainingStartNote_ == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingStartNote_ new failed!");
+            return false;
+        }
+        RespiratoryTrainingStartNote_->SetPosition(53, 320, 360, 70);
+        RespiratoryTrainingStartNote_->SetStyle(STYLE_TEXT_FONT, 30);
+        RespiratoryTrainingStartNote_->SetStyle(STYLE_LINE_HEIGHT, 35);
+        // RespiratoryTrainingStartNote_->SetText("保持不动,将注意力集中在呼吸上");
+        RespiratoryTrainingStartNote_->SetTextId(STR_KEEP_STILL_FOCUS_ON_BREATHING);
+        RespiratoryTrainingStartNote_->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+        RespiratoryTrainingStartNote_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+        RespiratoryTrainingStartNote_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+        RespiratoryTrainingStartNote_->SetVisible(false);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(RespiratoryTrainingStartNote_);
+        return true;
+    }
+
+    bool RespiratoryTrainingView::InitRespiratoryTrainingElements2()
+    {
+        titlelabel = new UILabelExt();
+        if (titlelabel == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "titlelabel new failed!");
+            return false;
+        }
+        titlelabel->SetPosition(83, 50, 300, 35);
+        titlelabel->SetStyle(STYLE_TEXT_FONT, 30);
+        titlelabel->SetStyle(STYLE_LINE_HEIGHT, 35);
+        titlelabel->SetTextId(STR_BREATH_TRAINING);
+        titlelabel->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);
+        titlelabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+        titlelabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(titlelabel);
+        /* -------------------------------------------------------------------------- */
+        TrainingDurationSelectiontitlelabel = new UILabelExt();
+        if (TrainingDurationSelectiontitlelabel == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TrainingDurationSelectiontitlelabel new failed!");
+            return false;
+        }
+        TrainingDurationSelectiontitlelabel->SetPosition(83, 95, 300, 35);
+        TrainingDurationSelectiontitlelabel->SetStyle(STYLE_TEXT_FONT, 30);
+        TrainingDurationSelectiontitlelabel->SetStyle(STYLE_LINE_HEIGHT, 35);
+        TrainingDurationSelectiontitlelabel->SetTextId(presenter_->GetRespiratoryTrainingSelectTime());
+        TrainingDurationSelectiontitlelabel->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);
+        TrainingDurationSelectiontitlelabel->SetTextColor(Color::GetColorFromRGBA(0x66, 0x66, 0x66, 0xff));
+        TrainingDurationSelectiontitlelabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+        TrainingDurationSelectiontitlelabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+        TrainingDurationSelectiontitlelabel->SetTouchable(true);
+        TrainingDurationSelectiontitlelabel->SetOnClickListener(this);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(TrainingDurationSelectiontitlelabel);
+        /* -------------------------------------------------------------------------- */
+        Image = new UIImageView();
+        if (Image == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Image new failed!");
+            return false;
+        }
+        Image->SetSrc(RespiratoryTrainingImageBGPath);
+        Image->SetPosition(133, 133, 200, 200);
+        Image->SetStyle(STYLE_IMAGE_OPA, OPA_OPAQUE);
+        Image->SetStyle(STYLE_BACKGROUND_OPA, OPA_TRANSPARENT);
+        Image->SetAutoEnable(false);
+        Image->SetResizeMode(UIImageView::CENTER);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(Image);
+        /* -------------------------------------------------------------------------- */
+        RespiratoryTrainingStartButton = new UILabelButtonExt();
+        if (RespiratoryTrainingStartButton == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Image new failed!");
+            return false;
+        }
+        RespiratoryTrainingStartButton->SetPosition(93, 330, 280, 90);
+        RespiratoryTrainingStartButton->SetOnClickListener(this);
+        RespiratoryTrainingStartButton->SetStyle(STYLE_BACKGROUND_COLOR, 0xff4fc34c);
+        RespiratoryTrainingStartButton->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+        RespiratoryTrainingStartButton->SetStyleForState(STYLE_BACKGROUND_OPA, 0x00, UIButton::PRESSED);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(RespiratoryTrainingStartButton);
+        /* -------------------------------------------------------------------------- */
+        RespiratoryTrainingStartLabel = new UILabelExt();
+        if (RespiratoryTrainingStartLabel == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingStartLabel new failed!");
+            return false;
+        }
+        RespiratoryTrainingStartLabel->SetPosition(143, 358, 180, 35);
+        RespiratoryTrainingStartLabel->SetStyle(STYLE_TEXT_FONT, 30);
+        RespiratoryTrainingStartLabel->SetStyle(STYLE_LINE_HEIGHT, 35);
+        RespiratoryTrainingStartLabel->SetTextId(STR_START); // 开始
+        RespiratoryTrainingStartLabel->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);
+        RespiratoryTrainingStartLabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+        RespiratoryTrainingStartLabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->Add(RespiratoryTrainingStartLabel);
+        return true;
+    }
+
+    bool RespiratoryTrainingView::InitRespiratoryTrainingCallBackAndAnimator()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::InitRespiratoryTrainingCallBackAndAnimator");
+        RespiratoryTrainingCallBack_ = new RespiratoryTrainingCallback(RespiratoryTrainingImg_);
+        if (RespiratoryTrainingCallBack_ == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "callBack new fail");
+            return false;
+        }
+
+        RespiratoryTrainingAnimator_ = new Animator(RespiratoryTrainingCallBack_, RespiratoryTrainingImg_, 0, true);
+        if (RespiratoryTrainingAnimator_ == nullptr)
+        {
+            delete RespiratoryTrainingCallBack_;
+            RespiratoryTrainingCallBack_ = nullptr;
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Animator new fail");
+            return false;
+        }
+
+        animateStart_ = false;
+
+        return true;
+    }
+
+    bool RespiratoryTrainingView::InitRespiratoryTrainingScrollView()
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingCompletedView::CreateRespiratoryTrainingCompletedScrollView");
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING] = new UIScrollView();
+        if (RespiratoryTrainingScrollView[RESPIRATORYTRAINING] == nullptr)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ScrollView new fail!");
+            return false;
+        }
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());
+        // RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->SetScrollBlankSize(50);
+        // RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->SetReboundSize(35);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->SetHorizontalScrollState(false);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->SetTouchable(true);
+        RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->SetDraggable(true);
+        AddViewToRootContainer(RespiratoryTrainingScrollView[RESPIRATORYTRAINING]);
+
+        return true;
+    }
+
+    void RespiratoryTrainingView::RespiratoryTrainingViewInit(void)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RespiratoryTrainingView::RespiratoryTrainingViewInit");
+        if (!InitRespiratoryTrainingScrollView() || !InitRespiratoryTrainingElements() ||
+            !InitRespiratoryTrainingElements2() || !InitRespiratoryTrainingCallBackAndAnimator())
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "RecordListViewInit fail!!");
+            return;
+        }
+#if 0
+        StartRespiratoryTraining();                                                          // 开始呼吸训练
+        HeartModel::GetInstance()->Heart_Control(HeartState::Start, HeartCheck::INITIATIVE); // 开启心率检测
+        RespiratoryTrainingModel::GetInstance().RT_StartTimer(ONE_MIN);                      // 开启一个一分钟的定时器
+        ui_api_vibrate_once_func();                                                          // 震动提醒
+#endif
+    }
+
+    void RespiratoryTrainingView::ClearAll(void)
+    {
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "[RespiratoryTrainingView]ClearAll start");
+        if (RespiratoryTrainingScrollView[RESPIRATORYTRAINING] != nullptr)
+        {
+            RespiratoryTrainingScrollView[RESPIRATORYTRAINING]->RemoveAll();
+            delete RespiratoryTrainingScrollView[RESPIRATORYTRAINING];
+            RespiratoryTrainingScrollView[RESPIRATORYTRAINING] = nullptr;
+        }
+
+        if (RespiratoryTrainingAnimator_ != nullptr)
+        {
+            RespiratoryTrainingAnimator_->Stop();
+            delete RespiratoryTrainingAnimator_;
+            RespiratoryTrainingAnimator_ = nullptr;
+        }
+
+        if (RespiratoryTrainingCallBack_ != nullptr)
+        {
+            delete RespiratoryTrainingCallBack_;
+            RespiratoryTrainingCallBack_ = nullptr;
+        }
+
+        if (RespiratoryTrainingImg_ != nullptr)
+        {
+            ImageCacheManager::GetInstance().UnloadSingleRes(RespiratoryTrainingImagePath);
+            delete RespiratoryTrainingImg_;
+            RespiratoryTrainingImg_ = nullptr;
+        }
+
+        if (RespiratoryTrainingLabel_ != nullptr)
+        {
+            delete RespiratoryTrainingLabel_;
+            RespiratoryTrainingLabel_ = nullptr;
+        }
+        if (RespiratoryTrainingStartNote_ != nullptr)
+        {
+            delete RespiratoryTrainingStartNote_;
+            RespiratoryTrainingStartNote_ = nullptr;
+        }
+        if (titlelabel != nullptr)
+        {
+            delete titlelabel;
+            titlelabel = nullptr;
+        }
+        if (TrainingDurationSelectiontitlelabel != nullptr)
+        {
+            delete TrainingDurationSelectiontitlelabel;
+            TrainingDurationSelectiontitlelabel = nullptr;
+        }
+        if (Image != nullptr)
+        {
+            delete Image;
+            Image = nullptr;
+        }
+        if (RespiratoryTrainingStartButton != nullptr)
+        {
+            delete RespiratoryTrainingStartButton;
+            RespiratoryTrainingStartButton = nullptr;
+        }
+        if (RespiratoryTrainingStartLabel != nullptr)
+        {
+            delete RespiratoryTrainingStartLabel;
+            RespiratoryTrainingStartLabel = nullptr;
+        }
+
+        if (p_RespiratoryTrainingCompletedView != nullptr)
+        {
+            delete p_RespiratoryTrainingCompletedView;
+            p_RespiratoryTrainingCompletedView = nullptr;
+            RespiratoryTrainingScrollView[RESPIRATORYTRAININGCOMPLETED] = nullptr;
+        }
+
+        if (p_RespiratoryTrainingDurationSelectionView != nullptr)
+        {
+            delete p_RespiratoryTrainingDurationSelectionView;
+            p_RespiratoryTrainingDurationSelectionView = nullptr;
+            RespiratoryTrainingScrollView[RESPIRATORY_TRAINING_DURATION_SELECTION] = nullptr;
+        }
+
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "[RespiratoryTrainingView]ClearAll end!!");
+    }
+
+    void RespiratoryTrainingView::OnStart()
+    {
+        RespiratoryTrainingViewInit();
+    }
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/MainStopWatchPresenter.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/MainStopWatchPresenter.h
new file mode 100644 (file)
index 0000000..1152b15
--- /dev/null
@@ -0,0 +1,77 @@
+/*\r
+ * @Author: luozheng 1732036298@qq.com\r
+ * @Date: 2024-04-04 15:13:51\r
+ * @LastEditors: luozheng 1732036298@qq.com\r
+ * @LastEditTime: 2024-10-30 14:51:08\r
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/include/MainStopWatchPresenter.h\r
+ */\r
+#ifndef MAIN_STOPWATCH_PRESENTER_H\r
+#define MAIN_STOPWATCH_PRESENTER_H\r
+\r
+#include "Presenter.h"\r
+#include "View.h"\r
+#include "MainStopWatchView.h"\r
+#include <string>\r
+#include "ui_resource_string.h"\r
+#include "ModelListenerSample.h"\r
+/*秒表主视图*/\r
+namespace OHOS\r
+{\r
+    static constexpr int STOPWATCH_TIMER_DURATION = 10; // 定时器周期,每10毫秒定时器会触发一次\r
+    static constexpr int MAX_RECORD_LOOPS = 99;         // 最大记录圈数为99\r
+    static const char *MAX_RECORD_TIME = "99:59.99";    // 最高记录时间为99:59.99\r
+    typedef enum\r
+    {\r
+        MAIN_STOPWATCH = 0,              // 秒表主视图\r
+        STOPWATCH_RECORD_CYCLE_TO_LIMIT, // 到达秒表计时的最高记录圈数后弹出视图\r
+        STOPWATCH_RECORD_TIME_TO_LIMIT,  // 到达秒表最大的记录时间后弹出该视图\r
+        STOPWATCH_MAXVIEWID,             // 秒表的视图id数量\r
+    } StopWatchViewId;\r
+    class MainStopWatchView;\r
+    class MainStopWatchPresenter : public Presenter<MainStopWatchView>,\r
+                                   public UIView::OnClickListener,\r
+                                   public UIView::OnDragListener,\r
+                                   public RootView::OnKeyActListener,\r
+                                   public UICheckBox::OnChangeListener,\r
+                                   public ModelListenerSample\r
+    {\r
+    public:\r
+        MainStopWatchPresenter();\r
+        virtual ~MainStopWatchPresenter();\r
+        void OnStart() override;\r
+        void OnPause() override;\r
+        void OnResume() override;\r
+        void OnStop() override;\r
+        static MainStopWatchPresenter *GetInstance();\r
+        void SetCheckBoxAndButton(UICheckBox *checkbox, UIButton *button);\r
+        void SetLable(UILabel *label1, UILabel *label2);\r
+        void SetShowTimeLableStyle(UILabel *label, int x, int y, int TextFont, int LineHeight);\r
+        void SetList(UIList *list, TextAdapter *listadapter, List<const char *> *listdata);\r
+        void SetImage(UIImageView *image);\r
+\r
+    private:\r
+        // 因为继承了OnClickListener,所以有OnClick属性,至于参数,原函数也这样直接继承;\r
+        bool OnClick(UIView &view, const ClickEvent &event) override;\r
+        bool OnDrag(UIView &view, const DragEvent &event) override;\r
+        bool OnDragEnd(UIView &view, const DragEvent &event) override;\r
+        bool OnChange(UICheckBox::UICheckBoxState state) override;\r
+        bool OnKeyAct(UIView &view, const KeyEvent &event) override; // 增加按键事件\r
+        void Notify() override;\r
+    private:\r
+        UICheckBox *m_checkbox{nullptr};\r
+        UIButton *m_button{nullptr};\r
+        UILabel *m_stopWatchlabel{nullptr};\r
+        UILabel *m_showTimelabel{nullptr};\r
+        UIImageView *m_image{nullptr};\r
+        UIList *m_list = nullptr;\r
+        TextAdapter *m_listadapter = nullptr;\r
+        List<const char *> *m_listdata = nullptr;\r
+        uint64_t lastRecordTime = 0; // 上次记录时间\r
+        int RecordCycleNumber{0};    // 用来记录计时圈数\r
+        bool X_DRAG_FLAG{false};\r
+        bool isStartMark{false};\r
+        bool TimerIsRunning{false};\r
+    };\r
+} // namespace OHOS\r
+\r
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/MainStopWatchView.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/MainStopWatchView.h
new file mode 100644 (file)
index 0000000..ffbaadb
--- /dev/null
@@ -0,0 +1,82 @@
+/*\r
+ * @Author: luozheng 1732036298@qq.com\r
+ * @Date: 2024-04-04 15:13:51\r
+ * @LastEditors: luozheng 1732036298@qq.com\r
+ * @LastEditTime: 2025-02-24 17:21:50\r
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/include/MainStopWatchView.h\r
+ */\r
+#ifndef MAIN_STOPWATCH_VIEW_H\r
+#define MAIN_STOPWATCH_VIEW_H\r
+\r
+\r
+#include "View.h"\r
+#include "components/ui_view_group.h"\r
+#include "components/ui_scroll_view.h"\r
+#include "components/ui_label_ext.h"\r
+#include "components/ui_checkbox.h"\r
+#include "components/ui_button.h"\r
+#include "components/ui_list.h"\r
+#include "components/text_adapter.h"\r
+#include "stopwatch/include/StopWatchRecordCycleToLimitView.h"\r
+#include "stopwatch/include/StopWatchRecordTimeToLimitView.h"\r
+#include "stopwatch/include/StopWatchAdapter.h"\r
+/*秒表主视图*/\r
+namespace OHOS\r
+{\r
+\r
+    static const char *checkBox1selectedImg = "/user/res/countdown/COUNTDOWM_PLAY.bin";         // 播放\r
+    static const char *checkBox1unSelectedImg = "/user/res/countdown/COUNTDOWM_PAUSE.bin";      // 暂停\r
+    static const char *ResetOrMarkButtonImg_Reset1 = "/user/res/stopwatch/STOPWATH_RESET1.bin"; // 重置_灰色 图片\r
+    static const char *ResetOrMarkButtonImg_Reset2 = "/user/res/stopwatch/STOPWATH_RESET2.bin"; // 重置_白色 图片\r
+    static const char *ResetOrMarkButtonImg_Mark = "/user/res/stopwatch/STOPWATCH_MARK.bin";    // 标记 图片\r
+    static const char *PlayOrPausecheckBoxVIEWID = "PlayOrPausecheckBox";                       // 播放或暂停复选框视图ID\r
+    static const char *ResetOrMarkButton_RESET1_VIEWID = "ResetOrMarkButtonReset1";             // ResetOrMarkButtonReset1\r
+    static const char *ResetOrMarkButton_RESET2_VIEWID = "ResetOrMarkButtonReset2";             // ResetOrMarkButtonReset2\r
+    static const char *ResetOrMarkButton_MARK_VIEWID = "ResetOrMarkButtonMark";                 // ResetOrMarkButtonMark\r
+    static const char *CycleToLimitReturnButton_VIEWID = "CycleToLimitReturnButton";            // CycleToLimitReturnButton\r
+    static const char *TimeToLimitReturnButton_VIEWID = "TimeToLimitReturnButton";              // TimeToLimitReturnButton\r
+    class MainStopWatchPresenter;\r
+    class MainStopWatchView : public View<MainStopWatchPresenter>\r
+    {\r
+\r
+    public:\r
+        MainStopWatchView();\r
+        virtual ~MainStopWatchView();\r
+        static MainStopWatchView *GetInstance(void);\r
+\r
+        void OnStart() override;\r
+        void SetViewIsVisible(UIView *view, bool status);\r
+        bool ClickFunc(int16_t interface);\r
+        void ChangeView(int16_t hideView, int16_t showView);\r
+        bool ChangeMainStopWatch(int16_t thisView);\r
+        bool ChangeStopWatchRecordCycleLimit(int16_t thisView);\r
+        bool ChangeMainStopWatchRecordTimeLimit(int16_t thisView);\r
+        UILabelExt *GetShowTimelabel(void);\r
+        UICheckBox *GetPlayOrPausecheckBox(void);\r
+        void ChangeRecordTimeToLimitView(void);\r
+        UIScrollView *GetScrollView(int16_t index);\r
+        StopWatchAdapter *GetStopWatchAdapter(void);\r
+    private:\r
+        void InitMainStopWatchView(void);\r
+        void ClearAll(void);\r
+\r
+    private:\r
+        StopWatchRecordCycleToLimitView *p_StopWatchRecordCycleToLimitView{nullptr}; // 圈数到达上限后弹出该视图\r
+        StopWatchRecordTimeToLimitView *p_StopWatchRecordTimeToLimitView{nullptr};   // 计时时间到达上限后弹出该视图\r
+\r
+    private:\r
+        UIViewGroup *group = nullptr;\r
+        UIScrollView *scrollView[3] = {nullptr};\r
+        UILabelExt *stopWatchlabel = nullptr;      // 内容为“秒表”的标签\r
+        UILabelExt *showTimelabel = nullptr;       // 用来显示计时时间\r
+        UICheckBox *PlayOrPausecheckBox = nullptr; // 播放或暂停复选框\r
+        UIButton *ResetOrMarkbutton = nullptr;     // 重置或标记按钮\r
+        UIImageView *LineImage{nullptr};\r
+        UIList *list = nullptr;\r
+        TextAdapter *listadapter = nullptr;\r
+        List<const char *> *listdata = nullptr;\r
+\r
+        StopWatchAdapter *stopWatchAdapter{nullptr};\r
+    };\r
+} // namespace OHOS\r
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchAdapter.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchAdapter.h
new file mode 100644 (file)
index 0000000..8720b69
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-12-12 11:03:15
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-12-16 11:43:14
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/include/StopWatchAdapter.h
+ */
+
+#ifndef STOPWATCH_ADAPTER_H
+#define STOPWATCH_ADAPTER_H
+
+#include "components/ui_list.h"
+#include "components/ui_label.h"
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+#include "components/ui_image_view.h"
+#include "components/abstract_adapter.h"
+#include <list>
+#include <string>
+
+namespace OHOS
+{
+    typedef struct
+    {
+        std::string RecordCycleNumber;
+        std::string formattedTime;
+        std::string formattedDiff;
+        int curSelectItemViewIndex;
+    } StopWatchItem;
+
+    class StopWatchAdapter : public AbstractAdapter
+    {
+    public:
+        explicit StopWatchAdapter();
+        ~StopWatchAdapter() override;
+        uint16_t GetCount(void) override;
+        void ClearData(void);
+        void setSelectedIndex(int index);
+        UIView *GetView(UIView *inView, int16_t index) override;
+        void AddListItem(const StopWatchItem &item);
+
+    private:
+        std::list<const StopWatchItem> listData;
+        int curSelectItemViewIndex{-1};
+    };
+}
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchItemView.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchItemView.h
new file mode 100644 (file)
index 0000000..9f5f11a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-03-30 09:14:38
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-12-16 14:42:31
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/settings/include/SettingItemView.h
+ */
+
+#ifndef STOPWATCH_ITEM_VIEW_H
+#define STOPWATCH_ITEM_VIEW_H
+
+#include "components/ui_label_ext.h"
+#include "components/ui_view_group.h"
+#include "stopwatch/include/StopWatchAdapter.h"
+namespace OHOS
+{
+    class StopWatchItemView : public UIViewGroup
+    {
+    public:
+        explicit StopWatchItemView();
+        ~StopWatchItemView() override;
+        void SetItemInfo(const StopWatchItem itemInfo);
+        void SetUpChild();
+        void SetItemViewColor(bool flag);
+
+    private:
+        UILabel RecordCycleNumber;
+        UILabel formattedTime;
+        UILabel formattedDiff;
+    };
+}
+#endif
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchModel.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchModel.h
new file mode 100644 (file)
index 0000000..b2d2a77
--- /dev/null
@@ -0,0 +1,43 @@
+/*\r
+ * @Author: luozheng 1732036298@qq.com\r
+ * @Date: 2024-04-04 15:13:51\r
+ * @LastEditors: luozheng 1732036298@qq.com\r
+ * @LastEditTime: 2024-11-27 17:45:02\r
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/include/StopWatchModel.h\r
+ */\r
+#ifndef STOP_WATCH_MODEL_H\r
+#define STOP_WATCH_MODEL_H\r
+\r
+#include <iostream>\r
+#include <string>\r
+#include <memory>\r
+\r
+namespace OHOS\r
+{\r
+    \r
+    class StopWatchModel\r
+    {\r
+    public:\r
+        static std::shared_ptr<StopWatchModel> GetInstance(); // shared_ptr:当没有引用指向这个实例时会自动释放内存\r
+        ~StopWatchModel() {}\r
+        std::string DisplayStopWatchDuration();\r
+        void StopWatch_StartTimer(uint32_t interval);\r
+        void StopWatch_StopTimer(void);\r
+        void StopWatch_PauseTimer();\r
+        void StopWatch_ResumeTimer(uint32_t interval);\r
+        bool isReachMaxRecordTime(void);\r
+        \r
+\r
+    private:\r
+        // 将构造函数、拷贝构造函数和赋值操作符私有化\r
+        StopWatchModel() = default;\r
+        StopWatchModel(const StopWatchModel &) = delete;\r
+        StopWatchModel &operator=(const StopWatchModel &) = delete;\r
+\r
+    private:\r
+        std::string stopwatchDuration;\r
+    };\r
+\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchRecordCycleToLimitView.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchRecordCycleToLimitView.h
new file mode 100644 (file)
index 0000000..85d24ef
--- /dev/null
@@ -0,0 +1,39 @@
+/*\r
+ * @Author: luozheng 1732036298@qq.com\r
+ * @Date: 2024-04-04 15:13:51\r
+ * @LastEditors: luozheng 1732036298@qq.com\r
+ * @LastEditTime: 2024-04-07 16:07:11\r
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/include/StopWatchRecordCycleToLimitView.h\r
+ */\r
+#ifndef STOPWATCH_RECORD_CYCLE_TO_LIMIT_VIEW_H\r
+#define STOPWATCH_RECORD_CYCLE_TO_LIMIT_VIEW_H\r
+\r
+\r
+#include "View.h"\r
+#include "components/ui_view_group.h"\r
+#include "components/ui_scroll_view.h"\r
+#include "components/ui_label_ext.h"\r
+#include "components/ui_image_view.h"\r
+#include "components/ui_label_button_ext.h"\r
+/*弹窗视图:到达最高圈数99时弹出*/\r
+namespace OHOS\r
+{\r
+    class StopWatchRecordCycleToLimitView\r
+    {\r
+\r
+    public:\r
+        StopWatchRecordCycleToLimitView();\r
+        void ClearAll(void);\r
+        virtual ~StopWatchRecordCycleToLimitView();\r
+        static StopWatchRecordCycleToLimitView *GetInstance(void);\r
+        UIScrollView *InitStopWatchRecordCycleToLimitView(void);\r
+\r
+    private:\r
+        UIScrollView *scrollView = nullptr;\r
+        UILabelExt *Title = nullptr;\r
+        UIImageView *AttentionImage = nullptr;\r
+        UILabelExt *AttentionLabel = nullptr;\r
+        UILabelButtonExt *button = nullptr;\r
+    };\r
+} // namespace OHOS\r
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchRecordTimeToLimitView.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/include/StopWatchRecordTimeToLimitView.h
new file mode 100644 (file)
index 0000000..d5a6253
--- /dev/null
@@ -0,0 +1,39 @@
+/*\r
+ * @Author: luozheng 1732036298@qq.com\r
+ * @Date: 2024-04-04 15:13:51\r
+ * @LastEditors: luozheng 1732036298@qq.com\r
+ * @LastEditTime: 2024-04-07 16:28:41\r
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/include/StopWatchRecordTimeToLimitView.h\r
+ */\r
+#ifndef STOPWATCH_RECORD_TIME_TO_LIMIT_VIEW_H\r
+#define STOPWATCH_RECORD_TIME_TO_LIMIT_VIEW_H\r
+\r
+\r
+#include "View.h"\r
+#include "components/ui_view_group.h"\r
+#include "components/ui_scroll_view.h"\r
+#include "components/ui_label_ext.h"\r
+#include "components/ui_image_view.h"\r
+#include "components/ui_label_button_ext.h"\r
+/*弹窗视图:到达最高时间99:59.99时弹出*/\r
+namespace OHOS\r
+{\r
+    class StopWatchRecordTimeToLimitView\r
+    {\r
+    public:\r
+        StopWatchRecordTimeToLimitView();\r
+        virtual ~StopWatchRecordTimeToLimitView();\r
+        static StopWatchRecordTimeToLimitView *GetInstance(void);\r
+        UIScrollView *InitStopWatchRecordTimeToLimitView(void);\r
+        void ClearAll(void);\r
+\r
+    private:\r
+        UIScrollView *scrollView = nullptr;\r
+        UILabelExt *Title = nullptr;\r
+        UIImageView *AttentionImage = nullptr;\r
+        UILabelExt *AttentionLabel_1 = nullptr;\r
+        UILabelExt *AttentionLabel_2 = nullptr;\r
+        UILabelButtonExt *button = nullptr;\r
+    };\r
+} // namespace OHOS\r
+#endif
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/MainStopWatchPresenter.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/MainStopWatchPresenter.cpp
new file mode 100644 (file)
index 0000000..249a639
--- /dev/null
@@ -0,0 +1,377 @@
+#include "AppViewIDs.h"\r
+#include "NativeAbility.h"\r
+#include "UiConfig.h"\r
+#include "stopwatch/include/MainStopWatchPresenter.h"\r
+#include "stopwatch/include/StopWatchModel.h"\r
+#include "common/key_code.h"\r
+#include <custom_utils.h>\r
+/*秒表主视图*/\r
+#include "gfx_utils/mem_check.h"\r
+\r
+#include "NativeRegisterManager.h"\r
+namespace OHOS\r
+{\r
+    REGIST_MENU_PRO(VIEW_MAIN_STOPWATCH, MainStopWatchView, MainStopWatchPresenter,\r
+                    PNG_APPLIST_STOPWATCH_IMAGE_BIG, PNG_APPLIST_STOPWATCH_IMAGE, PNG_APPLIST_STOPWATCH_IMAGE, "秒表", STR_STOPWATCH);\r
+\r
+    // 当前Presenter属性实例化的内存指针\r
+    static MainStopWatchPresenter *g_pMainStopWatchPresenter = nullptr;\r
+\r
+    MainStopWatchPresenter::MainStopWatchPresenter()\r
+    {\r
+        g_pMainStopWatchPresenter = this;\r
+#ifdef MEM_CHECK\r
+        MemCheck::GetInstance()->EnableLeakCheck(true);\r
+#endif\r
+    }\r
+\r
+    MainStopWatchPresenter::~MainStopWatchPresenter()\r
+    {\r
+        StopWatchModel::GetInstance()->StopWatch_StopTimer(); // 退出删除并清理定时器资源\r
+        g_pMainStopWatchPresenter = nullptr;\r
+#ifdef MEM_CHECK\r
+        MemCheck::GetInstance()->EnableLeakCheck(false);\r
+#endif\r
+    }\r
+\r
+    MainStopWatchPresenter *MainStopWatchPresenter::GetInstance()\r
+    {\r
+        return g_pMainStopWatchPresenter;\r
+    }\r
+\r
+    void MainStopWatchPresenter::OnStart()\r
+    {\r
+        // ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON); // 长亮屏\r
+    }\r
+\r
+    void MainStopWatchPresenter::OnPause(void)\r
+    {\r
+\r
+        Presenter<MainStopWatchView>::OnPause();\r
+    }\r
+\r
+    void MainStopWatchPresenter::OnResume(void)\r
+    {\r
+\r
+        Presenter<MainStopWatchView>::OnResume();\r
+    }\r
+\r
+    void MainStopWatchPresenter::OnStop(void)\r
+    {\r
+        // ScreenAlwaysOnCtr(CANCEL_SCREEN_ALWAYS_ON); // 取消长亮屏\r
+    }\r
+\r
+    /// @brief 设置复选框和按钮\r
+    /// @param checkbox\r
+    /// @param button\r
+    void MainStopWatchPresenter::SetCheckBoxAndButton(UICheckBox *checkbox, UIButton *button)\r
+    {\r
+        m_checkbox = checkbox;\r
+        m_button = button;\r
+    }\r
+\r
+    /// @brief 设置标签\r
+    /// @param label1\r
+    /// @param label2\r
+    void MainStopWatchPresenter::SetLable(UILabel *label1, UILabel *label2)\r
+    {\r
+        m_stopWatchlabel = label1;\r
+        m_showTimelabel = label2;\r
+    }\r
+\r
+    /// @brief 设置显示时间的标签样式\r
+    /// @param label\r
+    /// @param x\r
+    /// @param y\r
+    /// @param TextFont\r
+    /// @param LineHeight\r
+    void MainStopWatchPresenter::SetShowTimeLableStyle(UILabel *label, int x, int y, int TextFont, int LineHeight)\r
+    {\r
+        label->SetPosition(x, y, 466, LineHeight);\r
+        label->SetStyle(STYLE_TEXT_FONT, TextFont);\r
+        label->SetStyle(STYLE_LINE_HEIGHT, LineHeight);\r
+        label->SetFont(DEFAULT_VECTOR_FONT_FILENAME_EN, TextFont);\r
+        label->SetLineBreakMode(UILabel::LINE_BREAK_CLIP);\r
+        label->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+        label->Invalidate();\r
+    }\r
+\r
+    void MainStopWatchPresenter::SetList(UIList *list, TextAdapter *listadapter, List<const char *> *listdata)\r
+    {\r
+        m_list = list;\r
+        m_listadapter = listadapter;\r
+        m_listdata = listdata;\r
+    }\r
+\r
+    void MainStopWatchPresenter::SetImage(UIImageView *image)\r
+    {\r
+        m_image = image;\r
+    }\r
+\r
+    bool MainStopWatchPresenter::OnDrag(UIView &view, const DragEvent &event)\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchPresenter::OnDrag");\r
+        UNUSED(view);\r
+        if ((event.GetDeltaX() > X_DRAG_OFFSET) && (event.GetDeltaX() > (DOUBLES * MATH_ABS(event.GetDeltaY())))) // 拖动过程中判断是否是水平滑动\r
+        {\r
+            MainStopWatchPresenter::X_DRAG_FLAG = true;\r
+        }\r
+        return true;\r
+    }\r
+\r
+    bool MainStopWatchPresenter::OnDragEnd(UIView &view, const DragEvent &event)\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchPresenter::OnDragEnd");\r
+        UNUSED(view);\r
+        if (event.GetDragDirection() == DragEvent::DIRECTION_LEFT_TO_RIGHT && MainStopWatchPresenter::X_DRAG_FLAG)\r
+        {\r
+            if (&view == view_->GetScrollView(MAIN_STOPWATCH))\r
+            {\r
+                NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);\r
+            }\r
+            else if (&view == view_->GetScrollView(STOPWATCH_RECORD_CYCLE_TO_LIMIT))\r
+            {\r
+                view_->ChangeView(STOPWATCH_RECORD_CYCLE_TO_LIMIT, MAIN_STOPWATCH);\r
+            }\r
+            else if (&view == view_->GetScrollView(STOPWATCH_RECORD_TIME_TO_LIMIT))\r
+            {\r
+                view_->ChangeView(STOPWATCH_RECORD_TIME_TO_LIMIT, MAIN_STOPWATCH);\r
+            }\r
+            X_DRAG_FLAG = false;\r
+        }\r
+        return true;\r
+    }\r
+\r
+    /*\r
+     * Converts a time string in the format "mm:ss.ms" to milliseconds.\r
+     *\r
+     * @param timeStr The time string to convert.\r
+     * @return The time in milliseconds.\r
+     */\r
+    static unsigned long TimeStrToMilliseconds(const char *timeStr)\r
+    {\r
+        unsigned minutes, seconds, milliseconds;\r
+\r
+        // Assuming the format is "mm:ss.ms"\r
+        if (sscanf(timeStr, "%u:%u.%u", &minutes, &seconds, &milliseconds) == 3)\r
+        {\r
+            // 将分钟转换为毫秒,将秒转换为毫秒,并将它们相加\r
+            unsigned long totalMilliseconds = (minutes * 60 * 1000) + (seconds * 1000) + (milliseconds * 10);\r
+            return totalMilliseconds;\r
+        }\r
+        else\r
+        {\r
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Error: Invalid time format.");\r
+            return 0;\r
+        }\r
+    }\r
+\r
+    /*将毫秒级数转成格式为"mm:ss.ms"*/\r
+    static void MillisecondsToTimeStr(unsigned long milliseconds, char *buffer)\r
+    {\r
+        unsigned minutes = milliseconds / (60 * 1000);\r
+        milliseconds %= (60 * 1000); // Remaining milliseconds after converting to minutes\r
+        unsigned seconds = milliseconds / 1000;\r
+        unsigned ms = milliseconds % 1000; // Remaining milliseconds after converting to seconds\r
+\r
+        // Assuming ms needs to be in hundredths of a second for the ".ms" format\r
+        unsigned msHundredths = ms / 10; // Convert to hundredths of a second\r
+\r
+        // Write the formatted time string to the buffer\r
+        sprintf(buffer, "%02u:%02u.%02u", minutes, seconds, msHundredths);\r
+    }\r
+\r
+    bool MainStopWatchPresenter::OnClick(UIView &view, const ClickEvent &event)\r
+    {\r
+        UNUSED(view);\r
+        MainStopWatchView *mainStopWatchView = MainStopWatchView::GetInstance();\r
+        const char *clickedViewId = view.GetViewId();\r
+        if (strcmp(clickedViewId, ResetOrMarkButton_MARK_VIEWID) == 0) // 点击标记按钮\r
+        {\r
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "RecordCycleNumber:%d\n", RecordCycleNumber);\r
+            if (RecordCycleNumber >= MAX_RECORD_LOOPS) // 到达最高记录圈数\r
+            {\r
+                m_checkbox->SetState(UICheckBox::UNSELECTED);\r
+                mainStopWatchView->ChangeView(MAIN_STOPWATCH, STOPWATCH_RECORD_CYCLE_TO_LIMIT);\r
+                return true;\r
+            }\r
+            else\r
+            {\r
+                RecordCycleNumber += 1;\r
+            }\r
+\r
+            if (isStartMark == false) // 第一次标记\r
+            {\r
+                WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "isStartMark = %d", isStartMark);\r
+                mainStopWatchView->SetViewIsVisible(m_stopWatchlabel, false);\r
+                SetShowTimeLableStyle(m_showTimelabel, 0, 60, 65, 78);\r
+                mainStopWatchView->SetViewIsVisible(m_list, true);\r
+                mainStopWatchView->SetViewIsVisible(m_image, true);\r
+                isStartMark = true;\r
+            }\r
+\r
+            uint64_t currentRecordTime = TimeStrToMilliseconds(MainStopWatchView::GetInstance()->GetShowTimelabel()->GetText()); // 获取当前时间\r
+            uint64_t timeDifference = 0;                                                                                         // 时间差值\r
+            if (lastRecordTime > 0)\r
+            {\r
+                // 如果不是第一次标记,则计算与上一次记录的时间差\r
+                timeDifference = currentRecordTime - lastRecordTime;\r
+            }\r
+            // 将时间和时间差格式化为字符串,更新到UI\r
+            const char *formattedTime = MainStopWatchView::GetInstance()->GetShowTimelabel()->GetText();\r
+            char formattedDiff[64] = {0};\r
+            MillisecondsToTimeStr(timeDifference, formattedDiff);\r
+            // char *PickerData = (char *)malloc(128);\r
+            // if (lastRecordTime == 0) // 如果是第一次标记\r
+            // {\r
+            //     sprintf(PickerData, "%02d    %s    +%s", RecordCycleNumber, formattedTime, formattedTime);\r
+            // }\r
+            // else\r
+            // {\r
+            //     sprintf(PickerData, "%02d    %s    +%s", RecordCycleNumber, formattedTime, formattedDiff);\r
+            // }\r
+            // m_listdata->PushFront(const_cast<const char *>(PickerData));\r
+            // m_listadapter->SetData(m_listdata);\r
+            // m_list->SetAdapter(m_listadapter);\r
+            // char PickerData[128]={0};\r
+            // if (lastRecordTime == 0) // 如果是第一次标记\r
+            // {\r
+            //     sprintf(PickerData, "%02d    %s    +%s", RecordCycleNumber, formattedTime, formattedTime);\r
+            // }\r
+            // else\r
+            // {\r
+            //     sprintf(PickerData, "%02d    %s    +%s", RecordCycleNumber, formattedTime, formattedDiff);\r
+            // }\r
+\r
+            StopWatchItem item = {"", "", "", 0};\r
+            char buffer[3] = {0}; // 足够存放两位数字及字符串结束符'\0'\r
+            std::sprintf(buffer, "%02d", RecordCycleNumber);\r
+            item.RecordCycleNumber = buffer;\r
+            item.formattedTime = std::string(formattedTime);\r
+            ;\r
+            item.formattedDiff = "+" + std::string((lastRecordTime == 0) ? formattedTime : formattedDiff); // lastRecordTime == 0,如果是第一次标记\r
+            item.curSelectItemViewIndex = RecordCycleNumber - 1;\r
+\r
+            view_->GetStopWatchAdapter()->setSelectedIndex(RecordCycleNumber - 1);\r
+            view_->GetStopWatchAdapter()->AddListItem(item);\r
+            m_list->SetAdapter(view_->GetStopWatchAdapter());\r
+            m_list->SetStartIndex(RecordCycleNumber - 1);\r
+            m_list->SetSelectPosition(RecordCycleNumber);\r
+            m_list->RefreshList();\r
+            // 保存当前时间为“上一次记录时间”,为下一次标记做准备\r
+            lastRecordTime = currentRecordTime;\r
+            return true;\r
+        }\r
+        else if (strcmp(clickedViewId, ResetOrMarkButton_RESET2_VIEWID) == 0) // 点击重置按钮\r
+        {\r
+            StopWatchModel::GetInstance()->StopWatch_StopTimer(); // 停止定时器\r
+            mainStopWatchView->GetShowTimelabel()->SetText("00:00.00");\r
+            lastRecordTime = 0;\r
+            view_->GetStopWatchAdapter()->ClearData();\r
+            m_list->SetAdapter(nullptr);\r
+            m_list->RefreshList();\r
+\r
+            m_button->SetViewId(ResetOrMarkButton_RESET1_VIEWID);\r
+            m_button->SetImageSrc(ResetOrMarkButtonImg_Reset1, ResetOrMarkButtonImg_Reset1);\r
+            m_button->Invalidate();\r
+\r
+            if (isStartMark == true) // 重置标记\r
+            {\r
+                WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "isStartMark = %d", isStartMark);\r
+                mainStopWatchView->SetViewIsVisible(m_stopWatchlabel, true);\r
+                SetShowTimeLableStyle(m_showTimelabel, 0, 160, 84, 94);\r
+                mainStopWatchView->SetViewIsVisible(m_image, false);\r
+                mainStopWatchView->SetViewIsVisible(m_list, false);\r
+                isStartMark = false;\r
+            }\r
+\r
+            // if (m_listdata != nullptr)\r
+            // {\r
+            //     // 遍历列表,释放每个元素所占用的内存\r
+            //     ListNode<const char *> *node = m_listdata->Head();\r
+            //     int cnt = 0;\r
+            //     while (node != m_listdata->End())\r
+            //     {\r
+            //         WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "%d", ++cnt);\r
+            //         const char *p = node->data_;\r
+            //         free(const_cast<char *>(p));\r
+\r
+            //         node = m_listdata->Remove(node);\r
+            //     }\r
+            //     m_listdata->Clear();\r
+            // }\r
+            // else\r
+            // {\r
+            //     // Handle error or log the issue\r
+            //     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Error: m_listdata is nullptr");\r
+            // }\r
+            RecordCycleNumber = 0;\r
+        }\r
+        else if (strcmp(clickedViewId, CycleToLimitReturnButton_VIEWID) == 0) // 点击返回按钮\r
+        {\r
+            mainStopWatchView->ChangeView(STOPWATCH_RECORD_CYCLE_TO_LIMIT, MAIN_STOPWATCH);\r
+        }\r
+        else if (strcmp(clickedViewId, TimeToLimitReturnButton_VIEWID) == 0) // 点击返回按钮\r
+        {\r
+            mainStopWatchView->ChangeView(STOPWATCH_RECORD_TIME_TO_LIMIT, MAIN_STOPWATCH);\r
+        }\r
+        else if (strcmp(clickedViewId, PlayOrPausecheckBoxVIEWID) == 0) // 点击播放和暂停按钮\r
+        {\r
+            if (StopWatchModel::GetInstance()->isReachMaxRecordTime()) // 到达最大时间不能再计时\r
+            {\r
+                mainStopWatchView->ChangeView(MAIN_STOPWATCH, STOPWATCH_RECORD_TIME_TO_LIMIT);\r
+            }\r
+        }\r
+        return true;\r
+    }\r
+\r
+    bool MainStopWatchPresenter::OnChange(UICheckBox::UICheckBoxState state)\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchPresenter::OnChange,state = %d", state);\r
+        if (state == UICheckBox::UICheckBoxState::SELECTED)\r
+        {\r
+            StopWatchModel::GetInstance()->StopWatch_StartTimer(STOPWATCH_TIMER_DURATION);  // 开启定时器,如果定时器存在会直接返回\r
+            StopWatchModel::GetInstance()->StopWatch_ResumeTimer(STOPWATCH_TIMER_DURATION); // 恢复定时器\r
+\r
+            m_button->SetViewId(ResetOrMarkButton_MARK_VIEWID);\r
+            m_button->SetImageSrc(ResetOrMarkButtonImg_Mark, ResetOrMarkButtonImg_Mark);\r
+            m_button->Invalidate(); // 图片重新赋值要刷新该区域\r
+            TimerIsRunning = true;\r
+        }\r
+        else\r
+        {\r
+            StopWatchModel::GetInstance()->StopWatch_PauseTimer(); // 暂停定时器\r
+            m_button->SetViewId(ResetOrMarkButton_RESET2_VIEWID);\r
+            m_button->SetImageSrc(ResetOrMarkButtonImg_Reset2, ResetOrMarkButtonImg_Reset2);\r
+            m_button->Invalidate();\r
+            TimerIsRunning = false;\r
+        }\r
+        return true;\r
+    }\r
+\r
+    // 增加按键事件\r
+    bool MainStopWatchPresenter::OnKeyAct(UIView &view, const KeyEvent &event)\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchPresenter::OnKeyAct");\r
+        uint16_t keyID = event.GetKeyId();\r
+        uint16_t keyState = event.GetState();\r
+        // 如果是power键就返回上一级\r
+        if (keyID == static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_POWER) && keyState == 1 && uapi_get_screen_state() == 0)\r
+        {\r
+            NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);\r
+        }\r
+        return false;\r
+    }\r
+\r
+    void MainStopWatchPresenter::Notify()\r
+    {\r
+        if (view_->GetScrollView(MAIN_STOPWATCH) && view_->GetScrollView(MAIN_STOPWATCH)->IsVisible())\r
+        {\r
+            if (TimerIsRunning)\r
+            {\r
+                view_->GetShowTimelabel()->SetText(StopWatchModel::GetInstance()->DisplayStopWatchDuration().c_str());\r
+                view_->GetShowTimelabel()->Invalidate();\r
+            }\r
+        }\r
+    }\r
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/MainStopWatchView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/MainStopWatchView.cpp
new file mode 100644 (file)
index 0000000..547ddf7
--- /dev/null
@@ -0,0 +1,417 @@
+#include "stopwatch/include/MainStopWatchView.h"\r
+#include "stopwatch/include/MainStopWatchPresenter.h"\r
+#include "UiConfig.h"\r
+#include "NativeAbility.h"\r
+#include "AppViewIDs.h"\r
+#include "wearable_log.h"\r
+/*秒表主视图*/\r
+namespace OHOS\r
+{\r
+\r
+    static MainStopWatchView *g_pMainStopWatchView{nullptr};\r
+\r
+    MainStopWatchView::MainStopWatchView()\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchView::MainStopWatchView");\r
+        g_pMainStopWatchView = this;\r
+    }\r
+\r
+    MainStopWatchView::~MainStopWatchView()\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchView::~MainStopWatchView");\r
+        ClearAll();\r
+        g_pMainStopWatchView = nullptr;\r
+    }\r
+\r
+    MainStopWatchView *MainStopWatchView::GetInstance()\r
+    {\r
+        return g_pMainStopWatchView;\r
+    }\r
+\r
+    void MainStopWatchView::ClearAll()\r
+    {\r
+        if (group != nullptr)\r
+        {\r
+            group->RemoveAll();\r
+            scrollView[MAIN_STOPWATCH]->RemoveAll();\r
+\r
+            if (stopWatchlabel != nullptr)\r
+            {\r
+                delete stopWatchlabel;\r
+                stopWatchlabel = nullptr;\r
+            }\r
+\r
+            if (showTimelabel != nullptr)\r
+            {\r
+                delete showTimelabel;\r
+                showTimelabel = nullptr;\r
+            }\r
+\r
+            if (PlayOrPausecheckBox != nullptr)\r
+            {\r
+                delete PlayOrPausecheckBox;\r
+                PlayOrPausecheckBox = nullptr;\r
+            }\r
+\r
+            if (ResetOrMarkbutton != nullptr)\r
+            {\r
+                delete ResetOrMarkbutton;\r
+                ResetOrMarkbutton = nullptr;\r
+            }\r
+\r
+            if (LineImage != nullptr)\r
+            {\r
+                delete LineImage;\r
+                LineImage = nullptr;\r
+            }\r
+\r
+            if (stopWatchAdapter != nullptr)\r
+            {\r
+                delete stopWatchAdapter;\r
+                stopWatchAdapter = nullptr;\r
+            }\r
+\r
+            if (list != nullptr)\r
+            {\r
+                delete list;\r
+                list = nullptr;\r
+            }\r
+\r
+            if (listadapter != nullptr)\r
+            {\r
+                delete listadapter;\r
+                listadapter = nullptr;\r
+            }\r
+\r
+            if (listdata != nullptr)\r
+            {\r
+                listdata->Clear();\r
+                delete listdata;\r
+                listdata = nullptr;\r
+            }\r
+\r
+            if (scrollView[MAIN_STOPWATCH] != nullptr)\r
+            {\r
+                delete scrollView[MAIN_STOPWATCH];\r
+                scrollView[MAIN_STOPWATCH] = nullptr;\r
+            }\r
+\r
+            if (p_StopWatchRecordCycleToLimitView != nullptr)\r
+            {\r
+                delete p_StopWatchRecordCycleToLimitView;\r
+                p_StopWatchRecordCycleToLimitView = nullptr;\r
+            }\r
+\r
+            if (p_StopWatchRecordTimeToLimitView != nullptr)\r
+            {\r
+                delete p_StopWatchRecordTimeToLimitView;\r
+                p_StopWatchRecordTimeToLimitView = nullptr;\r
+            }\r
+\r
+            for (int i = MAIN_STOPWATCH; i < STOPWATCH_MAXVIEWID; i++)\r
+            {\r
+                scrollView[MAIN_STOPWATCH] = nullptr;\r
+            }\r
+\r
+            delete group;\r
+            group = nullptr;\r
+        }\r
+    }\r
+\r
+    /// @brief 设置视图是否可见\r
+    /// @param view 具体视图\r
+    /// @param status true 可见 false 不可见\r
+    void MainStopWatchView::SetViewIsVisible(UIView *view, bool status)\r
+    {\r
+        if (status == true)\r
+        {\r
+            view->SetVisible(true);\r
+        }\r
+        else\r
+        {\r
+            view->SetVisible(false);\r
+        }\r
+        view->Invalidate();\r
+    }\r
+\r
+    // 1.切换到秒表主视图\r
+    bool MainStopWatchView::ChangeMainStopWatch(int16_t thisView)\r
+    {\r
+        if (scrollView[thisView] == nullptr)\r
+        {\r
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchView::ChangeMainStopWatch");\r
+            group->Add(scrollView[thisView]);\r
+        }\r
+        return true;\r
+    }\r
+\r
+    // 2.切换到秒表到达最高圈数视图\r
+    bool MainStopWatchView::ChangeStopWatchRecordCycleLimit(int16_t thisView)\r
+    {\r
+        if (scrollView[thisView] == nullptr)\r
+        {\r
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchView::StopWatchRecordCycleToLimitView");\r
+            p_StopWatchRecordCycleToLimitView = new StopWatchRecordCycleToLimitView();\r
+            if (p_StopWatchRecordCycleToLimitView == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "MainStopWatchView:: new scrollView[%d] fail", thisView);\r
+                return false;\r
+            }\r
+            scrollView[thisView] = p_StopWatchRecordCycleToLimitView->InitStopWatchRecordCycleToLimitView();\r
+            group->Add(scrollView[thisView]);\r
+        }\r
+        return true;\r
+    }\r
+\r
+    // 3.切换到秒表到达最大时间计时视图\r
+    bool MainStopWatchView::ChangeMainStopWatchRecordTimeLimit(int16_t thisView)\r
+    {\r
+        if (scrollView[thisView] == nullptr)\r
+        {\r
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "MainStopWatchView::ChangeMainStopWatchRecordTimeLimit");\r
+            p_StopWatchRecordTimeToLimitView = new StopWatchRecordTimeToLimitView();\r
+            if (p_StopWatchRecordTimeToLimitView == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "MainStopWatchView:: new scrollView[%d] fail", thisView);\r
+                return false;\r
+            }\r
+            scrollView[thisView] = p_StopWatchRecordTimeToLimitView->InitStopWatchRecordTimeToLimitView();\r
+            group->Add(scrollView[thisView]);\r
+        }\r
+        return true;\r
+    }\r
+    /* -------------------------------------------------------------------------- */\r
+    // 1.MainStopWatchViewMapper:结构体定义了一个将视图ID映射到特定成员函数指针的映射表。\r
+    struct MainStopWatchViewMapper\r
+    {\r
+        const int16_t viewId;\r
+        bool (MainStopWatchView::*func)(int16_t thisView);\r
+    };\r
+\r
+    // 2.ViewMapper[] 是一个静态的视图映射数组,将视图ID与MainStopWatchView类中的特定成员函数指针进行了绑定。\r
+    static const MainStopWatchViewMapper ViewMapper[] = {\r
+        {MAIN_STOPWATCH, &MainStopWatchView::ChangeMainStopWatch},\r
+        {STOPWATCH_RECORD_CYCLE_TO_LIMIT, &MainStopWatchView::ChangeStopWatchRecordCycleLimit},\r
+        {STOPWATCH_RECORD_TIME_TO_LIMIT, &MainStopWatchView::ChangeMainStopWatchRecordTimeLimit},\r
+    };\r
+\r
+    // 3.根据传入的interface参数执行相应视图ID关联的成员函数。\r
+    bool MainStopWatchView::ClickFunc(int16_t interface)\r
+    {\r
+        int funcSize = sizeof(ViewMapper) / sizeof(ViewMapper[0]);\r
+        for (int i = 0; i < funcSize; i++)\r
+        {\r
+            if (interface == ViewMapper[i].viewId)\r
+            {\r
+                bool jug = (this->*(ViewMapper[i].func))(interface);\r
+                if (jug == false)\r
+                {\r
+                    return false;\r
+                }\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    // 4.切换视图\r
+    void MainStopWatchView::ChangeView(int16_t hideView, int16_t showView)\r
+    {\r
+        if (hideView > STOPWATCH_MAXVIEWID)\r
+        {\r
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "MainStopWatchView:: StopWatchViewId Overstep ones bounds");\r
+            return;\r
+        }\r
+        scrollView[hideView]->SetVisible(false);\r
+        ClickFunc(showView);\r
+        scrollView[showView]->SetVisible(true);\r
+        Draw();\r
+    }\r
+    /* -------------------------------------------------------------------------- */\r
+    // 初始化秒表主视图\r
+    void MainStopWatchView::InitMainStopWatchView()\r
+    {\r
+        if (group == nullptr)\r
+        {\r
+            /* ---------------------------------- group --------------------------------- */\r
+            group = new UIViewGroup();\r
+            if (group == nullptr)\r
+            {\r
+                return;\r
+            }\r
+            group->SetPosition(0, 0, Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight());\r
+            /* ----------------------- scrollView[MAIN_STOPWATCH] ----------------------- */\r
+            if (scrollView[MAIN_STOPWATCH] == nullptr)\r
+            {\r
+                scrollView[MAIN_STOPWATCH] = new UIScrollView();\r
+            }\r
+            scrollView[MAIN_STOPWATCH]->SetPosition(0, 0, 466, 466);\r
+            scrollView[MAIN_STOPWATCH]->SetStyle(STYLE_BACKGROUND_OPA, 0xff);\r
+            scrollView[MAIN_STOPWATCH]->SetTouchable(true);\r
+            scrollView[MAIN_STOPWATCH]->SetDraggable(true);\r
+            scrollView[MAIN_STOPWATCH]->SetOnDragListener(presenter_);\r
+            /* ----------------------------- stopWatchlabel ----------------------------- */\r
+            stopWatchlabel = new UILabelExt();\r
+            if (stopWatchlabel == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "stopWatchlabel new fail!!");\r
+                return;\r
+            }\r
+            stopWatchlabel->SetPosition(83, 50, 300, 35);\r
+            stopWatchlabel->SetStyle(STYLE_TEXT_FONT, 30);\r
+            stopWatchlabel->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            stopWatchlabel->SetTextId(STR_STOPWATCH); // 秒表\r
+            stopWatchlabel->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);\r
+            stopWatchlabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+            stopWatchlabel->SetRollSpeed(23);\r
+            stopWatchlabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            stopWatchlabel->SetVisible(true);\r
+            /* ------------------------------ showTimelabel ----------------------------- */\r
+            showTimelabel = new UILabelExt();\r
+            if (showTimelabel == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "showTimelabel new fail!!");\r
+                return;\r
+            }\r
+            showTimelabel->SetPosition(0, 160, 466, 94);\r
+            showTimelabel->SetStyle(STYLE_TEXT_FONT, 84);\r
+            showTimelabel->SetStyle(STYLE_LINE_HEIGHT, 94);\r
+            showTimelabel->SetText("00:00.00");\r
+            showTimelabel->SetLineBreakMode(UILabel::LINE_BREAK_CLIP);\r
+            showTimelabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+            showTimelabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME_EN, 84);\r
+            /* -------------------------------- LineImage ------------------------------- */\r
+            LineImage = new UIImageView();\r
+            if (LineImage == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LineImage new fail!!");\r
+                return;\r
+            }\r
+            LineImage->SetPosition(53, 150);\r
+            const char *imageimagesPath = "/user/res/stopwatch/STOPWATCH_LINE.bin";\r
+            LineImage->SetSrc(imageimagesPath);\r
+            LineImage->SetVisible(false);\r
+            /* ---------------------------- stopWatchAdapter ---------------------------- */\r
+            stopWatchAdapter = new StopWatchAdapter();\r
+            if (stopWatchAdapter == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "stopWatchAdapter new fail!!");\r
+                return;\r
+            }\r
+            /* ---------------------------------- list ---------------------------------- */\r
+            list = new UIList();\r
+            if (list == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "list new fail!!");\r
+                return;\r
+            }\r
+            list->SetPosition(56, 170, 360, 130);\r
+            list->SetStyle(STYLE_BACKGROUND_COLOR, 0xff000000);\r
+            list->SetReboundSize(30);\r
+            list->SetThrowDrag(true);\r
+            list->SetIntercept(true);\r
+            list->SetMaxScrollDistance(1000);\r
+            list->SetDragACCLevel(5);\r
+            list->SetSwipeACCLevel(200); /*设置手指抬起屏幕后的补偿距离。*/\r
+            list->SetVisible(false);\r
+            list->RequestFocus();\r
+            list->SetRotateFactor(1.0);\r
+            /* ------------------------------- listadapter ------------------------------ */\r
+            listadapter = new TextAdapter();\r
+            if (listadapter == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "listadapter new fail!!");\r
+                return;\r
+            }\r
+            listadapter->SetFont(DEFAULT_VECTOR_FONT_FILENAME_EN, 28);\r
+            listadapter->SetWidth(356);\r
+            listadapter->SetHeight(32);\r
+            listadapter->SetLineBreakMode(UILabel::LINE_BREAK_CLIP);\r
+            /* -------------------------------- listdata -------------------------------- */\r
+            listdata = new List<const char *>();\r
+            if (listdata == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "listdata new fail!!");\r
+                return;\r
+            }\r
+            /* --------------------------- PlayOrPausecheckBox -------------------------- */\r
+            PlayOrPausecheckBox = new UICheckBox();\r
+            if (PlayOrPausecheckBox == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "PlayOrPausecheckBox new fail!!");\r
+                return;\r
+            }\r
+            PlayOrPausecheckBox->SetPosition(118, 321, 120, 120);\r
+            PlayOrPausecheckBox->SetViewId(PlayOrPausecheckBoxVIEWID);\r
+            PlayOrPausecheckBox->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);\r
+            PlayOrPausecheckBox->SetStyle(STYLE_BORDER_RADIUS, 51);\r
+            PlayOrPausecheckBox->SetImages(checkBox1selectedImg, checkBox1unSelectedImg);\r
+            PlayOrPausecheckBox->SetVisible(true);\r
+            /* ---------------------------- ResetOrMarkbutton --------------------------- */\r
+            ResetOrMarkbutton = new UIButton();\r
+            if (ResetOrMarkbutton == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ResetOrMarkbutton new fail!!");\r
+                return;\r
+            }\r
+            ResetOrMarkbutton->SetPosition(256, 333, 100, 100);\r
+            ResetOrMarkbutton->SetViewId(ResetOrMarkButton_RESET1_VIEWID);\r
+            ResetOrMarkbutton->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);\r
+            ResetOrMarkbutton->SetStyle(STYLE_BACKGROUND_OPA, 0x00);\r
+            ResetOrMarkbutton->SetImageSrc(ResetOrMarkButtonImg_Reset1, ResetOrMarkButtonImg_Reset1);\r
+            ResetOrMarkbutton->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xfffbfbfb, UIButton::PRESSED);\r
+            ResetOrMarkbutton->SetStyleForState(STYLE_BACKGROUND_OPA, 0x00, UIButton::PRESSED);\r
+            ResetOrMarkbutton->SetStyleForState(STYLE_BORDER_COLOR, 0xff0b1928, UIButton::PRESSED);\r
+            ResetOrMarkbutton->SetVisible(true);\r
+            /* -------------------------------------------------------------------------- */\r
+            MainStopWatchPresenter::GetInstance()->SetCheckBoxAndButton(PlayOrPausecheckBox, ResetOrMarkbutton);\r
+            MainStopWatchPresenter::GetInstance()->SetLable(stopWatchlabel, showTimelabel);\r
+            MainStopWatchPresenter::GetInstance()->SetImage(LineImage);\r
+            MainStopWatchPresenter::GetInstance()->SetList(list, listadapter, listdata);\r
+            // Add the CallBack about events\r
+            PlayOrPausecheckBox->SetOnChangeListener(presenter_);\r
+            PlayOrPausecheckBox->SetOnClickListener(presenter_);\r
+            ResetOrMarkbutton->SetOnClickListener(presenter_);\r
+        }\r
+    }\r
+\r
+    UILabelExt *MainStopWatchView::GetShowTimelabel(void)\r
+    {\r
+        return showTimelabel;\r
+    }\r
+\r
+    UICheckBox *MainStopWatchView::GetPlayOrPausecheckBox(void)\r
+    {\r
+        return PlayOrPausecheckBox;\r
+    }\r
+\r
+    /*切换到记录时间到达上线视图*/\r
+    void MainStopWatchView::ChangeRecordTimeToLimitView(void)\r
+    {\r
+        ChangeView(MAIN_STOPWATCH, STOPWATCH_RECORD_TIME_TO_LIMIT);\r
+    }\r
+\r
+    UIScrollView *MainStopWatchView::GetScrollView(int16_t index)\r
+    {\r
+        return scrollView[index];\r
+    }\r
+\r
+    StopWatchAdapter *MainStopWatchView::GetStopWatchAdapter(void)\r
+    {\r
+        return stopWatchAdapter;\r
+    }\r
+\r
+    void MainStopWatchView::OnStart()\r
+    {\r
+        InitMainStopWatchView();\r
+\r
+        scrollView[MAIN_STOPWATCH]->Add(stopWatchlabel);\r
+        scrollView[MAIN_STOPWATCH]->Add(showTimelabel);\r
+        scrollView[MAIN_STOPWATCH]->Add(LineImage);\r
+        scrollView[MAIN_STOPWATCH]->Add(list);\r
+        scrollView[MAIN_STOPWATCH]->Add(PlayOrPausecheckBox);\r
+        scrollView[MAIN_STOPWATCH]->Add(ResetOrMarkbutton);\r
+        group->Add(scrollView[MAIN_STOPWATCH]);\r
+        AddViewToRootContainer(group);\r
+    }\r
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchAdapter.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchAdapter.cpp
new file mode 100644 (file)
index 0000000..352b06c
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-12-12 11:07:26
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-12-16 11:43:35
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/src/StopWatchAdapter.cpp
+ */
+#include "stopwatch/include/StopWatchAdapter.h"
+#include "stopwatch/include/StopWatchItemView.h"
+#include "wearable_log.h"
+namespace OHOS
+{
+    StopWatchAdapter::StopWatchAdapter() {}
+    StopWatchAdapter::~StopWatchAdapter()
+    {
+        listData.clear();
+    }
+    uint16_t StopWatchAdapter::GetCount(void)
+    {
+        return listData.size();
+    }
+
+    void StopWatchAdapter::ClearData(void)
+    {
+        listData.clear();
+        curSelectItemViewIndex = -1;
+    }
+
+    void StopWatchAdapter::setSelectedIndex(int index)
+    {
+        curSelectItemViewIndex = index;
+    }
+
+    UIView *StopWatchAdapter::GetView(UIView *inView, int16_t index)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GetView:%d", index);
+        if (listData.empty())
+        {
+            return nullptr;
+        }
+        if (index < 0)
+        {
+            return nullptr;
+        }
+        size_t uindex = index;
+        if (uindex >= listData.size())
+        {
+            return nullptr;
+        }
+
+        std::list<const StopWatchItem>::iterator it = listData.begin();
+        for (int i = 0; i < index; i++)
+        {
+            it++;
+        }
+        const StopWatchItem itemData = *it;
+        StopWatchItemView *item = nullptr;
+        if (inView == nullptr)
+        {
+            item = new StopWatchItemView();
+        }
+        else
+        {
+            item = static_cast<StopWatchItemView *>(inView);
+        }
+        item->SetViewIndex(index);
+        item->SetItemInfo(itemData);
+        if(curSelectItemViewIndex == itemData.curSelectItemViewIndex)
+        {
+            item->SetItemViewColor(true);
+        }
+        else
+        {
+            item->SetItemViewColor(false);
+        }
+        return item;
+    }
+
+    void StopWatchAdapter::AddListItem(const StopWatchItem &item)
+    {
+        listData.push_front(item);
+    }
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchItemView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchItemView.cpp
new file mode 100644 (file)
index 0000000..c632d8c
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * @Author: luozheng 1732036298@qq.com
+ * @Date: 2024-12-12 14:40:44
+ * @LastEditors: luozheng 1732036298@qq.com
+ * @LastEditTime: 2024-12-16 11:40:34
+ * @FilePath: /FD230/code/sdk/application/wearable/fd230app/nativeui/stopwatch/src/StopWatchItemView.cpp
+ */
+#include "stopwatch/include/StopWatchItemView.h"
+#include "wearable_log.h"
+namespace OHOS
+{
+    StopWatchItemView::StopWatchItemView()
+    {
+        SetPosition(0, 0, 360, 42);
+        SetStyle(STYLE_BACKGROUND_COLOR, Color::Black().full);
+        SetStyle(STYLE_BACKGROUND_OPA, 255);
+        SetUpChild();
+        Add(&RecordCycleNumber);
+        Add(&formattedTime);
+        Add(&formattedDiff);
+    }
+
+    StopWatchItemView::~StopWatchItemView()
+    {
+        RemoveAll();
+    }
+
+    void StopWatchItemView::SetItemInfo(const StopWatchItem itemInfo)
+    {
+        RecordCycleNumber.SetText(itemInfo.RecordCycleNumber.c_str());
+        formattedTime.SetText(itemInfo.formattedTime.c_str());
+        formattedDiff.SetText(itemInfo.formattedDiff.c_str());
+    }
+
+    void StopWatchItemView::SetUpChild()
+    {
+        RecordCycleNumber.SetPosition(0, 0, 80, 32);
+        RecordCycleNumber.SetStyle(STYLE_TEXT_FONT, 28);
+        RecordCycleNumber.SetStyle(STYLE_LINE_HEIGHT, 32);
+        RecordCycleNumber.SetLineBreakMode(UILabel::LINE_BREAK_CLIP);
+        RecordCycleNumber.SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+        RecordCycleNumber.SetFont(DEFAULT_VECTOR_FONT_FILENAME, 28);
+
+        formattedTime.SetPosition(80, 0, 120, 32);
+        formattedTime.SetStyle(STYLE_TEXT_FONT, 28);
+        formattedTime.SetStyle(STYLE_LINE_HEIGHT, 32);
+        formattedTime.SetLineBreakMode(UILabel::LINE_BREAK_CLIP);
+        formattedTime.SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+        formattedTime.SetFont(DEFAULT_VECTOR_FONT_FILENAME, 28);
+
+        formattedDiff.SetPosition(230, 0, 130, 32);
+        formattedDiff.SetStyle(STYLE_TEXT_FONT, 28);
+        formattedDiff.SetStyle(STYLE_LINE_HEIGHT, 32);
+        formattedDiff.SetLineBreakMode(UILabel::LINE_BREAK_CLIP);
+        formattedDiff.SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
+        formattedDiff.SetFont(DEFAULT_VECTOR_FONT_FILENAME, 28);
+    }
+
+    void StopWatchItemView::SetItemViewColor(bool flag)
+    {
+        if (flag)
+        {
+            RecordCycleNumber.SetTextColor(Color::GetColorFromRGBA(0xff, 0x8a, 0x36, 0xff));
+            formattedTime.SetTextColor(Color::GetColorFromRGBA(0xff, 0x8a, 0x36, 0xff));
+            formattedDiff.SetTextColor(Color::GetColorFromRGBA(0xff, 0x8a, 0x36, 0xff));
+        }
+        else
+        {
+            RecordCycleNumber.SetTextColor(Color::GetColorFromRGBA(0xaa, 0xaa, 0xaa, 0xff));
+            formattedTime.SetTextColor(Color::GetColorFromRGBA(0xaa, 0xaa, 0xaa, 0xff));
+            formattedDiff.SetTextColor(Color::GetColorFromRGBA(0xaa, 0xaa, 0xaa, 0xff));
+        }
+    }
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchModel.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchModel.cpp
new file mode 100644 (file)
index 0000000..ff9d685
--- /dev/null
@@ -0,0 +1,210 @@
+#include "stopwatch/include/StopWatchModel.h"\r
+#include "cmsis_os.h"\r
+#include <cmsis_os2.h>\r
+#include <service/graphic_service.h>\r
+#include "custom_utils.h"\r
+#include "wearable_log.h"\r
+#include <ohos_types.h>\r
+#include "stopwatch/include/MainStopWatchView.h"\r
+namespace OHOS\r
+{\r
+\r
+    std::shared_ptr<StopWatchModel> StopWatchModel::GetInstance() // shared_ptr:当没有引用指向这个实例时会自动释放内存\r
+    {\r
+        static std::shared_ptr<StopWatchModel> instance(new StopWatchModel());\r
+        return instance;\r
+    }\r
+\r
+    /* ----------------------------------- 定时器 ---------------------------------- */\r
+    // 声明定时器变量\r
+    static osTimerId_t g_timer = nullptr;\r
+    static uint32_t g_StopWatchTimes = {0};\r
+    static constexpr int32 NUM_TEN = 10; // 主要用于格式化输出,当小时、分钟或秒小于10时,在其前面添加一个’0’以保持常规时间格式\r
+    static constexpr int16 ERROR = 0;\r
+    static constexpr int16 SUCCESS = 1;\r
+    static constexpr int16 NUM_TIME_MIN = 60;\r
+    static constexpr int16 NUM_TIME_HOUR = 3600;\r
+    static constexpr int32 NUM_TIME_DAY = 86400;\r
+    static constexpr uint32_t MAX_STOPWATCH_TIME = (99 * 60 * 1000) + (59 * 1000) + 990; // 99分59秒990毫秒\r
+    static uint64_t g_timerStartTimestamp = 0;                                  // 添加一个全局变量来存储定时器开始的时间戳\r
+    /*\r
+     * @brief 显示倒计时(分钟、秒、毫秒),毫秒显示为两位数\r
+     */\r
+    std::string StopWatchModel::DisplayStopWatchDuration()\r
+    {\r
+        stopwatchDuration.erase();\r
+        std::string minute;\r
+        std::string seconds;\r
+        std::string milliseconds;\r
+\r
+        // 假设 g_StopWatchTimes 是毫秒数\r
+        int min = g_StopWatchTimes / (60 * 1000);          // 60,000 毫秒 = 1 分钟\r
+        int sec = (g_StopWatchTimes % (60 * 1000)) / 1000; // 1,000 毫秒 = 1 秒\r
+        int msec = (g_StopWatchTimes % 1000) / 10;         // 获得两位数的毫秒,最大值为99\r
+\r
+        if (min < 10)\r
+        {\r
+            minute = "0" + std::to_string(min);\r
+        }\r
+        else\r
+        {\r
+            minute = std::to_string(min);\r
+        }\r
+\r
+        if (sec < 10)\r
+        {\r
+            seconds = "0" + std::to_string(sec);\r
+        }\r
+        else\r
+        {\r
+            seconds = std::to_string(sec);\r
+        }\r
+\r
+        // 格式化毫秒为两位数\r
+        if (msec < 10)\r
+        {\r
+            milliseconds = "0" + std::to_string(msec);\r
+        }\r
+        else\r
+        {\r
+            milliseconds = std::to_string(msec);\r
+        }\r
+\r
+        // 拼接分钟、秒和毫秒\r
+        stopwatchDuration = minute + ":" + seconds + "." + milliseconds;\r
+\r
+        return stopwatchDuration.c_str();\r
+    }\r
+\r
+    // 检查是否达到最大记录时间\r
+    bool StopWatchModel::isReachMaxRecordTime(void)\r
+    {\r
+        if (g_StopWatchTimes >= MAX_STOPWATCH_TIME)\r
+        {\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    static void StopWatch_TimerCallbackProc(int data)\r
+    {\r
+        if (MainStopWatchView::GetInstance() == nullptr)\r
+        {\r
+            return;\r
+        }\r
+        MainStopWatchView::GetInstance()->GetPlayOrPausecheckBox()->SetState(UICheckBox::UNSELECTED);\r
+        MainStopWatchView::GetInstance()->ChangeRecordTimeToLimitView();\r
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "已到达记录最高时间:%d",g_StopWatchTimes);\r
+        g_StopWatchTimes = MAX_STOPWATCH_TIME;\r
+        MainStopWatchView::GetInstance()->GetShowTimelabel()->SetText(StopWatchModel::GetInstance()->DisplayStopWatchDuration().c_str());\r
+        uapi_set_screen_on_api();\r
+    }\r
+\r
+    /*\r
+     * @brief 倒计时回调函数\r
+     */\r
+    static void StopWatch_TimerCallback(int data)\r
+    {\r
+        g_StopWatchTimes += 10; // 每次回调增加10毫秒\r
+        if(StopWatchModel::GetInstance()->isReachMaxRecordTime()) //达到最大时间\r
+        {\r
+            GraphicService::GetInstance()->PostGraphicEvent(std::bind(StopWatch_TimerCallbackProc, data));\r
+        }\r
+    }\r
+\r
+    /*\r
+     *  @brief 开启定时器函数\r
+     *  @param interval 时间间隔,单位ms\r
+     */\r
+    void StopWatchModel::StopWatch_StartTimer(uint32_t interval)\r
+    {\r
+        /* -------------------------- 1.检查定时器是否已经存在,如果存在则返回 ------------------------- */\r
+        if (g_timer != nullptr)\r
+        {\r
+            WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "StopWatchModel::StopWatch_StartTimer Timer already exists!!");\r
+            return;\r
+        }\r
+\r
+        /* --------------------------------- 2.创建定时器 -------------------------------- */\r
+        g_timer = osTimerNew((osTimerFunc_t)StopWatch_TimerCallback, osTimerPeriodic, nullptr, nullptr);\r
+        // 检查定时器创建是否成功\r
+        if (g_timer == nullptr)\r
+        {\r
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StopWatchModel::StopWatch_StartTimer Timer create failed!!");\r
+            return;\r
+        }\r
+\r
+        /* ----------------------------- 3.将毫秒转换为系统时钟节拍数 ---------------------------- */\r
+        int tick = osMs2Tick(interval);\r
+\r
+        /* --------------------------------- 4.启动定时器 -------------------------------- */\r
+        // g_timerStartTimestamp = GetCurrentTimestamp(); // 假设GetCurrentTimestamp()是获取当前时间戳的函数\r
+        osStatus_t ret = osTimerStart(g_timer, tick);\r
+        /* -------------------- 5.检查定时器启动是否成功,如果启动失败,删除定时器并置为 nullptr -------------------- */\r
+        if (ret != osOK)\r
+        {\r
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[StopWatch_StartTimer]osTimerDelete failed, ret = %lx!!", ret);\r
+            if (g_timer != nullptr)\r
+            {\r
+                ret = osTimerDelete(g_timer);\r
+            }\r
+            g_timer = nullptr;\r
+            return;\r
+        }\r
+        return;\r
+    }\r
+\r
+    /*\r
+     * @brief 停止定时器\r
+     */\r
+    void StopWatchModel::StopWatch_StopTimer(void)\r
+    {\r
+        // 检查定时器是否存在\r
+        if (g_timer != nullptr)\r
+        {\r
+            osTimerStop(g_timer);\r
+            // 删除定时器并置为 nullptr\r
+            osStatus_t ret = osTimerDelete(g_timer);\r
+            if (ret != osOK)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ScanTimerCallback osTimerDelete failed, ret = %lx!!", ret);\r
+            }\r
+            g_timer = nullptr;\r
+            g_StopWatchTimes = 0;\r
+        }\r
+    }\r
+    /*\r
+     * @brief 暂停定时器\r
+     */\r
+    void StopWatchModel::StopWatch_PauseTimer(void)\r
+    {\r
+        // 检查定时器是否存在\r
+        if (g_timer != nullptr)\r
+        {\r
+            // 暂停定时器,但不删除它\r
+            osStatus_t ret = osTimerStop(g_timer);\r
+            if (ret != osOK)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StopWatch_PauseTimer osTimerStop failed, ret = %lx!!", ret);\r
+            }\r
+        }\r
+    }\r
+\r
+    /*\r
+     * @brief 恢复定时器\r
+     */\r
+    void StopWatchModel::StopWatch_ResumeTimer(uint32_t interval)\r
+    {\r
+        // 检查定时器是否存在\r
+        if (g_timer != nullptr)\r
+        {\r
+            int tick = osMs2Tick(interval);\r
+            // 恢复定时器\r
+            osStatus_t ret = osTimerStart(g_timer, tick);\r
+            if (ret != osOK)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StopWatch_ResumeTimer osTimerStart failed, ret = %lx!!", ret);\r
+            }\r
+        }\r
+    }\r
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchRecordCycleToLimitView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchRecordCycleToLimitView.cpp
new file mode 100644 (file)
index 0000000..a6bf75f
--- /dev/null
@@ -0,0 +1,156 @@
+#include "stopwatch/include/StopWatchRecordCycleToLimitView.h"\r
+#include "stopwatch/include/MainStopWatchPresenter.h"\r
+#include "UiConfig.h"\r
+#include "wearable_log.h"\r
+/*弹窗视图:到达最高圈数99时弹出*/\r
+namespace OHOS\r
+{\r
+\r
+    static StopWatchRecordCycleToLimitView *g_pStopWatchRecordCycleToLimitView{nullptr};\r
+\r
+    StopWatchRecordCycleToLimitView::StopWatchRecordCycleToLimitView()\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "StopWatchRecordCycleToLimitView::StopWatchRecordCycleToLimitView");\r
+        g_pStopWatchRecordCycleToLimitView = this;\r
+    }\r
+\r
+    StopWatchRecordCycleToLimitView::~StopWatchRecordCycleToLimitView()\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "StopWatchRecordCycleToLimitView::~StopWatchRecordCycleToLimitView");\r
+        ClearAll();\r
+        g_pStopWatchRecordCycleToLimitView = nullptr;\r
+    }\r
+\r
+    StopWatchRecordCycleToLimitView *StopWatchRecordCycleToLimitView::GetInstance()\r
+    {\r
+        return g_pStopWatchRecordCycleToLimitView;\r
+    }\r
+    // 初始化秒表到达最高圈数时弹出的视图\r
+    UIScrollView *StopWatchRecordCycleToLimitView::InitStopWatchRecordCycleToLimitView()\r
+    {\r
+        if (scrollView == nullptr)\r
+        {\r
+            scrollView = new UIScrollView();\r
+            scrollView->SetPosition(0, 0, 466, 466);\r
+            scrollView->SetStyle(STYLE_BACKGROUND_OPA, 0xff);\r
+            scrollView->SetTouchable(true);\r
+            scrollView->SetDraggable(true);\r
+            UIView::OnDragListener *draglistener = static_cast<UIView::OnDragListener *>(MainStopWatchPresenter::GetInstance());\r
+            if (draglistener == nullptr)\r
+            {\r
+                return nullptr;\r
+            }\r
+            scrollView->SetOnDragListener(draglistener);\r
+            /* -------------------------------------------------------------------------- */\r
+            if (Title == nullptr)\r
+            {\r
+                Title = new UILabelExt();\r
+                if (Title == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Title new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            Title->SetPosition(83, 50, 300, 35);\r
+            Title->SetStyle(STYLE_TEXT_FONT, 30);\r
+            Title->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            Title->SetTextId(STR_STOPWATCH); // 秒表\r
+            Title->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);\r
+            Title->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+            Title->SetRollSpeed(23);\r
+            Title->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            /* -------------------------------------------------------------------------- */\r
+            if (AttentionImage == nullptr)\r
+            {\r
+                AttentionImage = new UIImageView();\r
+                if (AttentionImage == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AttentionImage new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            AttentionImage->SetPosition(193, 121, 100, 100);\r
+            AttentionImage->SetStyle(STYLE_BORDER_RADIUS, 100);\r
+            AttentionImage->SetSrc(PNG_ATTENTION_IMAGE);\r
+            /* -------------------------------------------------------------------------- */\r
+            if (AttentionLabel == nullptr)\r
+            {\r
+                AttentionLabel = new UILabelExt();\r
+                if (AttentionLabel == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AttentionLabel new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            AttentionLabel->SetPosition(83, 253, 300, 35);\r
+            AttentionLabel->SetStyle(STYLE_TEXT_FONT, 30);\r
+            AttentionLabel->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            AttentionLabel->SetTextId(STR_TMCHBR); // 当前已达到了最高计数\r
+            AttentionLabel->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);\r
+            AttentionLabel->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+            AttentionLabel->SetTextColor(Color::GetColorFromRGBA(0xaa, 0xaa, 0xaa, 0xff));\r
+            AttentionLabel->SetRollSpeed(23);\r
+            AttentionLabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            /* -------------------------------------------------------------------------- */\r
+            if (button == nullptr)\r
+            {\r
+                button = new UILabelButtonExt();\r
+                if (button == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "button new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            button->SetPosition(0, 366, 466, 100);\r
+            button->SetViewId(CycleToLimitReturnButton_VIEWID);\r
+            button->SetImageSrc(PNG_466x100BUTTON_IMAGE, PNG_466x100BUTTON_IMAGE);\r
+            button->SetTextId(STR_KONW);\r
+            button->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            button->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            button->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xff000000, UIButton::PRESSED);\r
+            UIView::OnClickListener *buttonClick = static_cast<UIView::OnClickListener *>(MainStopWatchPresenter::GetInstance());\r
+            if (buttonClick == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "buttonClick new fail");\r
+                return nullptr;\r
+            }\r
+            button->SetOnClickListener(buttonClick);\r
+            /* ----------------------------------- Add ---------------------------------- */\r
+            scrollView->Add(Title);\r
+            scrollView->Add(AttentionImage);\r
+            scrollView->Add(AttentionLabel);\r
+            scrollView->Add(button);\r
+        }\r
+        return scrollView;\r
+    }\r
+\r
+    void StopWatchRecordCycleToLimitView::ClearAll()\r
+    {\r
+        if (scrollView != nullptr)\r
+        {\r
+            scrollView->RemoveAll();\r
+            if (Title != nullptr)\r
+            {\r
+                delete Title;\r
+                Title = nullptr;\r
+            }\r
+            if (AttentionImage != nullptr)\r
+            {\r
+                delete AttentionImage;\r
+                AttentionImage = nullptr;\r
+            }\r
+            if (AttentionLabel != nullptr)\r
+            {\r
+                delete AttentionLabel;\r
+                AttentionLabel = nullptr;\r
+            }\r
+            if (button != nullptr)\r
+            {\r
+                delete button;\r
+                button = nullptr;\r
+            }\r
+            delete scrollView;\r
+            scrollView = nullptr;\r
+        }\r
+    }\r
+}
\ No newline at end of file
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchRecordTimeToLimitView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/stopwatch/src/StopWatchRecordTimeToLimitView.cpp
new file mode 100644 (file)
index 0000000..403064d
--- /dev/null
@@ -0,0 +1,178 @@
+#include "stopwatch/include/StopWatchRecordTimeToLimitView.h"\r
+#include "stopwatch/include/MainStopWatchPresenter.h"\r
+#include "UiConfig.h"\r
+#include "wearable_log.h"\r
+/*弹窗视图:到达最高时间99:59.99时弹出*/\r
+namespace OHOS\r
+{\r
+\r
+    static StopWatchRecordTimeToLimitView *g_pStopWatchRecordTimeToLimitView{nullptr};\r
+\r
+    StopWatchRecordTimeToLimitView::StopWatchRecordTimeToLimitView()\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "StopWatchRecordTimeToLimitView::StopWatchRecordTimeToLimitView");\r
+        g_pStopWatchRecordTimeToLimitView = this;\r
+    }\r
+\r
+    StopWatchRecordTimeToLimitView::~StopWatchRecordTimeToLimitView()\r
+    {\r
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "StopWatchRecordTimeToLimitView::~StopWatchRecordTimeToLimitView");\r
+        ClearAll();\r
+        g_pStopWatchRecordTimeToLimitView = nullptr;\r
+    }\r
+\r
+    StopWatchRecordTimeToLimitView *StopWatchRecordTimeToLimitView::GetInstance()\r
+    {\r
+\r
+        return g_pStopWatchRecordTimeToLimitView;\r
+    }\r
+\r
+    UIScrollView *StopWatchRecordTimeToLimitView::InitStopWatchRecordTimeToLimitView()\r
+    {\r
+        if (scrollView == nullptr)\r
+        {\r
+            scrollView = new UIScrollView();\r
+            scrollView->SetPosition(0, 0, 466, 466);\r
+            scrollView->SetStyle(STYLE_BACKGROUND_OPA, 0xff);\r
+            scrollView->SetTouchable(true);\r
+            scrollView->SetDraggable(true);\r
+            UIView::OnDragListener *draglistener = static_cast<UIView::OnDragListener *>(MainStopWatchPresenter::GetInstance());\r
+            if (draglistener == nullptr)\r
+            {\r
+                return nullptr;\r
+            }\r
+            scrollView->SetOnDragListener(draglistener);\r
+            /* -------------------------------------------------------------------------- */\r
+            if (Title == nullptr)\r
+            {\r
+                Title = new UILabelExt();\r
+                if (Title == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Title new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            Title->SetPosition(83, 50, 300, 35);\r
+            Title->SetStyle(STYLE_TEXT_FONT, 30);\r
+            Title->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            Title->SetTextId(STR_STOPWATCH); // 秒表\r
+            Title->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);\r
+            Title->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+            Title->SetRollSpeed(23);\r
+            Title->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            /* -------------------------------------------------------------------------- */\r
+            if (AttentionImage == nullptr)\r
+            {\r
+                AttentionImage = new UIImageView();\r
+                if (AttentionImage == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AttentionImage new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            AttentionImage->SetPosition(193, 121, 100, 100);\r
+            AttentionImage->SetStyle(STYLE_BORDER_RADIUS, 100);\r
+            AttentionImage->SetSrc(PNG_ATTENTION_IMAGE);\r
+            /* ---------------------------- AttentionLabel_1 ---------------------------- */\r
+            if (AttentionLabel_1 == nullptr)\r
+            {\r
+                AttentionLabel_1 = new UILabelExt();\r
+                if (AttentionLabel_1 == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AttentionLabel_1 new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            AttentionLabel_1->SetPosition(83, 235, 300, 35);\r
+            AttentionLabel_1->SetStyle(STYLE_TEXT_FONT, 30);\r
+            AttentionLabel_1->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            AttentionLabel_1->SetTextId(STR_MAXIMUM_TIME_REACH_NOTE); // 当前已达到了最长时间\r
+            AttentionLabel_1->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);\r
+            AttentionLabel_1->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+            AttentionLabel_1->SetTextColor(Color::GetColorFromRGBA(0xaa, 0xaa, 0xaa, 0xff));\r
+            AttentionLabel_1->SetRollSpeed(23);\r
+            AttentionLabel_1->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            /* ---------------------------- AttentionLabel_2 ---------------------------- */\r
+            if (AttentionLabel_2 == nullptr)\r
+            {\r
+                AttentionLabel_2 = new UILabelExt();\r
+                if (AttentionLabel_2 == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AttentionLabel_2 new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            AttentionLabel_2->SetPosition(53, 279, 360, 35);\r
+            AttentionLabel_2->SetStyle(STYLE_TEXT_FONT, 30);\r
+            AttentionLabel_2->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            AttentionLabel_2->SetTextId(STR_STOPWATCH_STOP_NOTE); // 秒表自动暂停,请重新开始\r
+            AttentionLabel_2->SetLineBreakMode(UILabel::LINE_BREAK_OSCILLATION);\r
+            AttentionLabel_2->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);\r
+            AttentionLabel_2->SetTextColor(Color::GetColorFromRGBA(0xaa, 0xaa, 0xaa, 0xff));\r
+            AttentionLabel_2->SetRollSpeed(23);\r
+            AttentionLabel_2->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            /* -------------------------------------------------------------------------- */\r
+            if (button == nullptr)\r
+            {\r
+                button = new UILabelButtonExt();\r
+                if (button == nullptr)\r
+                {\r
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "button new fail");\r
+                    return nullptr;\r
+                }\r
+            }\r
+            button->SetPosition(0, 366, 466, 100);\r
+            button->SetViewId(TimeToLimitReturnButton_VIEWID);\r
+            button->SetImageSrc(PNG_466x100BUTTON_IMAGE, PNG_466x100BUTTON_IMAGE);\r
+            button->SetTextId(STR_KONW);\r
+            button->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);\r
+            button->SetStyle(STYLE_LINE_HEIGHT, 35);\r
+            button->SetStyleForState(STYLE_BACKGROUND_COLOR, 0xff000000, UIButton::PRESSED);\r
+            UIView::OnClickListener *buttonClick = static_cast<UIView::OnClickListener *>(MainStopWatchPresenter::GetInstance());\r
+            if (buttonClick == nullptr)\r
+            {\r
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "buttonClick new fail");\r
+                return nullptr;\r
+            }\r
+            button->SetOnClickListener(buttonClick);\r
+            /* ----------------------------------- Add ---------------------------------- */\r
+            scrollView->Add(Title);\r
+            scrollView->Add(AttentionImage);\r
+            scrollView->Add(AttentionLabel_1);\r
+            scrollView->Add(AttentionLabel_2);\r
+            scrollView->Add(button);\r
+        }\r
+        return scrollView;\r
+    }\r
+\r
+    void StopWatchRecordTimeToLimitView::ClearAll()\r
+    {\r
+        if (scrollView != nullptr)\r
+        {\r
+            scrollView->RemoveAll();\r
+            if (Title != nullptr)\r
+            {\r
+                delete Title;\r
+                Title = nullptr;\r
+            }\r
+            if (AttentionImage != nullptr)\r
+            {\r
+                delete AttentionImage;\r
+                AttentionImage = nullptr;\r
+            }\r
+            if (AttentionLabel_1 != nullptr)\r
+            {\r
+                delete AttentionLabel_1;\r
+                AttentionLabel_1 = nullptr;\r
+            }\r
+            if (button != nullptr)\r
+            {\r
+                delete button;\r
+                button = nullptr;\r
+            }\r
+\r
+            delete scrollView;\r
+            scrollView = nullptr;\r
+        }\r
+    }\r
+}
\ No newline at end of file
index 41e18ef..6f8e33d 100644 (file)
@@ -386,8 +386,8 @@ target = {
                         #   "ppg_sensor",
                         #   "ppgBleService",
                         #   "mag_sensor",
-                        #   "vibrator",
-                        #   "vibrator_service",
+                          "vibrator",
+                          "vibrator_service",
                         #   "lwIP",
                         #   "slip",
                         #   "cat1_service",