386 lines
13 KiB
C++
386 lines
13 KiB
C++
/*----------------------------------------------------------------------------
|
||
* 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 <iomanip>
|
||
#include <sstream>
|
||
#include <string>
|
||
|
||
#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<int> &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
|