Files
P4-USB-transfer/main/ui/generated/events_init.c
T
zhengxinandCursor 851e347cf5 初始提交卡包 USB 工程
纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 21:54:50 +08:00

1966 lines
78 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright 2026 NXP
* NXP Proprietary. This software is owned or controlled by NXP and may only be used strictly in
* accordance with the applicable license terms. By expressly accepting such terms or by downloading, installing,
* activating and/or otherwise using the software, you are agreeing that you have read, and that you agree to
* comply with and are bound by, such license terms. If you do not agree to be bound by the applicable license
* terms, then you may not retain, install, activate or otherwise use the software.
*/
#include "events_init.h"
#include <stdio.h>
#include <inttypes.h>
#include "core/lv_obj_style_gen.h"
#include "lvgl.h"
#include "ui.h"
#include "ui_nav.h"
#include "UI_DISPATCH/ui_dispatch.h"
#include "esp_system.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "config.h"
#include "widgets_init.h"
#include "remapping.h"
#include "custom.h"
#include "SCREEN/screen_brightness.h"
#include "SCREEN/screen_sleep.h"
#include "BLE/ble_send.h"
static const char *TAG_SLEEP_EVT = "sleep_evt";
extern lv_obj_t *ui_Screen_Lottie;
// 【解决方案二】正念练习活跃状态标志(用于禁用手势切换)
volatile bool g_mindfulness_active = false;
// 【性能优化】手势防抖机制:防止快速滑动时触发多次切换
static uint32_t last_gesture_time = 0;
#define GESTURE_DEBOUNCE_MS 300 // 300ms防抖间隔
static bool is_gesture_allowed(void) {
uint32_t now = lv_tick_get();
if (now - last_gesture_time < GESTURE_DEBOUNCE_MS) {
return false; // 忽略过快的手势
}
last_gesture_time = now;
return true;
}
#if LV_USE_GUIDER_SIMULATOR && LV_USE_FREEMASTER
#include "freemaster_client.h"
#endif
//包含点击事件:休眠】【产品说明】【使用说明】【恢复出厂设置】【亮度调节】
static void screen_sortition_btn_history_event_handler(lv_event_t *e);
static void sortition_text_back_to_components(lv_event_t *e);
static void screen_home_weather_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_GESTURE:
{
if (!is_gesture_allowed()) return; // 防抖检查
lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active());
switch(dir) {
case LV_DIR_LEFT:
{
lv_indev_wait_release(lv_indev_active());
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_time, guider_ui.screen_time_del, &guider_ui.screen_home_weather_del, setup_scr_screen_time, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, 2);
break;
}
case LV_DIR_RIGHT:
{
lv_indev_wait_release(lv_indev_active());
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del, &guider_ui.screen_home_weather_del, setup_scr_screen_component, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, UI_IDX_COMPONENTS);
break;
}
case LV_DIR_BOTTOM:
{
lv_indev_wait_release(lv_indev_active());
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_home_weather_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_OVER_BOTTOM, 150, 0, false, true, UI_IDX_PULLDOWN);
break;
}
default:
break;
}
break;
}
default:
break;
}
}
void events_init_screen_home_weather (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_home_weather, ui_event_gesture_cb, LV_EVENT_ALL, ui);
}
void events_init_screen_time (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_time, ui_event_gesture_cb, LV_EVENT_ALL, ui);
}
static void screen_pull_down_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_GESTURE:
{
if (!is_gesture_allowed()) return; // 防抖检查
lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active());
switch(dir) {
case LV_DIR_TOP:
{
lv_indev_wait_release(lv_indev_active());
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_home_weather, guider_ui.screen_home_weather_del, &guider_ui.screen_pull_down_del, setup_scr_screen_home_weather, LV_SCR_LOAD_ANIM_MOVE_TOP, 150, 0, false, true, 1);
break;
}
default:
break;
}
break;
}
default:
break;
}
}
static void screen_pull_down_btn_Wifi_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
break;
}
default:
break;
}
}
static void screen_pull_down_btn_blu_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
break;
}
default:
break;
}
}
static void screen_pull_down_btn_set_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_set, guider_ui.screen_set_del, &guider_ui.screen_pull_down_del, setup_scr_screen_set, LV_SCR_LOAD_ANIM_FADE_ON, 150, 0, false, true, UI_NAV_PAGE_IDX_KEEP);
break;
}
default:
break;
}
}
static void screen_pull_down_btn_sleep_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_sleep, guider_ui.screen_sleep_del, &guider_ui.screen_pull_down_del, setup_scr_screen_sleep, LV_SCR_LOAD_ANIM_FADE_ON, 150, 0, false, true, UI_NAV_PAGE_IDX_KEEP);
break;
}
default:
break;
}
}
static void screen_pull_down_btn_restart_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
if (panel_handle) {
panel_qspi_amoled_set_brightness(panel_handle, 0);
vTaskDelay(pdMS_TO_TICKS(50));
esp_lcd_panel_disp_on_off(panel_handle, false);
vTaskDelay(pdMS_TO_TICKS(50));
}
esp_restart();
break;
}
default:
break;
}
}
#include "esp_lcd_panel_ops.h" // 必须包含这个头文件
#include "driver/rtc_io.h"
static void screen_pull_down_btn_turnoff_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_CLICKED:
{
ESP_LOGI("SYSTEM", "正在执行深度关屏程序...");
// 1. 等待手指离开(防止秒醒,这是你之前的改动)
while (gpio_get_level(EXAMPLE_PIN_NUM_TOUCH_INT) == 0) {
vTaskDelay(pdMS_TO_TICKS(10));
}
vTaskDelay(pdMS_TO_TICKS(100));
// 2. 【核心】给屏幕发送关闭显示指令
// 这会让 AMOLED 像素点停止发光,进入低功耗睡眠模式
if (panel_handle != NULL) {
esp_lcd_panel_disp_on_off(panel_handle, false); // 关闭显示
}
// 3. 【核心】拉低复位引脚并锁定
// IO17 是你的 LCD_RST。在睡觉前把它拉低,强制屏幕处于复位状态
rtc_gpio_init(EXAMPLE_PIN_NUM_LCD_RST);
rtc_gpio_set_direction(EXAMPLE_PIN_NUM_LCD_RST, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(EXAMPLE_PIN_NUM_LCD_RST, 0);
rtc_gpio_hold_en(EXAMPLE_PIN_NUM_LCD_RST); // 即使 CPU 睡着了,IO17 也会一直保持低电平
ESP_LOGI("SYSTEM", "Good night!");
vTaskDelay(pdMS_TO_TICKS(100));
// 5. 正式关机
esp_deep_sleep_start();
break;
}
default:
break;
}
}
void events_init_screen_pull_down (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_pull_down, ui_event_gesture_cb, LV_EVENT_ALL, ui);
// lv_obj_add_event_cb(ui->screen_pull_down_btn_Wifi, screen_pull_down_btn_Wifi_event_handler, LV_EVENT_ALL, ui);
// lv_obj_add_event_cb(ui->screen_pull_down_btn_blu, screen_pull_down_btn_blu_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_pull_down_btn_set, screen_pull_down_btn_set_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_pull_down_btn_sleep, screen_pull_down_btn_sleep_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_pull_down_btn_restart, screen_pull_down_btn_restart_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_pull_down_btn_turnoff, screen_pull_down_btn_turnoff_event_handler, LV_EVENT_ALL, ui);
}
#include "esp_wifi.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
volatile bool is_system_sleeping = false;
extern esp_lcd_panel_handle_t panel_handle; // 引用屏幕句柄
extern void ble_app_advertise(void);
// 1. 声明外部函数
extern void set_auto_sleep_timeout(uint32_t ms);
static void preload_black_frame_before_wakeup(void)
{
if (panel_handle == NULL) {
return;
}
/* 先把面板 GRAM 清成黑色,避免开屏瞬间显示上一次残留的 sleep 界面 */
static lv_color_t black_rows[EXAMPLE_LCD_H_RES * 8];
for (int y = 0; y < EXAMPLE_LCD_V_RES; y += 8) {
int row_count = EXAMPLE_LCD_V_RES - y;
if (row_count > 8) {
row_count = 8;
}
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, EXAMPLE_LCD_H_RES, y + row_count, black_rows);
}
}
// 2. 定义时间常量 (毫秒)
#define TIMEOUT_30S 30000
#define TIMEOUT_5MIN (5 * 60 * 1000)
#define TIMEOUT_10MIN (10 * 60 * 1000)
#define TIMEOUT_NEVER 0
// 这会让选中的按钮保持 "CHECKED" 状态(通常变色),其他的变回普通状态
static void update_sleep_btn_ui(lv_obj_t *active_btn, lv_ui *ui) {
// 清除所有按钮的选中状态
lv_obj_remove_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED);
lv_obj_remove_state(ui->screen_sleep_btn_5min, LV_STATE_CHECKED);
lv_obj_remove_state(ui->screen_sleep_btn_10min, LV_STATE_CHECKED);
lv_obj_remove_state(ui->screen_sleep_btn_never, LV_STATE_CHECKED);
// 给当前点击的按钮加上选中状态
if (active_btn) {
lv_obj_add_state(active_btn, LV_STATE_CHECKED);
}
}
// 进入/退出休眠的控制函数
void toggle_sleep_mode(bool enter_sleep)
{
if (enter_sleep)
{
if (is_system_sleeping) return;
// 关屏
if (panel_handle) {
ESP_LOGI("SLEEP", "System Sleeping...");
// 尝试调用设置亮度函数,不同驱动可能名字不同,通常是这个
panel_qspi_amoled_set_brightness(panel_handle, 0);
vTaskDelay(pdMS_TO_TICKS(50)); // 给屏幕一点时间反应
// 2. 发送关闭显示指令
esp_lcd_panel_disp_on_off(panel_handle, false);
}
// 关 WiFi
esp_wifi_stop();
// 如果已连接,断开连接
//ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
// 停止广播
ble_gap_adv_stop();
is_system_sleeping = true;
}
else
{
if (!is_system_sleeping) return;
/* 开屏前先立即回到默认页,避免保留在 sleep screen 导致后续手势失效 */
ui_nav_apply_wake_to_lottie_home();
if (guider_ui.screen_pull_down_del || guider_ui.screen_pull_down == NULL)
{
guider_ui.screen_pull_down = NULL;
guider_ui.screen_pull_down_del = true;
setup_scr_screen_pull_down(&guider_ui);
}
preload_black_frame_before_wakeup();
// 开屏
if (panel_handle) {
ESP_LOGI("SLEEP", "System Waking up...");
esp_lcd_panel_disp_on_off(panel_handle, true);
vTaskDelay(pdMS_TO_TICKS(50));
panel_qspi_amoled_set_brightness(panel_handle, 255);
}
// 开 WiFi
esp_wifi_start();
esp_wifi_connect();
ble_app_advertise();
is_system_sleeping = false;
}
}
static void screen_sleep_btn_10min_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_ui *ui = (lv_ui *)lv_event_get_user_data(e);
switch (code) {
case LV_EVENT_CLICKED:
case LV_EVENT_PRESSED:
{
set_auto_sleep_timeout(TIMEOUT_10MIN);
commit_auto_sleep_timeout_if_dirty();
update_sleep_btn_ui(ui->screen_sleep_btn_10min, ui);
break;
}
default:
break;
}
}
static void screen_sleep_btn_5min_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_ui *ui = (lv_ui *)lv_event_get_user_data(e);
switch (code) {
case LV_EVENT_CLICKED:
case LV_EVENT_PRESSED:
{
set_auto_sleep_timeout(TIMEOUT_5MIN);
commit_auto_sleep_timeout_if_dirty();
update_sleep_btn_ui(ui->screen_sleep_btn_5min, ui);
break;
}
default:
break;
}
}
static void screen_sleep_btn_never_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_ui *ui = (lv_ui *)lv_event_get_user_data(e);
switch (code) {
case LV_EVENT_CLICKED:
case LV_EVENT_PRESSED:
{
set_auto_sleep_timeout(TIMEOUT_NEVER);
commit_auto_sleep_timeout_if_dirty();
update_sleep_btn_ui(ui->screen_sleep_btn_never, ui);
break;
}
default:
break;
}
}
static void screen_sleep_btn_30s_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_ui *ui = (lv_ui *)lv_event_get_user_data(e); // 获取 UI 句柄
switch (code) {
case LV_EVENT_CLICKED: // 建议用 CLICKED 代替 PRESSED
case LV_EVENT_PRESSED:
{
set_auto_sleep_timeout(TIMEOUT_30S);
commit_auto_sleep_timeout_if_dirty();
update_sleep_btn_ui(ui->screen_sleep_btn_30s, ui);
break;
}
default:
break;
}
}
static void screen_sleep_img_sleep_back_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
commit_auto_sleep_timeout_if_dirty();
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_sleep_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, UI_IDX_PULLDOWN);
break;
}
default:
break;
}
}
static void screen_sleep_btn_now_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
toggle_sleep_mode(true);
break;
}
default:
break;
}
}
extern uint32_t get_auto_sleep_timeout(void);
void events_init_screen_sleep (lv_ui *ui)
{
/* Guider 可能只有 img 或只有 btn 作为返回;当前工程 setup 仅创建 screen_sleep_btn_set_back */
lv_obj_t *sleep_back_target = ui->screen_sleep_img_sleep_back
? ui->screen_sleep_img_sleep_back
: ui->screen_sleep_btn_set_back;
ESP_LOGD(TAG_SLEEP_EVT, "enter");
ESP_LOGD(TAG_SLEEP_EVT, "ptrs: 10min=%p 5min=%p never=%p 30s=%p img_back=%p btn_set_back=%p back_use=%p now=%p",
(void *)ui->screen_sleep_btn_10min, (void *)ui->screen_sleep_btn_5min,
(void *)ui->screen_sleep_btn_never, (void *)ui->screen_sleep_btn_30s,
(void *)ui->screen_sleep_img_sleep_back, (void *)ui->screen_sleep_btn_set_back,
(void *)sleep_back_target, (void *)ui->screen_sleep_btn_now);
if (!ui->screen_sleep_btn_10min || !ui->screen_sleep_btn_5min || !ui->screen_sleep_btn_never ||
!ui->screen_sleep_btn_30s || !sleep_back_target || !ui->screen_sleep_btn_now) {
ESP_LOGE(TAG_SLEEP_EVT, "NULL widget ptr (check gui_guider.h vs setup_scr_screen_sleep.c)");
return;
}
ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb 10min");
lv_obj_add_event_cb(ui->screen_sleep_btn_10min, screen_sleep_btn_10min_event_handler, LV_EVENT_ALL, ui);
ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb 5min");
lv_obj_add_event_cb(ui->screen_sleep_btn_5min, screen_sleep_btn_5min_event_handler, LV_EVENT_ALL, ui);
ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb never");
lv_obj_add_event_cb(ui->screen_sleep_btn_never, screen_sleep_btn_never_event_handler, LV_EVENT_ALL, ui);
ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb 30s");
lv_obj_add_event_cb(ui->screen_sleep_btn_30s, screen_sleep_btn_30s_event_handler, LV_EVENT_ALL, ui);
ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb sleep_back (img or btn_set_back)");
lv_obj_add_event_cb(sleep_back_target, screen_sleep_img_sleep_back_event_handler, LV_EVENT_ALL, ui);
ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb btn_now");
lv_obj_add_event_cb(ui->screen_sleep_btn_now, screen_sleep_btn_now_event_handler, LV_EVENT_ALL, ui);
ESP_LOGD(TAG_SLEEP_EVT, "get_auto_sleep_timeout");
uint32_t current_ms = get_auto_sleep_timeout();
ESP_LOGD(TAG_SLEEP_EVT, "current_ms=%" PRIu32, (uint32_t)current_ms);
ESP_LOGD(TAG_SLEEP_EVT, "remove_state all checked");
lv_obj_remove_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED);
lv_obj_remove_state(ui->screen_sleep_btn_5min, LV_STATE_CHECKED);
lv_obj_remove_state(ui->screen_sleep_btn_10min, LV_STATE_CHECKED);
lv_obj_remove_state(ui->screen_sleep_btn_never, LV_STATE_CHECKED);
ESP_LOGD(TAG_SLEEP_EVT, "add_state default branch");
if (current_ms == TIMEOUT_30S) {
lv_obj_add_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED);
} else if (current_ms == TIMEOUT_5MIN) {
lv_obj_add_state(ui->screen_sleep_btn_5min, LV_STATE_CHECKED);
} else if (current_ms == TIMEOUT_10MIN) {
lv_obj_add_state(ui->screen_sleep_btn_10min, LV_STATE_CHECKED);
} else if (current_ms == TIMEOUT_NEVER) {
lv_obj_add_state(ui->screen_sleep_btn_never, LV_STATE_CHECKED);
} else {
lv_obj_add_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED);
}
ESP_LOGD(TAG_SLEEP_EVT, "leave ok");
}
extern int get_system_brightness_percent(void);
// 2. 滑动条事件回调
static void screen_set_slider_brightness_event_handler(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_VALUE_CHANGED: // 数值改变时触发
{
lv_obj_t * slider = lv_event_get_target(e);
// 获取滑块当前值 (0-100)
int val = (int)lv_slider_get_value(slider);
// 调用设置函数
set_system_brightness(val);
break;
}
case LV_EVENT_RELEASED:
ble_request_send_brightness();
break;
default:
break;
}
}
static void screen_set_btn_set_back_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
commit_brightness_if_dirty();
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_set_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, UI_IDX_PULLDOWN);
break;
}
default:
break;
}
}
// ================= 使用指南翻页数据与逻辑 =================
// 1. 将长文本拆分为 4 页
static const char * guide_pages[] = {
// 第 1 页:产品安装
"一、产品安装\n"
"1、将身体支架使用附赠3M背胶固定牢固安装于车机屏幕后方,确保支架水平稳定、无松动或晃动。\n"
"2、使用支架自带的连接线一端接入中控内端子接口,为机器人提供稳定供电与数据通路。\n",
"3、将机器人主机底部标准Type-C接口,对准支架前端预留的Type-C公座,垂直、平稳插入直至完全卡合(有清晰“咔嗒”入位感)完成物理安装与通信连接。\n"
"4、安装完毕后,机器人即通过支架获取电力并接入车机系统,无需额外配置。",
// 第 2 页:开机与关机
"二、开机与关机\n"
"1、本产品采用智能随车电源管理设计,无物理开关。\n"
" 全程自动运行:\n"
" - 当车辆启动并进入ACC或ON档位(仪表盘通电)时,机器人于3秒内完成自检并自动开机,进入待命状态。\n",
" - 当车辆熄火断电后,机器人于30秒内自动进入低功耗休眠模式,停止所有功能响应。(哨兵模式除外)。\n"
"2、该机制严格同步车辆电源状态,杜绝误操作与冗余功耗,真正实现“即插即用、上车即控、下车即止。”",
// 第 3 页:网络连接
"三、网络连接与系统联动\n"
"1、机器人与车机完成首次绑定(通过设置-wifi-点击配置)后,将自动识别、认证并连接车机内置的专用Wi-Fi热点,全程无需手动选择网络或输入密码。\n"
"2、连接成功后,APP端显示“成功在线”,即可实时启用以下核心能力",
// 第 4 页:功能概览
"四、核心功能概览\n"
"1、智能车况表情系统:基于真实车况动态生成友好化表情界面——兼顾信息传达效率与视觉亲和力,让车辆状态“一眼可知、一目了然”。\n",
"2、主动情绪陪伴:结合时间、天气、驾驶行为与行程特征,提供有温度的交互体验,以恰到好处的关怀缓解驾驶压力,提升出行幸福感。\n"
"3、表情包下载和ota需要热点内打开兼容模式。\n",
};
#define GUIDE_TOTAL_PAGES 7
// 2. 定义静态变量用于控制状态
static int current_guide_page = 0; // 当前页码
static lv_obj_t * label_guide_content = NULL; // 用于显示内容的标签对象
static lv_obj_t * label_guide_title = NULL; // 用于显示标题的标签对象 (显示页码)
static lv_obj_t * btn_guide_prev = NULL; // 上一页按钮
static lv_obj_t * btn_guide_next = NULL; // 下一页按钮
// 辅助函数:更新界面内容和按钮状态
static void update_guide_ui(void)
{
// 更新文本内容(不再触发重排)
if (label_guide_content) {
lv_label_set_text(label_guide_content, guide_pages[current_guide_page]);
}
// 更新标题 (显示页码 1/4)
if (label_guide_title) {
char title_buf[32];
snprintf(title_buf, sizeof(title_buf), "产品说明 (%d/%d)", current_guide_page + 1, GUIDE_TOTAL_PAGES);
lv_label_set_text(label_guide_title, title_buf);
}
// 按钮状态控制(你原来的逻辑保持不变)
if (btn_guide_prev) {
if (current_guide_page == 0) {
lv_obj_add_state(btn_guide_prev, LV_STATE_DISABLED); // 禁用状态
lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x555555), 0); // 变灰
} else {
lv_obj_remove_state(btn_guide_prev, LV_STATE_DISABLED);
lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), 0);
}
}
if (btn_guide_next) {
if (current_guide_page == GUIDE_TOTAL_PAGES - 1) {
lv_obj_add_state(btn_guide_next, LV_STATE_DISABLED);
lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x555555), 0); // 变灰
} else {
lv_obj_remove_state(btn_guide_next, LV_STATE_DISABLED);
lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), 0);
}
}
}
// 按钮回调:上一页
static void guide_prev_event_handler(lv_event_t * e) {
if (current_guide_page > 0) {
current_guide_page--;
update_guide_ui();
}
}
// 按钮回调:下一页
static void guide_next_event_handler(lv_event_t * e) {
if (current_guide_page < GUIDE_TOTAL_PAGES - 1) {
current_guide_page++;
update_guide_ui();
}
}
// [辅助函数] 用于关闭消息框
static void msgbox_close_event_handler(lv_event_t * e)
{
lv_obj_t * mbox = (lv_obj_t *)lv_event_get_user_data(e);
if(mbox && lv_obj_is_valid(mbox)) {
lv_msgbox_close(mbox); // LVGL v9 关闭消息框的专用函数
}
}
/** 设置页 msgbox 底部按钮:垂直加高;使用说明三键为 iconfont(见 `GUIDE_FOOTER_ICON_*`) */
#define SCREEN_SET_MSGBOX_BTN_PAD_VER 24
#define SCREEN_SET_MSGBOX_BTN_PAD_HOR 22
/** 使用说明「上一页/下一页/关闭」:与版本信息按钮大小保持一致 */
#define SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE 22
#define SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE 24
/** iconfont(`lv_font_iconfont_25.c`):U+E644 、U+E66C 、U+E655  */
#define GUIDE_FOOTER_ICON_PREV "\xEE\x99\x84"
#define GUIDE_FOOTER_ICON_NEXT "\xEE\x99\xAC"
#define GUIDE_FOOTER_ICON_CLOSE "\xEE\x99\x95"
/** 版本信息 / 系统提示等 msgbox:U+E67C (取消)、U+E64B (确定) */
#define MSGBOX_FOOTER_ICON_CANCEL "\xEE\x99\xBC"
#define MSGBOX_FOOTER_ICON_OK "\xEE\x99\x8B"
/** 版本信息 / 系统提示等 footer 按钮:图标字必须用 lv_font_iconfont_25(与按钮、label 均显式设置,避免继承 MiSans) */
static void screen_set_msgbox_footer_btn_label_iconfont(lv_obj_t *btn)
{
if (btn == NULL) {
return;
}
const lv_style_selector_t sel_d = LV_PART_MAIN | LV_STATE_DEFAULT;
const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED;
const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED;
const lv_style_selector_t sel_dis = LV_PART_MAIN | LV_STATE_DISABLED;
const lv_style_selector_t sel_f = LV_PART_MAIN | LV_STATE_FOCUSED;
lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_d);
lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_p);
lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_c);
lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_dis);
lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_f);
lv_obj_t *lbl = lv_obj_get_child(btn, 0);
if (lbl != NULL && lv_obj_is_valid(lbl)) {
lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_d);
lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_dis);
lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_p);
lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_c);
lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_f);
}
}
static void screen_set_apply_msgbox_footer_btn_size(lv_obj_t *btn)
{
if (btn == NULL) {
return;
}
lv_obj_set_style_pad_hor(btn, SCREEN_SET_MSGBOX_BTN_PAD_HOR, 0);
lv_obj_set_style_pad_ver(btn, SCREEN_SET_MSGBOX_BTN_PAD_VER, 0);
}
static void screen_set_apply_msgbox_footer_btn_size_guide(lv_obj_t *btn)
{
if (btn == NULL) {
return;
}
lv_obj_set_style_pad_hor(btn, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, 0);
lv_obj_set_style_pad_ver(btn, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, 0);
}
static void screen_set_btn_guide_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_CLICKED:
{
// 1. 重置页码
current_guide_page = 0;
// 2. 创建消息框
lv_obj_t * mbox = lv_msgbox_create(NULL);
// 3. 初始化标题和内容
// 捕获对象指针以便后续更新
label_guide_title = lv_msgbox_add_title(mbox, "产品说明 (1/4)");
label_guide_content = lv_msgbox_add_text(mbox, guide_pages[0]);
lv_obj_set_size(label_guide_content, 390, 285);
lv_label_set_long_mode(label_guide_content, LV_LABEL_LONG_WRAP);
// ================= 按钮区域 =================
btn_guide_prev = lv_msgbox_add_footer_button(mbox, GUIDE_FOOTER_ICON_PREV);
lv_obj_add_event_cb(btn_guide_prev, guide_prev_event_handler, LV_EVENT_CLICKED, NULL);
screen_set_msgbox_footer_btn_label_iconfont(btn_guide_prev);
btn_guide_next = lv_msgbox_add_footer_button(mbox, GUIDE_FOOTER_ICON_NEXT);
lv_obj_add_event_cb(btn_guide_next, guide_next_event_handler, LV_EVENT_CLICKED, NULL);
screen_set_msgbox_footer_btn_label_iconfont(btn_guide_next);
lv_obj_t * btn_close = lv_msgbox_add_footer_button(mbox, GUIDE_FOOTER_ICON_CLOSE);
lv_obj_add_event_cb(btn_close, msgbox_close_event_handler, LV_EVENT_CLICKED, mbox);
screen_set_msgbox_footer_btn_label_iconfont(btn_close);
// ================= 样式美化 =================
// --- A. mbox 整体设置 ---
lv_obj_set_width(mbox, 502);
lv_obj_set_height(mbox, 410);
lv_obj_center(mbox);
// 背景全黑
lv_obj_set_style_bg_color(mbox, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(mbox, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(mbox, 0, 0);
/* 勿对 mbox 设置 text_font,避免继承覆盖 footer 的 iconfont(否则 PUA 显示为方框) */
lv_obj_set_style_text_line_space(mbox, 10, 0); // 增加行间距,阅读更舒适
lv_obj_set_style_text_color(mbox, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(label_guide_title, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_font(label_guide_content, &lv_font_montserrat_14, 0);
// --- B. 标题区域设置 (Header) ---
// 解决标题背景白色的问题
lv_obj_t * header = lv_msgbox_get_header(mbox);
if (header) {
lv_obj_set_style_bg_color(header, lv_color_hex(0x000000), 0); // 强制黑底
lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0);
lv_obj_set_style_pad_left(header, 30, 0); // 标题左移一点
}
// 单独设置标题文字颜色
lv_obj_set_style_text_color(label_guide_title, lv_color_hex(0x000000), 0);
// --- C. 内容区域设置 (Content) ---
lv_obj_t * content = lv_msgbox_get_content(mbox);
if (content) {
lv_obj_set_style_bg_color(content, lv_color_hex(0x000000), 0);
lv_obj_set_style_pad_left(content, 50, 0);
lv_obj_set_style_pad_bottom(content, 6, 0);
}
// --- D. 按钮区域设置 (Footer) ---
lv_obj_t * footer = lv_msgbox_get_footer(mbox);
if (footer) {
lv_obj_set_style_bg_color(footer, lv_color_hex(0x000000), 0);
lv_obj_set_flex_flow(footer, LV_FLEX_FLOW_ROW);
/* 三键横向均匀分布(首尾留白与键间距一致) */
lv_obj_set_flex_align(footer, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(footer, 0, 0);
lv_obj_set_style_pad_left(footer, 6, 0);
lv_obj_set_style_pad_right(footer, 6, 0);
lv_obj_set_style_pad_top(footer, 4, 0);
}
// --- E. 按钮本身样式 ---
// 上一页按钮 (淡绿色);按下/选中时与常态完全一致(不显式变化)
lv_obj_set_size(btn_guide_prev, 100, 30);
lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), 0);
lv_obj_set_style_text_color(btn_guide_prev, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_radius(btn_guide_prev, 20, 0);
lv_obj_set_style_translate_x(btn_guide_prev, 20, 0); // 向右偏移20像素
screen_set_apply_msgbox_footer_btn_size_guide(btn_guide_prev);
{
const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED;
const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED;
lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), sel_p);
lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), sel_c);
lv_obj_set_style_bg_opa(btn_guide_prev, LV_OPA_COVER, sel_p);
lv_obj_set_style_bg_opa(btn_guide_prev, LV_OPA_COVER, sel_c);
lv_obj_set_style_text_color(btn_guide_prev, lv_color_hex(0xFFFFFF), sel_p);
lv_obj_set_style_text_color(btn_guide_prev, lv_color_hex(0xFFFFFF), sel_c);
lv_obj_set_style_radius(btn_guide_prev, 20, sel_p);
lv_obj_set_style_radius(btn_guide_prev, 20, sel_c);
lv_obj_set_style_pad_hor(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_p);
lv_obj_set_style_pad_hor(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_c);
lv_obj_set_style_pad_ver(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_p);
lv_obj_set_style_pad_ver(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_c);
lv_obj_set_style_border_width(btn_guide_prev, 0, sel_p);
lv_obj_set_style_border_width(btn_guide_prev, 0, sel_c);
lv_obj_set_style_shadow_width(btn_guide_prev, 0, sel_p);
lv_obj_set_style_shadow_width(btn_guide_prev, 0, sel_c);
lv_obj_set_style_translate_x(btn_guide_prev, 20, sel_p);
lv_obj_set_style_translate_y(btn_guide_prev, 0, sel_p);
lv_obj_set_style_translate_x(btn_guide_prev, 20, sel_c);
lv_obj_set_style_translate_y(btn_guide_prev, 0, sel_c);
}
// 下一页按钮 (淡绿色);按下/选中时与常态完全一致(不显式变化)
lv_obj_set_size(btn_guide_next, 100, 30);
lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), 0);
lv_obj_set_style_text_color(btn_guide_next, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_radius(btn_guide_next, 20, 0);
screen_set_apply_msgbox_footer_btn_size_guide(btn_guide_next);
{
const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED;
const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED;
lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), sel_p);
lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), sel_c);
lv_obj_set_style_bg_opa(btn_guide_next, LV_OPA_COVER, sel_p);
lv_obj_set_style_bg_opa(btn_guide_next, LV_OPA_COVER, sel_c);
lv_obj_set_style_text_color(btn_guide_next, lv_color_hex(0xFFFFFF), sel_p);
lv_obj_set_style_text_color(btn_guide_next, lv_color_hex(0xFFFFFF), sel_c);
lv_obj_set_style_radius(btn_guide_next, 20, sel_p);
lv_obj_set_style_radius(btn_guide_next, 20, sel_c);
lv_obj_set_style_pad_hor(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_p);
lv_obj_set_style_pad_hor(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_c);
lv_obj_set_style_pad_ver(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_p);
lv_obj_set_style_pad_ver(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_c);
lv_obj_set_style_border_width(btn_guide_next, 0, sel_p);
lv_obj_set_style_border_width(btn_guide_next, 0, sel_c);
lv_obj_set_style_shadow_width(btn_guide_next, 0, sel_p);
lv_obj_set_style_shadow_width(btn_guide_next, 0, sel_c);
lv_obj_set_style_translate_x(btn_guide_next, 0, sel_p);
lv_obj_set_style_translate_y(btn_guide_next, 0, sel_p);
lv_obj_set_style_translate_x(btn_guide_next, 0, sel_c);
lv_obj_set_style_translate_y(btn_guide_next, 0, sel_c);
}
// 关闭按钮 (灰色);按下/选中时与常态完全一致(不显式变化)
lv_obj_set_size(btn_close, 100, 30);
lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x808080), 0);
lv_obj_set_style_text_color(btn_close, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_radius(btn_close, 20, 0);
lv_obj_set_style_translate_x(btn_close, -20, 0); // 向左偏移20像素
screen_set_apply_msgbox_footer_btn_size_guide(btn_close);
{
const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED;
const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED;
lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x808080), sel_p);
lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x808080), sel_c);
lv_obj_set_style_bg_opa(btn_close, LV_OPA_COVER, sel_p);
lv_obj_set_style_bg_opa(btn_close, LV_OPA_COVER, sel_c);
lv_obj_set_style_text_color(btn_close, lv_color_hex(0xFFFFFF), sel_p);
lv_obj_set_style_text_color(btn_close, lv_color_hex(0xFFFFFF), sel_c);
lv_obj_set_style_radius(btn_close, 20, sel_p);
lv_obj_set_style_radius(btn_close, 20, sel_c);
lv_obj_set_style_pad_hor(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_p);
lv_obj_set_style_pad_hor(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_c);
lv_obj_set_style_pad_ver(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_p);
lv_obj_set_style_pad_ver(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_c);
lv_obj_set_style_border_width(btn_close, 0, sel_p);
lv_obj_set_style_border_width(btn_close, 0, sel_c);
lv_obj_set_style_shadow_width(btn_close, 0, sel_p);
lv_obj_set_style_shadow_width(btn_close, 0, sel_c);
lv_obj_set_style_translate_x(btn_close, -20, sel_p);
lv_obj_set_style_translate_y(btn_close, 0, sel_p);
lv_obj_set_style_translate_x(btn_close, -20, sel_c);
lv_obj_set_style_translate_y(btn_close, 0, sel_c);
}
screen_set_msgbox_footer_btn_label_iconfont(btn_guide_prev);
screen_set_msgbox_footer_btn_label_iconfont(btn_guide_next);
screen_set_msgbox_footer_btn_label_iconfont(btn_close);
// 4. 初始化 UI 状态 (应用第一页的禁用逻辑)
update_guide_ui();
break;
}
default:
break;
}
}
static void screen_set_btn_information_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_CLICKED:
{
const char * version_body = "软件版本号:v1.0.3\n"
"版本类型:首发正式版\n"
"发布内容:\n"
"(1)实现车载机器人基础功能的完整运行与控制\n"
"(2)完成与车机端的通信连接与指令交互\n"
"(3)支持基础状态显示与功能响应\n"
"(4)系统运行稳定,满足当前阶段的功能与性能要求";
// 1. 创建消息框
lv_obj_t * mbox = lv_msgbox_create(NULL);
// 2. 正文:首行「情绪机器人」单独居中,其余左对齐
lv_obj_t * content = lv_msgbox_get_content(mbox);
lv_obj_t * title_label = lv_label_create(content);
lv_label_set_text(title_label, "情绪机器人");
lv_obj_set_width(title_label, lv_pct(100));
lv_obj_set_style_text_align(title_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_color(title_label, lv_color_hex(0xFFFFFF), 0);
lv_obj_t * content_label = lv_label_create(content);
lv_label_set_text(content_label, version_body);
lv_obj_set_width(content_label, lv_pct(100));
lv_obj_set_style_text_align(content_label, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_text_color(content_label, lv_color_hex(0xFFFFFF), 0);
// 3. 添加按钮(取消 → iconfont )
lv_obj_t * btn = lv_msgbox_add_footer_button(mbox, "取消");
lv_obj_add_event_cb(btn, msgbox_close_event_handler, LV_EVENT_CLICKED, mbox);
lv_obj_set_style_text_font(btn, &lv_font_montserrat_14, 0);
// ================= 样式设置 =================
// --- A. mbox 整体设置 ---
lv_obj_set_width(mbox, 502);
lv_obj_set_height(mbox, 410);
lv_obj_center(mbox);
// 背景全黑
lv_obj_set_style_bg_color(mbox, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(mbox, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(mbox, 0, 0); // 去掉边框
/* 勿对 mbox 设置 text_font:可继承到 footer 按钮,MiSans 无 PUA 字形会显示为方框;正文仅用 MiSans */
lv_obj_set_style_text_line_space(mbox, 8, 0);
lv_obj_set_style_text_color(mbox, lv_color_hex(0xFFFFFF), 0);
if (title_label) {
lv_obj_set_style_text_font(title_label, &lv_font_montserrat_14, 0);
}
if (content_label) {
lv_obj_set_style_text_font(content_label, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_line_space(content_label, 8, 0);
}
// --- B. 内容区域 (Content):与左、上边缘约 20px
if (content) {
lv_obj_set_style_bg_color(content, lv_color_hex(0x000000), 0);
lv_obj_set_style_pad_left(content, 20, 0);
lv_obj_set_style_pad_top(content, 20, 0);
/* 版本信息正文区域高度减少 20,footer 会随布局整体上移 */
lv_obj_update_layout(mbox);
int32_t content_h = lv_obj_get_height(content);
if (content_h > 20) {
lv_obj_set_height(content, content_h - 20);
}
}
lv_obj_remove_flag(mbox, LV_OBJ_FLAG_SCROLLABLE);
{
lv_obj_t * mbox_parent = lv_obj_get_parent(mbox);
if (mbox_parent) {
lv_obj_remove_flag(mbox_parent, LV_OBJ_FLAG_SCROLLABLE);
}
}
if (content) {
lv_obj_remove_flag(content, LV_OBJ_FLAG_SCROLLABLE);
}
if (title_label) {
lv_obj_remove_flag(title_label, LV_OBJ_FLAG_SCROLLABLE);
}
if (content_label) {
lv_obj_remove_flag(content_label, LV_OBJ_FLAG_SCROLLABLE);
}
// --- C. 按钮区域设置 (Footer) ---
lv_obj_set_style_bg_color(btn, lv_color_hex(0x808080), 0);
lv_obj_set_style_text_color(btn, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_radius(btn, 20, 0);
screen_set_apply_msgbox_footer_btn_size(btn);
break;
}
default:
break;
}
}
// 确保声明了外部函数 (如果文件顶部没写)
extern void set_auto_sleep_timeout(uint32_t ms);
extern void set_system_brightness(int percent);
// 恢复出厂设置的确认回调函数
static void factory_reset_confirm_cb(lv_event_t *e)
{
lv_obj_t *mbox = (lv_obj_t *)lv_event_get_user_data(e);
// 关闭消息框
if(mbox && lv_obj_is_valid(mbox)) {
lv_msgbox_close(mbox);
}
// ================= 执行恢复出厂设置逻辑 =================
/* 恢复休眠为 0 分钟并立即写入 NVS。仅用 set_auto_sleep_timeout 会在 50ms 定时器里写盘,
* esp_restart 太快时回调来不及执行,且若当前已是 300000 会直接 return 导致永不落盘。 */
persist_auto_sleep_timeout_ms_now(0);
// 2. 恢复亮度为 80%
set_system_brightness(80);
// 3. 刷新亮度滑块 UI
if (guider_ui.screen_set_slider_light != NULL && lv_obj_is_valid(guider_ui.screen_set_slider_light)) {
lv_slider_set_value(guider_ui.screen_set_slider_light, 75, LV_ANIM_ON);
}
// 4. 重启设备以完成恢复
if (panel_handle) {
panel_qspi_amoled_set_brightness(panel_handle, 0);
vTaskDelay(pdMS_TO_TICKS(50));
esp_lcd_panel_disp_on_off(panel_handle, false);
vTaskDelay(pdMS_TO_TICKS(50));
}
esp_restart();
}
static void screen_set_btn_set_recover_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_CLICKED: // 建议用 CLICKED
{
// ================= 弹出确认对话框 =================
lv_obj_t * mbox = lv_msgbox_create(NULL);
lv_obj_t * title = lv_msgbox_add_title(mbox, "系统提示");
lv_obj_t * content = lv_msgbox_add_text(mbox, "是否恢复出厂设置?\n\n恢复后将重置所有设置并重启设备");
//内容居中
lv_obj_set_style_text_align(content, LV_TEXT_ALIGN_CENTER, 0);
// 按钮(确定 → ,取消 → )
lv_obj_t * btn1 = lv_msgbox_add_footer_button(mbox, "确认");
lv_obj_t * btn2 = lv_msgbox_add_footer_button(mbox, "取消");
lv_obj_add_event_cb(btn1, factory_reset_confirm_cb, LV_EVENT_CLICKED, mbox);
lv_obj_add_event_cb(btn2, msgbox_close_event_handler, LV_EVENT_CLICKED, mbox);
lv_obj_set_style_text_font(btn1, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_font(btn2, &lv_font_montserrat_14, 0);
// --- 样式美化 (保持全黑风格) ---
lv_obj_set_width(mbox, 400);
lv_obj_center(mbox);
// 弹窗外部遮罩改为纯黑
lv_obj_t * backdrop = lv_obj_get_parent(mbox);
if(backdrop) {
lv_obj_set_style_bg_color(backdrop, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(backdrop, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(backdrop, 0, 0);
}
// 弹窗本体黑底无边框
lv_obj_set_style_bg_color(mbox, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(mbox, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(mbox, 0, 0);
lv_obj_set_style_border_color(mbox, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_border_opa(mbox, LV_OPA_COVER, 0);
lv_obj_set_style_border_side(mbox, LV_BORDER_SIDE_FULL, 0);
// 字体与颜色(勿对 mbox 设 text_font,避免 footer 图标被 MiSans 继承成方框)
lv_obj_set_style_text_color(mbox, lv_color_hex(0xFFFFFF), 0); // 全局白字
lv_obj_set_style_text_font(title, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_font(content, &lv_font_montserrat_14, 0);
// 单独处理标题颜色
lv_obj_set_style_text_color(title, lv_color_hex(0x00E0FF), 0); // 标题蓝字
// 处理标题背景
lv_obj_t * header = lv_msgbox_get_header(mbox);
if(header) {
lv_obj_set_style_bg_color(header, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0);
}
// 处理底部按钮栏背景
lv_obj_t * footer = lv_msgbox_get_footer(mbox);
if(footer) {
lv_obj_set_style_bg_color(footer, lv_color_hex(0x000000), 0);
// 按钮居中
lv_obj_set_flex_align(footer, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(footer, 30, 0);
}
lv_obj_set_style_bg_color(btn1, lv_color_hex(0x56D695), 0);
lv_obj_set_style_text_color(btn1, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_radius(btn1, 20, 0);
screen_set_apply_msgbox_footer_btn_size_guide(btn1);
lv_obj_set_style_bg_color(btn2, lv_color_hex(0x808080), 0);
lv_obj_set_style_text_color(btn2, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_radius(btn2, 20, 0);
screen_set_apply_msgbox_footer_btn_size_guide(btn2);
break;
}
default:
break;
}
}
void events_init_screen_set (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_set_btn_guide, screen_set_btn_guide_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_set_btn_information, screen_set_btn_information_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_set_btn_set_recover, screen_set_btn_set_recover_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_set_btn_set_back, screen_set_btn_set_back_event_handler, LV_EVENT_ALL, ui);
if (ui->screen_set_slider_light != NULL) {
lv_obj_add_event_cb(ui->screen_set_slider_light, screen_set_slider_brightness_event_handler, LV_EVENT_ALL, ui);
int current_percent = get_system_brightness_percent();
lv_slider_set_value(ui->screen_set_slider_light, current_percent, LV_ANIM_OFF);
}
}
static void screen_component_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_GESTURE:
{
if (!is_gesture_allowed()) return; // 防抖检查
lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active());
switch(dir) {
case LV_DIR_LEFT:
{
lv_indev_wait_release(lv_indev_active());
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_home_weather, guider_ui.screen_home_weather_del, &guider_ui.screen_component_del, setup_scr_screen_home_weather, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, 1);
break;
}
case LV_DIR_RIGHT:
{
lv_indev_wait_release(lv_indev_active());
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_time, guider_ui.screen_time_del, &guider_ui.screen_component_del, setup_scr_screen_time, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, 2);
break;
}
case LV_DIR_BOTTOM:
{
lv_indev_wait_release(lv_indev_active());
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_component_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_OVER_BOTTOM, 150, 0, false, true, UI_IDX_PULLDOWN);
break;
}
default:
break;
}
break;
}
default:
break;
}
}
static void screen_component_btn_chouqian_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
ESP_LOGD("按钮","每日抽签");
ui_nav_go_by_index(0, LV_SCR_LOAD_ANIM_FADE_ON);
ui_nav_request_emoji_event(emoji_event_bless);
// 抽签结果的 UI 展示改到 lottie 播完后的回调里统一处理
//ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_sortition, guider_ui.screen_sortition_del, &guider_ui.screen_component_del, setup_scr_screen_sortition, LV_SCR_LOAD_ANIM_FADE_ON, 200, 100, false, true, UI_NAV_PAGE_IDX_KEEP);
break;
}
default:
break;
}
}
static void screen_component_btn_xingzuo_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
ESP_LOGD("按钮","星座");
ui_nav_go_by_index(0, LV_SCR_LOAD_ANIM_FADE_ON);
ui_nav_request_emoji_event(emoji_event_horoscope);
break;
}
default:
break;
}
}
static void screen_component_btn_zhengnian_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
ESP_LOGD("按钮","正念");
// 方案二:使用状态标志控制手势检测
ui_nav_go_by_index(0, LV_SCR_LOAD_ANIM_NONE);
// 设置正念活跃标志,关闭手势检测
g_mindfulness_active = true;
ESP_LOGI("mindfulness", "start playing, gesture disabled");
// 播放正念表情
ui_nav_request_emoji_event(emoji_event_mindfulness);
break;
}
default:
break;
}
}
static void screen_component_btn_calendar_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
ESP_LOGD("按钮","万年历");
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_Perpetual_Calendar, guider_ui.screen_Perpetual_Calendar_del,
&guider_ui.screen_component_del, setup_scr_screen_Perpetual_Calendar,
LV_SCR_LOAD_ANIM_FADE_ON, 150, 0, false, true, UI_NAV_PAGE_IDX_KEEP);
ui_dispatch_sync_perpetual_calendar_from_time();
break;
}
default:
break;
}
}
void events_init_screen_component (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_component, ui_event_gesture_cb, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_component_btn_chouqian, screen_component_btn_chouqian_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_component_btn_xingzuo, screen_component_btn_xingzuo_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_component_btn_zhengnian, screen_component_btn_zhengnian_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_component_btn_calendar, screen_component_btn_calendar_event_handler, LV_EVENT_ALL, ui);
}
// 定义星座运势库
static const char * const fortunes[] =
{
"行动力满格出行避冲动,小财进账事事皆顺意",
"精神物质双富足,社交沟通顺风顺水无阻碍",
"今日运势佳,开车别急躁做事稳一点更顺遂",
"遇财务小惊喜,出行路况顺把握当下好机会",
"宇宙送好运财运超棒,日常不宜冲动去消费",
"沟通运超佳正偏财皆来,出行宜轻装更省心",
"耐心处事情绪别起伏,出行避拥堵万事顺意",
"今日运势超亮眼,开车稳行平安伴左右",
"创意大爆发小金库看涨,展才华收获满惊喜",
"财运佳出行一路顺,展自身才华有意外收获",
"细节定成败做事宜细致,出行多留意路况稳",
"人际超和谐有意外小财,出行宜结伴更舒心",
"直觉超敏锐烦恼全消散,出行遇贵人少弯路",
"迎远方喜报幸运值拉满,出行宜放松享美好",
"稳步前行想做皆有回响,职场遇新机有突破",
"脑洞大开灵感不停歇,人际有惊喜出行宜随心",
"日常遇小确幸内心超平静,出行宜慢走赏风景",
"水逆退散霉运全清零,出行顺利社交节节升",
"锦鲤附身好运常相伴,小财入账生活皆顺意",
"贵人运爆棚遇事有人帮,开车稳行一路皆坦途",
"社交迎新机把握有收获,出行早出发避高峰",
"财运稳步涨理财有小成,出行少变道更安全",
"心态放平和事事皆顺意,开车不赶路享沿途",
"职场遇伯乐才华被看见,出行路况佳无阻碍",
"偏财运来袭有意外惊喜,出行宜谨慎保平安",
"事事皆顺意烦恼全远离,开车守规则一路畅通",
"能量满格做事效率高,小财进账出行皆顺利",
"人际遇良缘相处超和谐,出行宜结伴添乐趣",
"社交稳步走收获在眼前,出行避弯路更省时",
"财运有惊喜消费宜理性,开车保持好心态更顺",
"今日福运浓,凡事皆顺意出行平安财运稳",
"压力全减轻心想皆事成,开车心放宽路路通",
"财源缓缓来工作有进步,出行无波折事事顺",
"家庭超和谐社交破瓶颈,出行遇顺境少烦恼",
"吉兆自天成霉运全退散,开车多留意皆平安",
"大笔小进账诸事皆顺意,出行宜慢行享美好",
"正桃花降临治愈渐升华,出行遇美好多欢喜",
"心想皆事成付出有收获,出行一路顺无阻碍",
"生活无忧愁霉运全扫除,开车心轻松事事顺",
"社交攀新高财运跟着涨,出行早规划避拥堵",
"桃花朵朵开生活多甜蜜,出行宜舒心享时光",
"梦想逐步现好运连连来,出行无难事皆顺意",
"努力有回响收获在身边,开车稳行平安相伴",
"凡事皆顺意好运常围绕,小财入账生活多美好",
"思路开新局做事有方向,出行避高峰更顺畅",
"职场遇良机把握能出彩,出行路况佳无波折",
"情绪超稳定做事更高效,出行宜随心多顺遂",
"好运藏努力付出有回报,开车守交规一路平安",
"惊喜常相伴生活多美好,出行遇温暖少烦恼",
"福运滚滚来诸事皆顺意,开车享美好一路坦途",
};
#define FORTUNE_COUNT (sizeof(fortunes) / sizeof(fortunes[0]))
static void update_fortune_text(const char *new_text)
{
if (guider_ui.screen_fortune_text_label_fotune != NULL &&
lv_obj_is_valid(guider_ui.screen_fortune_text_label_fotune)) {
lv_label_set_text(guider_ui.screen_fortune_text_label_fotune, new_text);
}
}
void show_random_fortune_result(void)
{
char buffer[128];
srand(lv_tick_get());
snprintf(buffer, sizeof(buffer), "%s", fortunes[rand() % FORTUNE_COUNT]);
update_fortune_text(buffer);
ui_nav_load_fortune_text_fade();
}
// ===== 正念引导状态管理 =====
typedef enum {
MINDFULNESS_STOPPED,
MINDFULNESS_RUNNING,
MINDFULNESS_PAUSED
} mindfulness_state_t;
typedef struct {
uint32_t duration_ms; // 持续时间(毫秒)
lv_color_t bg_color; // 背景色
uint8_t arc_direction; // 弧形动画方向:0=顺时针增长, 1=逆时针减少
const char *instruction; // 详细引导语
} mindfulness_phase_t;
static const mindfulness_phase_t phases[] =
{
// 阶段 0: 准备
{3000, LV_COLOR_MAKE(80, 80, 100), 0, "准备:静坐,闭眼,感受呼吸自然流动..."},
// 阶段 1: 吸气(慢)
{5000, LV_COLOR_MAKE(30, 80, 150), 0, "吸气:缓慢吸气,感受空气进入腹部..."},
// 阶段 2: 屏息(短)
{2000, LV_COLOR_MAKE(50, 100, 180), 0, "屏息:轻轻屏住呼吸,保持平静..."},
// 阶段 3: 呼气(更慢)
{6000, LV_COLOR_MAKE(80, 140, 200), 1, "呼气:缓缓呼气,释放所有紧张..."},
// 阶段 4: 放松(停顿)
{3000, LV_COLOR_MAKE(60, 110, 170), 0, "放松:完全放松,不控制呼吸..."},
// 阶段 5: 身体扫描(新增!)
{4000, LV_COLOR_MAKE(100, 90, 160), 0, "觉察:注意脚趾→膝盖→肩膀→头顶..."},
// 阶段 6: 结束回归
{4000, LV_COLOR_MAKE(120, 120, 140), 0, "结束:轻轻动手指,慢慢睁开眼睛..."}
};
#define PHASE_COUNT (sizeof(phases) / sizeof(phases[0]))
static mindfulness_state_t mindfulness_state = MINDFULNESS_STOPPED;
static lv_timer_t *mindfulness_timer = NULL;
static uint8_t current_phase = 0;
static uint32_t phase_start_tick = 0;
static uint32_t pause_start_timestamp = 0;
// 定时器回调:驱动呼吸动画
static void mindfulness_timer_cb(lv_timer_t *timer)
{
if (mindfulness_state != MINDFULNESS_RUNNING) return;
uint32_t now = lv_tick_get();
uint32_t elapsed = now - phase_start_tick;
const mindfulness_phase_t *phase = &phases[current_phase];
uint32_t total = phase->duration_ms;
// 阶段结束:切换到下一阶段
if (elapsed >= total)
{
// 检查是否是最后一个阶段
if (current_phase == PHASE_COUNT - 1)
{
// 周期完成!停止并回到初始状态
mindfulness_state = MINDFULNESS_STOPPED;
if (mindfulness_timer) lv_timer_pause(mindfulness_timer);
// 更新 UI:显示”完成”
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "正念练习完成,感谢你的专注!");
lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, 0);
lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, lv_color_hex(0x000000), LV_PART_MAIN);
// 【解决方案二】清除正念活跃标志,恢复手势切换
g_mindfulness_active = false;
ESP_LOGI("mindfulness", "session completed, gesture enabled");
return; // 不再继续
}
// 否则进入下一阶段
current_phase++;
phase_start_tick = now;
phase = &phases[current_phase];
// 更新 UI
lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, phase->bg_color, LV_PART_MAIN);
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, phase->instruction);
}
// 更新弧形进度
if (mindfulness_state == MINDFULNESS_RUNNING)
{
int16_t angle = (elapsed * 360) / total;
if (phase->arc_direction == 1)
{
angle = 360 - angle; // 呼气时反向
}
lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, angle);
}
}
// 开始呼吸正念
static void start_mindfulness_session(void)
{
if (mindfulness_timer) {
lv_timer_del(mindfulness_timer);
mindfulness_timer = NULL;
}
mindfulness_state = MINDFULNESS_RUNNING;
current_phase = 0;
phase_start_tick = lv_tick_get();
if (!mindfulness_timer) {
mindfulness_timer = lv_timer_create(mindfulness_timer_cb, 100, NULL); // 100ms 刷新(优化:降低CPU占用)
} else {
lv_timer_resume(mindfulness_timer);
}
// 初始化 UI
const mindfulness_phase_t *first = &phases[0];
lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, first->bg_color, LV_PART_MAIN);
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, first->instruction);
lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, 0);
// 【解决方案二】设置正念活跃标志,禁用手势切换
g_mindfulness_active = true;
ESP_LOGI("mindfulness", "start session, gesture disabled");
}
// 重置呼吸状态
static void reset_mindfulness_session(void)
{
mindfulness_state = MINDFULNESS_STOPPED;
current_phase = 0;
phase_start_tick = 0;
if (mindfulness_timer) lv_timer_pause(mindfulness_timer);
// 重置 UI 到初始状态
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "准备开始");
lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, 0);
lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, lv_color_hex(0x000000), LV_PART_MAIN);
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "");
lv_obj_update_layout(guider_ui.screen_mindfulness_label_phase);
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "准备开始");
lv_obj_set_style_bg_color(guider_ui.screen_mindfulness_btn_pause, lv_color_hex(0x29333B), LV_PART_MAIN);
lv_label_set_text(guider_ui.screen_mindfulness_btn_pause_label, "暂停/继续");
lv_obj_invalidate(guider_ui.screen_mindfulness);
// 【解决方案二】清除正念活跃标志,恢复手势切换
g_mindfulness_active = false;
ESP_LOGI("mindfulness", "session reset, gesture enabled");
}
static void screen_mindfulness_btn_phase_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
if (mindfulness_state == MINDFULNESS_RUNNING)
{
// 暂停
mindfulness_state = MINDFULNESS_PAUSED;
pause_start_timestamp = lv_tick_get();
lv_obj_set_style_bg_color(guider_ui.screen_mindfulness_btn_pause, lv_color_hex(0x4CAF50), LV_PART_MAIN); // 绿色:表示可继续
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "已暂停");
lv_obj_invalidate(guider_ui.screen_mindfulness);
}
else if (mindfulness_state == MINDFULNESS_PAUSED)
{
// =========== 继续逻辑 ===========
mindfulness_state = MINDFULNESS_RUNNING;
// 1. 时间补偿 (保持不变)
uint32_t now = lv_tick_get();
phase_start_tick += (now - pause_start_timestamp);
// 2. 恢复按钮颜色 (保持不变)
lv_obj_set_style_bg_color(guider_ui.screen_mindfulness_btn_pause, lv_color_hex(0xF44336), LV_PART_MAIN);
// 获取当前正在进行的阶段数据
const mindfulness_phase_t *phase = &phases[current_phase];
// 重新设置文字为当前阶段的引导语 (例如 "吸气...")
lv_label_set_text(guider_ui.screen_mindfulness_label_phase, phase->instruction);
// 强制刷新文字区域,确保“已暂停”被立刻擦除
lv_obj_invalidate(guider_ui.screen_mindfulness);
// ==========================================================
// 4. 立即更新进度条 (保持不变)
if (mindfulness_timer)
{
mindfulness_timer_cb(mindfulness_timer);
}
}
break;
}
default:
break;
}
}
static void screen_mindfulness_btn_start_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
if (mindfulness_state == MINDFULNESS_STOPPED || mindfulness_state == MINDFULNESS_PAUSED)
{
start_mindfulness_session();
}
break;
}
default:
break;
}
}
static void screen_mindfulness_btn_reset_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
reset_mindfulness_session();
break;
}
default:
break;
}
}
static void screen_mindfulness_img_mindfulness_back_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
if (mindfulness_timer) {
lv_timer_del(mindfulness_timer);
mindfulness_timer = NULL;
}
mindfulness_state = MINDFULNESS_STOPPED; // 重置状态
// 【解决方案二】清除正念活跃标志,恢复手势切换
g_mindfulness_active = false;
ESP_LOGI("mindfulness", "back to components, gesture enabled");
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del, &guider_ui.screen_mindfulness_del, setup_scr_screen_component, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, UI_IDX_COMPONENTS);
break;
}
default:
break;
}
}
void events_init_screen_mindfulness (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_mindfulness_btn_pause, screen_mindfulness_btn_phase_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_mindfulness_btn_start, screen_mindfulness_btn_start_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_mindfulness_btn_reset, screen_mindfulness_btn_reset_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_mindfulness_img_mindfulness_back, screen_mindfulness_img_mindfulness_back_event_handler, LV_EVENT_ALL, ui);
// 【解决方案】禁用正念界面的手势切换,防止练习被打断
// 方法:清除手势标志,使该界面不响应手势事件
lv_obj_remove_flag(ui->screen_mindfulness, LV_OBJ_FLAG_GESTURE_BUBBLE);
lv_obj_clear_flag(ui->screen_mindfulness, LV_OBJ_FLAG_CLICKABLE);
}
// ===== 签文数据(按分类)=====
typedef struct {
const char *category;
const char *text;
} lottery_item_t;
// static const lottery_item_t all_messages[] = {
// // ========== 社交 ==========
// {"社交", "桃花满屏 · 脱单成功"},
// {"社交", "良缘天降 · 喜结连理"},
// {"社交", "搭子速配 · 知音相伴"},
// {"社交", "颜值开挂 · 桃花爆仓"},
// {"社交", "真爱降临 · 甜度超标"},
// {"社交", "脱单快进 · 缘分拉满"},
// {"社交", "告白必成 · 双向奔赴"},
// {"社交", "桃花朵朵 · 缘分爆棚"},
// // {"社交", "优先处理重要但不紧急的事。"},
// // {"社交", "信任团队,适当放手。"},
// // {"社交", "灵感稍纵即逝,立刻记录。"},
// // {"社交", "客户的真实需求,往往藏在抱怨里。"},
// // {"社交", "慢即是快,稳扎稳打胜过冒进。"},
// // {"社交", "今日贵人运强,多倾听前辈建议。"},
// // {"社交", "完成比完美更重要,先行动起来。"},
// // ========== 治愈 ==========
// {"治愈", "摆烂无罪 · 快乐加倍"},
// {"治愈", "烦恼清零 · 快乐加载"},
// {"治愈", "懒觉自由 · 元气满满"},
// {"治愈", "美食秒到 · 干饭无忧"},
// {"治愈", "脱发退散 · 发量暴增"},
// {"治愈", "失眠拜拜 · 一夜好眠"},
// {"治愈", "奶茶自由 · 快乐拉满"},
// {"治愈", "追剧无广 · 爽感拉满"},
// // {"治愈", "原谅不是遗忘,而是放过自己。"},
// // {"治愈", "安全感来自内心的稳定,而非对方的承诺。"},
// // {"治愈", "共同成长,比互相依赖更长久。"},
// // {"治愈", "今天适合写一封手写信给在乎的人。"},
// // {"治愈", "情绪来临时,先深呼吸三次再开口。"},
// // {"治愈", "爱自己,是终身浪漫的开始。"},
// // {"治愈", "真正的亲密,是敢于做真实的自己。"},
// // ========== 健康 ==========
// {"健康", "喝一杯温水,唤醒身体机能。"},
// {"健康", "久坐一小时,起身活动五分钟。"},
// {"健康", "今晚早睡半小时,明天精神倍增。"},
// {"健康", "吃七分饱,肠胃轻松一整天。"},
// {"健康", "阳光是最好的维生素,出门走走吧。"},
// {"健康", "深呼吸十次,缓解焦虑与压力。"},
// {"健康", "少看屏幕,多看看远方的绿意。"},
// {"健康", "身体是灵魂的圣殿,请温柔对待。"},
// // {"健康", "运动不必剧烈,坚持才是关键。"},
// // {"健康", "睡前放下烦恼,让心先入睡。"},
// // {"健康", "笑一笑,免疫力悄悄提升。"},
// // {"健康", "饮食清淡,内心也会更清明。"},
// // {"健康", "拉伸三分钟,缓解肩颈僵硬。"},
// // {"健康", "水分充足,思维更清晰。"},
// // {"健康", "健康不是目标,而是每一天的选择。"},
// // ========== 心灵 ==========
// {"心灵", "你此刻的呼吸,就是生命的奇迹。"},
// {"心灵", "允许自己休息,这不是懒惰。"},
// {"心灵", "杂念如云,天空始终澄明。"},
// {"心灵", "感恩三件小事,点亮今日心情。"},
// {"心灵", "内在平静,胜过外界喧嚣。"},
// {"心灵", "你不需要讨好所有人。"},
// {"心灵", "当下即是全部,无需追逐未来。"},
// {"心灵", "静坐片刻,找回内心的节奏。"},
// {"心灵", "接纳不完美,才是真正的自由。"},
// {"心灵", "你的存在本身,就有价值。"},
// {"心灵", "放下评判,以好奇之心看世界。"},
// {"心灵", "慈悲始于对自己的温柔。"},
// {"心灵", "简单生活,丰盈内心。"},
// {"心灵", "每一次呼吸,都是重新开始的机会。"},
// {"心灵", "你已走在正确的路上,请相信直觉。"},
// // ========== 通用==========
// {"通用", "锦鲤附体 · 好运炸屏"},
// {"通用", "欧气护体 · 万事开挂"},
// {"通用", "水逆退散 · 好运加载"},
// {"通用", "幸运值满 · 心想事成"},
// {"通用", "惊喜盲盒 · 好运爆棚"},
// {"通用", "快乐续费 · 好运无限"},
// {"通用", "好运拉满 · 万事顺遂"},
// {"通用", "喜事临门 · 福气满满"},
// {"通用", "水逆退散 · 时来运转"},
// {"通用", "锦鲤天降 · 惊喜连连"},
// {"通用", "烦恼退散 · 快乐加倍"},
// {"通用", "好运爆棚 · 心想事成"},
// {"通用", "福气满格 · 万事胜意"},
// {"通用", "惊喜不断 · 好运连连"},
// {"通用", "元气满满 · 好运加满"},
// {"通用", "万事亨通 · 好运常驻"},
// {"通用", "钱包鼓鼓 · 财运暴击"},
// {"通用", "躺赢剧本 · 万事开挂"},
// };
// #define ITEM_COUNT (sizeof(all_messages) / sizeof(all_messages[0]))
// // ===== 历史记录缓存=====
// #define HISTORY_MAX 5
// static char history_texts[HISTORY_MAX][128] = {0};
// static uint8_t history_count = 0;
// #define NVS_NAMESPACE "lottery"
// #define KEY_LAST_DRAW_TS "last_draw_ts"
// uint64_t boot_timestamp_ms = 0;
// // 获取当前“绝对”时间戳(ms)
// static uint64_t get_current_abs_time_ms(void) {
// boot_timestamp_ms = esp_timer_get_time() / 1000;
// return boot_timestamp_ms + (esp_timer_get_time() / 1000);
// }
// // 检查今日是否已抽签
// bool is_drawn_today(void) {
// nvs_handle_t handle;
// esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle);
// if (err != ESP_OK) return false;
// uint64_t last_draw_ts = 0;
// err = nvs_get_u64(handle, KEY_LAST_DRAW_TS, &last_draw_ts);
// nvs_close(handle);
// if (err != ESP_OK) {
// return false; // 从未抽过
// }
// uint64_t current_ts = get_current_abs_time_ms();
// uint64_t diff_ms = current_ts - last_draw_ts;
// // 如果距离上次抽签 < 24 小时(86400000 ms),视为同一天
// return (diff_ms < 24 * 60 * 60 * 1000ULL);
// }
// // 标记今日已抽签
// void mark_drawn_today(void) {
// nvs_handle_t handle;
// if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
// return;
// }
// uint64_t current_ts = get_current_abs_time_ms();
// nvs_set_u64(handle, KEY_LAST_DRAW_TS, current_ts);
// nvs_commit(handle);
// nvs_close(handle);
// }
// // ===== 添加历史记录 =====
// static void add_to_history(const char *msg) {
// // 移动旧记录
// if (history_count >= HISTORY_MAX) {
// for (int i = 0; i < HISTORY_MAX - 1; i++) {
// strcpy(history_texts[i], history_texts[i + 1]);
// }
// history_count = HISTORY_MAX - 1;
// }
// strncpy(history_texts[history_count], msg, sizeof(history_texts[0]) - 1);
// history_count++;
// }
static const char *CATEGORIES[] = {"通用", "社交", "治愈", "健康", "心灵"};
#define CAT_COUNT (sizeof(CATEGORIES)/sizeof(CATEGORIES[0]))
// ===== 抽签核心逻辑 =====
// static void perform_draw(void)
// {
// // if (is_drawn_today()) {
// // lv_label_set_text(guider_ui.screen_sortition_label_message, "今日已抽过签啦~");
// // return;
// // }
// // 获取当前选中分类
// uint16_t idx = lv_dropdown_get_selected(guider_ui.screen_sortition_ddlist_category);
// if (idx >= CAT_COUNT) idx = 0;
// const char *selected = CATEGORIES[idx];
// if (!selected) selected = "通用";
// // 过滤签文
// const lottery_item_t *candidates[ITEM_COUNT];
// uint8_t cand_count = 0;
// for (int i = 0; i < ITEM_COUNT; i++) {
// if (strcmp(all_messages[i].category, "通用") == 0 ||
// strcmp(all_messages[i].category, selected) == 0) {
// candidates[cand_count++] = &all_messages[i];
// }
// }
// if (cand_count == 0) {
// lv_label_set_text(guider_ui.screen_sortition_label_message, "暂无此分类签文");
// return;
// }
// // 随机抽取
// static bool seeded = false;
// if (!seeded) {
// srand((unsigned int)lv_tick_get());
// seeded = true;
// }
// const lottery_item_t *chosen = candidates[rand() % cand_count];
// // 显示 + 存储
// lv_label_set_text(guider_ui.screen_sortition_label_message, chosen->text);
// // add_to_history(chosen->text);
// // mark_drawn_today();
// }
static void screen_sortition_btn_draw_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
perform_draw();
break;
}
default:
break;
}
}
static void screen_sortition_img_sortition_back_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del, &guider_ui.screen_sortition_del, setup_scr_screen_component, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, UI_IDX_COMPONENTS);
break;
}
default:
break;
}
}
void events_init_screen_sortition (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_sortition_btn_draw, screen_sortition_btn_draw_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_sortition_img_sortition_back, screen_sortition_img_sortition_back_event_handler, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen_sortition_btn_history, screen_sortition_btn_history_event_handler, LV_EVENT_ALL, ui);
}
static void screen_sortition_btn_history_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
// // 清空历史列表
// lv_obj_clean(guider_ui.screen_sortition_history_list_history);
// // 添加历史项
// for (int i = history_count - 1; i >= 0; i--) {
// lv_obj_t *item = lv_list_add_text(guider_ui.screen_sortition_history_list_history, history_texts[i]);
// lv_obj_set_style_pad_left(item, 10, LV_PART_MAIN);
// }
/* 抽签历史页 setup 已移除:此处不再切换屏幕 */
break;
}
default:
break;
}
}
static void screen_sortition_history_img_sortition_history_back_event_handler (lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
{
/* 抽签页 setup 已移除:此处不再切换屏幕 */
break;
}
default:
break;
}
}
void events_init_screen_sortition_history (lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_sortition_history_img_sortition_history_back, screen_sortition_history_img_sortition_history_back_event_handler, LV_EVENT_ALL, ui);
}
void events_init_screen_fortune_text(lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_fortune_text_btn_2, sortition_text_back_to_components, LV_EVENT_PRESSED, NULL);
}
static void sortition_text_back_to_components(lv_event_t *e)
{
(void)e;
extern bool back2default;
back2default = true; // 返回时恢复默认表情
ui_nav_go_by_index(UI_IDX_COMPONENTS, LV_SCR_LOAD_ANIM_FADE_ON);
}
static void sortition_text_pressed_handler(lv_event_t *e)
{
sortition_text_back_to_components(e);
}
void events_init_screen_sortition_text(lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_sortition_text_btn_st_back, sortition_text_back_to_components, LV_EVENT_PRESSED, NULL);
}
static void screen_Perpetual_Calendar_btn_PC_back_event_handler(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code) {
case LV_EVENT_PRESSED:
/* 与抽签页返回一致:经 ui_load_scr_animation 正确恢复可能已被 auto_del 的 screen_component */
ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del,
&guider_ui.screen_Perpetual_Calendar_del, setup_scr_screen_component,
LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, UI_IDX_COMPONENTS);
break;
default:
break;
}
}
void events_init_screen_Perpetual_Calendar(lv_ui *ui)
{
lv_obj_add_event_cb(ui->screen_Perpetual_Calendar_btn_PC_back, screen_Perpetual_Calendar_btn_PC_back_event_handler,
LV_EVENT_ALL, ui);
}
void events_init(lv_ui *ui)
{
}