纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。 Co-authored-by: Cursor <cursoragent@cursor.com>
1966 lines
78 KiB
C
1966 lines
78 KiB
C
/*
|
||
* 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)
|
||
{
|
||
|
||
}
|