/*---------------------------------------------------------------------------- * Copyright (c) TJD Technologies Co., Ltd. 2024. All rights reserved. * * Description: TjdUiAppMainPageHealth.cpp * * Author: luziquan@ss-tjd.com * * Create: 2024-12-27 *--------------------------------------------------------------------------*/ #include "TjdUiAppMainPageHealth.h" #include "TjdUiAppMainPresenter.h" #include "TjdUiImageIds.h" #include "TjdUiMemManage.h" #include "TjdUiMultiLanguageExt.h" #include "common/image_cache_manager.h" #include "hal_tick.h" #include "rtc_api.h" #include "sql_fit.h" #include "sys_config.h" #include #include #include #define ENABLE_PRINT_INFO 1 #define DEBUG_ENABLE 1 #if ENABLE_PRINT_INFO #define static_print_info(...) sys_ui_log_i(__VA_ARGS__) // 一般信息打印宏控制 #define static_print_warn(...) sys_ui_log_w(__VA_ARGS__) // 警告信息打印一般常开 #define static_print_error(...) sys_ui_log_e(__VA_ARGS__) // 错误信息打印一般常开 #if DEBUG_ENABLE #define static_print_debug(...) sys_ui_log_d(__VA_ARGS__) #else #define static_print_debug(...) #endif #else #define static_print_info(...) #define static_print_warn(...) #define static_print_error(...) #endif #define MAIN_HR_IMAGE_BIN_PATH TJD_IMAGE_PATH "img_main_health.bin" namespace TJD { static OHOS::ImageAnimatorInfo g_animatorInfo[3] = { {nullptr, {135, 35}, 56, 50, OHOS::IMG_SRC_IMAGE_INFO}, {nullptr, {135, 35}, 56, 50, OHOS::IMG_SRC_IMAGE_INFO}, {nullptr, {135, 35}, 56, 50, OHOS::IMG_SRC_IMAGE_INFO}, }; static constexpr uint8_t AVERAGE_HOURS = 12; static constexpr uint8_t VALUES_PER_HOUR = 24; static TjdUiAppMainPageHealth *g_heartRatePage = nullptr; TjdUiAppMainPageHealth::TjdUiAppMainPageHealth() { SetPosition(0, 0, OHOS::Screen::GetInstance().GetWidth(), OHOS::Screen::GetInstance().GetHeight()); g_heartRatePage = this; } TjdUiAppMainPageHealth::~TjdUiAppMainPageHealth() { UnLoad(); g_heartRatePage = nullptr; } TjdUiAppMainPageHealth *TjdUiAppMainPageHealth::GetInstance(void) { return g_heartRatePage; } void TjdUiAppMainPageHealth::PreLoad(void) { auto &image = OHOS::ImageCacheManager::GetInstance(); // OHOS::MemCheck::GetInstance()->EnableLeakCheck(true); static_print_debug("HeartRatePage::PreLoad"); if (!viewiInitStatus) { image.LoadAllInMultiRes(MAIN_HR_IMAGE_BIN_PATH); g_animatorInfo[0].imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR_DT_01_03, MAIN_HR_IMAGE_BIN_PATH); g_animatorInfo[1].imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR_DT_02_03, MAIN_HR_IMAGE_BIN_PATH); g_animatorInfo[2].imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR_DT_03_03, MAIN_HR_IMAGE_BIN_PATH); InitView(); UpdateValue(); HrPointArrayInit(); imageAnimator_->Start(); viewiInitStatus = true; } } void TjdUiAppMainPageHealth::UnLoad(void) { static_print_debug("HeartRatePage::UnLoad"); if (viewiInitStatus) { OHOS::ImageCacheManager::GetInstance().UnloadAllInMultiRes(MAIN_HR_IMAGE_BIN_PATH); RemoveAll(); if (container_) { container_->RemoveAll(); delete container_; container_ = nullptr; } if (chartBg_) { delete chartBg_; chartBg_ = nullptr; } if (imageAnimator_) { imageAnimator_->Stop(); delete imageAnimator_; imageAnimator_ = nullptr; } if (canvas_) { canvas_->Clear(); delete canvas_; canvas_ = nullptr; } if (hrTitleValue_) { delete hrTitleValue_; hrTitleValue_ = nullptr; } if (hrTitleUnit_) { delete hrTitleUnit_; hrTitleUnit_ = nullptr; } if (hrBg_) { delete hrBg_; hrBg_ = nullptr; } if (hrValue_) { delete hrValue_; hrValue_ = nullptr; } if (spo2Bg_) { delete spo2Bg_; spo2Bg_ = nullptr; } if (spo2Value_) { delete spo2Value_; spo2Value_ = nullptr; } if (sleepBg_) { delete sleepBg_; sleepBg_ = nullptr; } if (sleepValue_) { delete sleepValue_; sleepValue_ = nullptr; } viewiInitStatus = false; } } static void DrawHeartRateCurve(OHOS::UICanvasExt *canvas, std::vector &heartRates) { if (canvas == nullptr || heartRates.empty()) { return; } canvas->Clear(); OHOS::PaintExt paint; paint.SetFillColor(OHOS::Color::Black()); paint.SetStrokeWidth(3); paint.SetStyle(OHOS::Paint::PaintStyle::STROKE_STYLE); paint.SetOpacity(OHOS::OPA_OPAQUE); paint.SetStrokeColor(OHOS::Color::Red()); paint.SetJoinType(OHOS::JoinType::JOIN_ROUND); paint.SetGradientSpread(OHOS::GradientSpread::SPREAD_PAD); paint.SetAntialiased(true); const int width = canvas->GetWidth(); const int height = canvas->GetHeight(); const int minHeartRate = 0; // 最小心率 const int maxHeartRate = 120; // TODO: 不知道最大最小心率是多少,暂定为0-120 const int halfStrokeWidth = paint.GetStrokeWidth() / 2; const int adjustedWidth = width - paint.GetStrokeWidth(); const int adjustedHeight = height - paint.GetStrokeWidth(); OHOS::Point startPoint = {halfStrokeWidth, adjustedHeight - (heartRates[0] - minHeartRate) * adjustedHeight / (maxHeartRate - minHeartRate) + halfStrokeWidth}; for (size_t i = 1; i < heartRates.size(); ++i) { int x1 = (i - 1) * adjustedWidth / (heartRates.size() - 1) + halfStrokeWidth; int y1 = adjustedHeight - (heartRates[i - 1] - minHeartRate) * adjustedHeight / (maxHeartRate - minHeartRate) + halfStrokeWidth; int x2 = i * adjustedWidth / (heartRates.size() - 1) + halfStrokeWidth; int y2 = adjustedHeight - (heartRates[i] - minHeartRate) * adjustedHeight / (maxHeartRate - minHeartRate) + halfStrokeWidth; OHOS::Point controlPoint1 = {x1 + (x2 - x1) / 2, y1}; OHOS::Point controlPoint2 = {x1 + (x2 - x1) / 2, y2}; OHOS::Point endPoint = {x2, y2}; canvas->DrawCurve(startPoint, controlPoint1, controlPoint2, endPoint, paint); startPoint = endPoint; } } void TjdUiAppMainPageHealth::InitView(void) { auto &image = OHOS::ImageCacheManager::GetInstance(); container_ = new OHOS::UIViewGroup(); container_->SetPosition(0, 0, OHOS::Screen::GetInstance().GetWidth(), OHOS::Screen::GetInstance().GetHeight()); canvas_ = new OHOS::UICanvasExt(); canvas_->SetPosition(81, 102, 304, 60); container_->Add(canvas_); chartBg_ = new OHOS::UIImageView(); chartBg_->SetPosition(50, 107, 366, 85); auto imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_CHART, MAIN_HR_IMAGE_BIN_PATH); chartBg_->SetSrc(imageInfo); container_->Add(chartBg_); imageAnimator_ = new OHOS::UIImageAnimatorView(); imageAnimator_->SetPosition(135, 36, 56, 50); imageAnimator_->SetImageAnimatorSrc(g_animatorInfo, 3, 250); container_->Add(imageAnimator_); hrTitleValue_ = new OHOS::UILabel(); hrTitleValue_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 40); hrTitleValue_->SetText("098"); hrTitleValue_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP); hrTitleValue_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER); hrTitleValue_->SetPosition(198, 36, 64, 29); container_->Add(hrTitleValue_); hrTitleUnit_ = new OHOS::UILabelExt(); hrTitleUnit_->SetPosition(hrTitleValue_->GetX() + hrTitleValue_->GetWidth() + 11, 42, 72, 27); hrTitleUnit_->SetFont(TJD_VECTOR_FONT_FILENAME, 28); // hrTitleUnit_->SetText("次/分"); hrTitleUnit_->SetTextId(STR_ID_110); hrTitleUnit_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP); hrTitleUnit_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER); container_->Add(hrTitleUnit_); hrBg_ = new OHOS::UIImageView(); hrBg_->SetPosition(50, 214, 121, 124); imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR, MAIN_HR_IMAGE_BIN_PATH); hrBg_->SetSrc(imageInfo); container_->Add(hrBg_); hrValue_ = new OHOS::UILabel(); hrValue_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 30); hrValue_->SetText("098"); hrValue_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP); hrValue_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER); hrValue_->SetPosition(86, 253, 48, 23); container_->Add(hrValue_); spo2Bg_ = new OHOS::UIImageView(); spo2Bg_->SetPosition(173, 309, 121, 124); imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_SPO2, MAIN_HR_IMAGE_BIN_PATH); spo2Bg_->SetSrc(imageInfo); container_->Add(spo2Bg_); spo2Value_ = new OHOS::UILabel(); spo2Value_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 30); spo2Value_->SetText("098%"); spo2Value_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP); spo2Value_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER); spo2Value_->SetPosition(204, 348, 72, 21); container_->Add(spo2Value_); sleepBg_ = new OHOS::UIImageView(); sleepBg_->SetPosition(296, 214, 121, 124); imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_SLEEP, MAIN_HR_IMAGE_BIN_PATH); sleepBg_->SetSrc(imageInfo); container_->Add(sleepBg_); sleepValue_ = new OHOS::UILabel(); sleepValue_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 30); sleepValue_->SetText("7.35"); sleepValue_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP); sleepValue_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER); sleepValue_->SetPosition(323, 253, 68, 20); container_->Add(sleepValue_); Add(container_); } void TjdUiAppMainPageHealth::UpdateValue(void) { std::stringstream ss; struct rtc_time localTime = {}; tjd_driver_rtc_get_ops()->get_rtc_time(&localTime); int curHrValue = sql_fit_get_last_hr(localTime.tm_wday); int curSpo2Value = sql_fit_get_last_spo2(localTime.tm_wday); int curSleepTime = sql_fit_get_currentSleepTime_data(); if (curHrValue == 0) { if (localTime.tm_wday == 0) { curHrValue = sql_fit_get_last_hr(6); } else { curHrValue = sql_fit_get_last_hr(localTime.tm_wday - 1); } } if (hrTitleValue_ && hrValue_) { if (curHrValue == 0) { hrTitleValue_->SetText("---"); hrValue_->SetText("-"); } else { ss.str(""); ss << std::setw(3) << std::setfill('0') << curHrValue; hrTitleValue_->SetText(ss.str().c_str()); hrValue_->SetText(ss.str().c_str()); } } if (spo2Value_) { if (curSpo2Value == 0) { spo2Value_->SetText("-%"); } else { ss.str(""); ss << std::setw(3) << std::setfill('0') << curSpo2Value << "%"; spo2Value_->SetText(ss.str().c_str()); } } if (sleepValue_) { if (curSleepTime == 0) { sleepValue_->SetText("-"); } else { sleepValue_->SetText(std::to_string(curSleepTime).c_str()); } } } static void CalculateHourlyAverages(const uint8_t *hr_day_array, uint8_t *hr_hourly_avg) { for (int i = 0; i < AVERAGE_HOURS; ++i) { int sum = 0; int count = 0; const uint8_t *segment = hr_day_array + i * VALUES_PER_HOUR; for (const uint8_t *ptr = segment; ptr < segment + VALUES_PER_HOUR; ++ptr) { if (*ptr != 0) { sum += *ptr; ++count; } } hr_hourly_avg[i] = (count > 0) ? (sum / count) : 0; } } void TjdUiAppMainPageHealth::HrPointArrayInit(void) { uint8_t hr_hourly_avg[AVERAGE_HOURS] = {}; uint8_t *storeArray; int32_t num = sql_fit_get_hr_daydata(&storeArray); CalculateHourlyAverages(storeArray, hr_hourly_avg); hrDayArray_.clear(); for (int i = 0; i < AVERAGE_HOURS; ++i) { hrDayArray_.emplace_back(hr_hourly_avg[i]); } DrawHeartRateCurve(canvas_, hrDayArray_); } void TjdUiAppMainPageHealth::NotifyTick(void) { static uint32_t lastUpdateTime = 0; static uint32_t lastInitTime = 0; uint32_t curTime = OHOS::HALTick::GetInstance().GetTime(); if (lastUpdateTime == 0) { lastUpdateTime = curTime; } if (lastInitTime == 0) { lastInitTime = curTime; } /* 每5秒刷新一次 */ if (curTime - lastUpdateTime >= 5 * 1000) { lastUpdateTime = curTime; UpdateValue(); } /* 每5分钟刷新一次 */ if (curTime - lastInitTime >= 5 * 60 * 1000) { lastInitTime = curTime; HrPointArrayInit(); } } } // namespace TJD