Files
arcs/apps-ui/apps/llm/views/setting/setting_clock_view.c
2026-08-13 16:50:52 +08:00

388 lines
13 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.
/**
* @file setting_clock_view.c
* @brief Clock/Alarm settings view implementation
*/
#include "setting_clock_view.h"
#include "lisa_ui.h"
#include "lisa_ui_llm_base.h"
#include "lisa_ui_nav_scr.h"
#include "lisa_ui_nav_scr_ids.h"
#include "lisa_ui_assets.h"
#include "lisa_ui_fonts.h"
#include <time.h>
#include <stdio.h>
#define TAG "setting_clock_view"
typedef struct {
lisa_ui_llm_base_t base;
lv_obj_t *back_btn;
lv_obj_t *scroll_container;
lv_obj_t *empty_label;
lisa_ui_setting_clock_back_cb_t back_cb; /* 返回按钮回调函数 */
void *back_user_data; /* 回调函数用户数据 */
lisa_ui_setting_clock_delete_cb_t delete_cb; /* 删除闹钟回调函数 */
alarm_item_t alarm_items[20]; /* 使用统一的alarm结构体 */
int alarm_count;
} lisa_ui_setting_clock_view_t;
static void lisa_ui_setting_clock_view_class_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj);
const lv_obj_class_t lisa_ui_setting_clock_view_class = {
.base_class = &lisa_ui_llm_base_class,
.constructor_cb = lisa_ui_setting_clock_view_class_constructor,
.instance_size = sizeof(lisa_ui_setting_clock_view_t),
};
static void back_btn_event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)lv_event_get_user_data(e);
LISA_UI_LOGD("Clock back button clicked");
/* 调用回调函数将页面导航逻辑交给presenter层处理 */
if (view && view->back_cb) {
view->back_cb(view->back_user_data);
}
}
}
static void delete_btn_event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
lv_obj_t *btn = lv_event_get_target(e);
lv_obj_t *view_obj = lv_obj_get_parent(lv_obj_get_parent(btn));
while (view_obj && !lv_obj_check_type(view_obj, &lisa_ui_setting_clock_view_class)) {
view_obj = lv_obj_get_parent(view_obj);
}
if (!view_obj) {
return;
}
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)view_obj;
if (!view || !view->delete_cb) {
return;
}
uint64_t *timestamp = (uint64_t *)lv_event_get_user_data(e);
if (timestamp) {
view->delete_cb(*timestamp);
}
}
}
/* 手动将timestamp转换为日期时间不依赖localtime() */
static void timestamp_to_datetime(uint64_t timestamp, int *year, int *month, int *day, int *hour, int *minute,
int *second)
{
/* 时区偏移UTC+8 (28800秒) */
const int timezone_offset = 8 * 3600;
time_t t = (time_t)timestamp + timezone_offset;
/* 计算天数 */
int days = t / 86400;
int remaining = t % 86400;
/* 计算时分秒 */
*hour = remaining / 3600;
*minute = (remaining % 3600) / 60;
*second = remaining % 60;
/* 计算年月日 (从1970年1月1日开始) */
int y = 1970;
while (1) {
int days_in_year = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? 366 : 365;
if (days < days_in_year) {
break;
}
days -= days_in_year;
y++;
}
*year = y;
/* 计算月份 */
int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (*year % 4 == 0 && (*year % 100 != 0 || *year % 400 == 0)) {
days_in_month[1] = 29;
}
int m = 0;
while (m < 12 && days >= days_in_month[m]) {
days -= days_in_month[m];
m++;
}
*month = m + 1;
*day = days + 1;
}
/* 使用蔡勒公式计算星期几 */
static int calculate_weekday_zeller(int year, int month, int day)
{
/* 蔡勒公式W = (d + (13*(m+1))/5 + y + y/4 - y/100 + y/400) % 7 */
/* 注意1月和2月要当作上一年的13月和14月来计算 */
if (month < 3) {
month += 12;
year -= 1;
}
int century = year / 100;
int year_of_century = year % 100;
int w = (day + (13 * (month + 1)) / 5 + year_of_century + year_of_century / 4 + century / 4 - 2 * century) % 7;
/* 蔡勒公式结果0=周六, 1=周日, 2=周一, 3=周二, 4=周三, 5=周四, 6=周五 */
/* 转换为0=周日, 1=周一, 2=周二, 3=周三, 4=周四, 5=周五, 6=周六 */
int weekday = (w + 6) % 7;
return weekday;
}
static void format_alarm_time_string(uint64_t timestamp, char *time_str, char *date_str)
{
int year, month, day, hour, minute, second;
LISA_UI_LOGI("Format alarm time - timestamp: %llu", (unsigned long long)timestamp);
/* 检查timestamp是否有效负数或过大的值都是无效的 */
if ((int64_t)timestamp < 0 || timestamp > 4102444800ULL) {
LISA_UI_LOGE(" Invalid timestamp: %lld (negative or too large)", (int64_t)timestamp);
snprintf(time_str, 16, "--:--");
snprintf(date_str, 32, _("alarm invalid"));
return;
}
/* 手动转换时间戳 */
timestamp_to_datetime(timestamp, &year, &month, &day, &hour, &minute, &second);
LISA_UI_LOGI(" Converted: year=%d, mon=%d, mday=%d, hour=%d, min=%d, sec=%d", year, month, day, hour, minute,
second);
/* 格式化时间 */
snprintf(time_str, 16, "%02d:%02d", hour, minute);
/* 使用蔡勒公式计算正确的星期几 */
int weekday = calculate_weekday_zeller(year, month, day);
LISA_UI_LOGI(" Zeller weekday: %d", weekday);
const char *weekdays[] = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
snprintf(date_str, 32, "%d月%d日 %s", month, day, weekdays[weekday]);
LISA_UI_LOGI(" Formatted: time='%s', date='%s'", time_str, date_str);
}
static lv_obj_t *create_alarm_item(lv_obj_t *parent, const alarm_item_t *alarm_item, int y_offset)
{
lv_obj_t *alarm_container = lv_obj_create(parent);
lv_obj_set_size(alarm_container, LV_PCT(95), 80);
lv_obj_align(alarm_container, LV_ALIGN_TOP_MID, 0, y_offset);
lv_obj_set_style_bg_color(alarm_container, lv_color_make(0x4A, 0x4A, 0x4A), 0);
lv_obj_set_style_border_width(alarm_container, 0, 0);
lv_obj_set_style_radius(alarm_container, 8, 0);
lv_obj_set_style_pad_all(alarm_container, 10, 0);
char time_str[16];
char date_str[32];
format_alarm_time_string(alarm_item->timestamp, time_str, date_str);
lv_obj_t *time_label = lv_label_create(alarm_container);
lv_label_set_text(time_label, time_str);
lv_obj_set_style_text_color(time_label, lv_color_white(), 0);
lv_obj_set_style_text_font(time_label, &lv_font_chinese_16, 0);
lv_obj_align(time_label, LV_ALIGN_TOP_LEFT, 5, 8);
lv_obj_t *date_label = lv_label_create(alarm_container);
lv_label_set_text(date_label, date_str);
lv_obj_set_style_text_color(date_label, lv_color_make(0xAA, 0xAA, 0xAA), 0);
lv_obj_set_style_text_font(date_label, &lv_font_chinese_16, 0);
lv_obj_align(date_label, LV_ALIGN_BOTTOM_LEFT, 5, -8);
lv_obj_t *delete_btn = lv_btn_create(alarm_container);
lv_obj_set_size(delete_btn, 50, 30);
lv_obj_align(delete_btn, LV_ALIGN_RIGHT_MID, -5, 0);
lv_obj_set_style_bg_color(delete_btn, lv_color_make(0xFF, 0x44, 0x44), 0);
lv_obj_set_style_border_width(delete_btn, 0, 0);
lv_obj_set_style_radius(delete_btn, 4, 0);
lv_obj_t *delete_label = lv_label_create(delete_btn);
lv_label_set_text(delete_label, _("cancel"));
lv_obj_set_style_text_color(delete_label, lv_color_white(), 0);
lv_obj_set_style_text_font(delete_label, &lv_font_chinese_16, 0);
lv_obj_center(delete_label);
lv_obj_add_event_cb(delete_btn, delete_btn_event_cb, LV_EVENT_CLICKED, (void *)&alarm_item->timestamp);
return alarm_container;
}
static void lisa_ui_setting_clock_view_class_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj)
{
LV_UNUSED(class_p);
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)obj;
// Hide the bar to allow content to start from the very top
lv_obj_t *bar = lisa_ui_llm_base_bar_get(obj);
if (bar) {
lv_obj_add_flag(bar, LV_OBJ_FLAG_HIDDEN);
}
lv_obj_t *container = lisa_ui_llm_base_container_get(obj);
if (NULL == container) {
LISA_UI_LOGE("Failed to get base container");
return;
}
// Back button at the very top
view->back_btn = lv_btn_create(container);
lv_obj_set_size(view->back_btn, 30, 30);
lv_obj_align(view->back_btn, LV_ALIGN_TOP_LEFT, 5, 0);
lv_obj_set_style_bg_opa(view->back_btn, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_style_border_width(view->back_btn, 0, LV_PART_MAIN);
lv_obj_t *back_icon = lv_img_create(view->back_btn);
lv_img_set_src(back_icon, &icons_icon_back_png);
lv_obj_center(back_icon);
lv_obj_add_event_cb(view->back_btn, back_btn_event_cb, LV_EVENT_CLICKED, view);
/* 在返回按钮旁边创建标题文本 */
lv_obj_t *title = lv_label_create(container);
lv_label_set_text(title, _("alarm setting"));
lv_obj_set_style_text_font(title, &lv_font_chinese_16, LV_PART_MAIN);
lv_obj_set_style_text_color(title, lv_color_white(), LV_PART_MAIN);
lv_obj_align(title, LV_ALIGN_TOP_LEFT, 45, 8); /* 紧贴返回按钮右侧 */
/* 初始化列表容器为NULL等待刷新时创建 */
view->scroll_container = NULL;
view->empty_label = NULL;
view->delete_cb = NULL;
view->alarm_count = 0;
}
lv_obj_t *lisa_ui_setting_clock_view_create(lv_obj_t *parent)
{
lv_obj_t *obj = lv_obj_class_create_obj(&lisa_ui_setting_clock_view_class, parent);
lv_obj_class_init_obj(obj);
/* 初始化回调函数为NULL */
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)obj;
view->back_cb = NULL;
view->back_user_data = NULL;
view->delete_cb = NULL;
return obj;
}
void lisa_ui_setting_clock_view_set_alarms(lv_obj_t *obj, const alarm_item_t *alarms, uint32_t count)
{
if (!obj) {
return;
}
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)obj;
/* 限制最大数量 */
uint32_t copy_count = (count > 20) ? 20 : count;
/* 复制闹钟数据到view */
for (uint32_t i = 0; i < copy_count; i++) {
view->alarm_items[i].timestamp = alarms[i].timestamp;
LISA_UI_LOGI("set alarms, timestamp: %llu", view->alarm_items[i].timestamp);
}
view->alarm_count = copy_count;
LISA_UI_LOGD("Set %u alarms to view", copy_count);
}
void lisa_ui_setting_clock_view_refresh_list(lv_obj_t *obj)
{
if (!obj) {
return;
}
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)obj;
lv_obj_t *container = lisa_ui_llm_base_container_get(obj);
if (!container) {
return;
}
/* 清理旧的UI元素 */
if (view->scroll_container) {
lv_obj_del(view->scroll_container);
view->scroll_container = NULL;
}
if (view->empty_label) {
lv_obj_del(view->empty_label);
view->empty_label = NULL;
}
LISA_UI_LOGD("Refreshing alarm list, count=%d", view->alarm_count);
if (view->alarm_count == 0) {
LISA_UI_LOGD("No alarms to display, showing empty state");
view->empty_label = lv_label_create(container);
lv_label_set_text(view->empty_label, _("no alarm"));
lv_obj_set_style_text_color(view->empty_label, lv_color_make(0x88, 0x88, 0x88), 0);
lv_obj_set_style_text_font(view->empty_label, &lv_font_chinese_16, 0);
lv_obj_set_style_text_align(view->empty_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(view->empty_label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_width(view->empty_label, lv_pct(100));
lv_obj_set_height(view->empty_label, LV_SIZE_CONTENT);
lv_label_set_long_mode(view->empty_label, LV_LABEL_LONG_WRAP);
return;
}
/* 创建滚动容器 */
view->scroll_container = lv_obj_create(container);
lv_obj_set_size(view->scroll_container, LV_PCT(100), LV_PCT(75));
lv_obj_align(view->scroll_container, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_set_style_bg_color(view->scroll_container, lv_color_black(), 0);
lv_obj_set_style_border_width(view->scroll_container, 0, 0);
lv_obj_set_style_pad_all(view->scroll_container, 10, 0);
/* 创建UI元素 */
int y_offset = 10;
for (int i = 0; i < view->alarm_count; i++) {
/* 跳过无效的时间戳 */
if (view->alarm_items[i].timestamp == 0) {
LISA_UI_LOGD("Skipping alarm with timestamp=0 at index %d", i);
continue;
}
LISA_UI_LOGI("Creating alarm item %d: timestamp=%llu", i, (unsigned long long)view->alarm_items[i].timestamp);
lv_obj_t *item = create_alarm_item(view->scroll_container, &view->alarm_items[i], y_offset);
if (item) {
y_offset += 90;
}
}
}
void lisa_ui_setting_clock_view_set_back_cb(lv_obj_t *obj, lisa_ui_setting_clock_back_cb_t cb, void *user_data)
{
if (!obj) {
return;
}
/* 设置返回按钮回调函数和用户数据 */
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)obj;
view->back_cb = cb;
view->back_user_data = user_data;
}
void lisa_ui_setting_clock_view_set_delete_cb(lv_obj_t *obj, lisa_ui_setting_clock_delete_cb_t cb)
{
if (!obj) {
return;
}
lisa_ui_setting_clock_view_t *view = (lisa_ui_setting_clock_view_t *)obj;
view->delete_cb = cb;
}