- Added MainUIHandler class to manage the main UI and polling for arrival data. - Introduced SettingsUI class for displaying QR code and configuration options. - Created SettingsUIHandler to manage settings UI lifecycle and web server interactions. - Developed WebHandler to handle HTTP requests for MTR route settings, including adding and removing routes. - Implemented web endpoints for fetching MTR lines, routes, and saving settings. - Enhanced UI with responsive design for e-ink displays and added error handling for web interactions.
151 lines
4.5 KiB
C++
151 lines
4.5 KiB
C++
#include "ui/apps/travel/ui/settings.h"
|
|
#include "display/lvgl_handler.h"
|
|
#include "esp_log.h"
|
|
|
|
static const char* TAG = "TravelSettingsUI";
|
|
|
|
namespace travel {
|
|
|
|
SettingsUI::SettingsUI() = default;
|
|
|
|
SettingsUI::~SettingsUI() {
|
|
deinit();
|
|
}
|
|
|
|
esp_err_t SettingsUI::init(lv_obj_t* parent) {
|
|
if (!parent) {
|
|
ESP_LOGE(TAG, "Parent is null");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
parent_ = parent;
|
|
|
|
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
|
|
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
|
|
return ESP_ERR_TIMEOUT;
|
|
}
|
|
|
|
// Create main container
|
|
container_ = lv_obj_create(parent_);
|
|
lv_obj_set_size(container_, LV_PCT(100), LV_PCT(100));
|
|
lv_obj_set_flex_flow(container_, LV_FLEX_FLOW_COLUMN);
|
|
lv_obj_set_flex_align(container_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
|
lv_obj_set_style_pad_all(container_, 10, 0);
|
|
lv_obj_set_style_bg_opa(container_, LV_OPA_TRANSP, 0);
|
|
lv_obj_set_style_border_width(container_, 0, 0);
|
|
// Disable animations and scrolling for e-ink
|
|
lv_obj_set_style_anim_time(container_, 0, 0);
|
|
lv_obj_clear_flag(container_, LV_OBJ_FLAG_SCROLLABLE);
|
|
|
|
// Title
|
|
title_label_ = lv_label_create(container_);
|
|
lv_label_set_text(title_label_, "設定路線");
|
|
lv_obj_set_style_text_font(title_label_, &lv_font_montserrat_14, 0);
|
|
lv_obj_set_style_pad_bottom(title_label_, 10, 0);
|
|
|
|
// QR Code container
|
|
lv_obj_t* qr_container = lv_obj_create(container_);
|
|
lv_obj_set_size(qr_container, QR_CODE_SIZE + 10, QR_CODE_SIZE + 10);
|
|
lv_obj_set_style_bg_color(qr_container, lv_color_white(), 0);
|
|
lv_obj_set_style_border_width(qr_container, 2, 0);
|
|
lv_obj_set_style_border_color(qr_container, lv_color_black(), 0);
|
|
lv_obj_set_style_anim_time(qr_container, 0, 0);
|
|
lv_obj_clear_flag(qr_container, LV_OBJ_FLAG_SCROLLABLE);
|
|
|
|
// QR Code
|
|
qr_code_ = lv_qrcode_create(qr_container);
|
|
lv_qrcode_set_size(qr_code_, QR_CODE_SIZE);
|
|
lv_qrcode_set_dark_color(qr_code_, lv_color_black());
|
|
lv_qrcode_set_light_color(qr_code_, lv_color_white());
|
|
lv_obj_center(qr_code_);
|
|
|
|
// URL label
|
|
url_label_ = lv_label_create(container_);
|
|
lv_obj_set_width(url_label_, LV_PCT(100));
|
|
lv_label_set_text(url_label_, "");
|
|
lv_obj_set_style_text_font(url_label_, &lv_font_montserrat_14, 0);
|
|
lv_label_set_long_mode(url_label_, LV_LABEL_LONG_WRAP);
|
|
lv_obj_set_style_text_align(url_label_, LV_TEXT_ALIGN_CENTER, 0);
|
|
lv_obj_set_style_pad_top(url_label_, 10, 0);
|
|
|
|
// Status message
|
|
status_label_ = lv_label_create(container_);
|
|
lv_obj_set_width(status_label_, LV_PCT(100));
|
|
lv_label_set_text(status_label_, "正在啟動伺服器...");
|
|
lv_obj_set_style_text_font(status_label_, &lv_font_montserrat_14, 0);
|
|
lv_label_set_long_mode(status_label_, LV_LABEL_LONG_WRAP);
|
|
lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0);
|
|
lv_obj_set_style_pad_top(status_label_, 15, 0);
|
|
|
|
// Instructions
|
|
instruction_label_ = lv_label_create(container_);
|
|
lv_obj_set_width(instruction_label_, LV_PCT(100));
|
|
lv_label_set_text(instruction_label_,
|
|
"請使用手機掃描QR碼或瀏覽器開啟網址\n"
|
|
"以設定MTR路線");
|
|
lv_obj_set_style_text_font(instruction_label_, &lv_font_montserrat_14, 0);
|
|
lv_label_set_long_mode(instruction_label_, LV_LABEL_LONG_WRAP);
|
|
lv_obj_set_style_text_align(instruction_label_, LV_TEXT_ALIGN_CENTER, 0);
|
|
lv_obj_set_style_text_color(instruction_label_, lv_color_hex(0x606060), 0);
|
|
lv_obj_set_style_pad_top(instruction_label_, 15, 0);
|
|
|
|
lvgl_port_unlock();
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t SettingsUI::deinit() {
|
|
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
|
|
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
|
|
return ESP_ERR_TIMEOUT;
|
|
}
|
|
|
|
if (container_) {
|
|
lv_obj_del(container_);
|
|
container_ = nullptr;
|
|
}
|
|
|
|
title_label_ = nullptr;
|
|
qr_code_ = nullptr;
|
|
url_label_ = nullptr;
|
|
status_label_ = nullptr;
|
|
instruction_label_ = nullptr;
|
|
|
|
lvgl_port_unlock();
|
|
parent_ = nullptr;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void SettingsUI::update_qr_code(const std::string& url) {
|
|
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
|
|
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
|
|
return;
|
|
}
|
|
|
|
if (qr_code_) {
|
|
lv_qrcode_update(qr_code_, url.c_str(), url.length());
|
|
}
|
|
|
|
if (url_label_) {
|
|
lv_label_set_text(url_label_, url.c_str());
|
|
}
|
|
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void SettingsUI::update_status_message(const std::string& message) {
|
|
if (!lvgl_port_lock(pdMS_TO_TICKS(1000))) {
|
|
ESP_LOGE(TAG, "Failed to acquire LVGL lock");
|
|
return;
|
|
}
|
|
|
|
if (status_label_) {
|
|
lv_label_set_text(status_label_, message.c_str());
|
|
}
|
|
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
} // namespace travel
|