feat: Implement Discord app UI and settings management

- Added MainUI class for displaying voice state, status icon, and buttons.
- Introduced MainUIHandler to manage UI interactions and bridge communication.
- Created SettingsUI for displaying QR code and configuration instructions.
- Implemented SettingsUIHandler to manage settings and web server interactions.
- Developed WebHandler for handling HTTP requests for settings configuration.
- Updated AppRegistry to initialize with the new Discord app descriptor.
- Enhanced InteractionHandler to support keyboard interactions across app switches.
- Updated UIHandler to manage app switching and rendering of app icons.
- Enabled QR code support in LVGL configuration.
This commit is contained in:
GW_MC
2026-02-02 20:47:27 +08:00
parent 12ad5be48a
commit e467951b8c
28 changed files with 1927 additions and 24 deletions

View File

@@ -1,8 +1,14 @@
#include "ui/ui_handler.h"
#include "ui/apps/registry.h"
#include "esp_log.h"
#define TAG "UIHandler"
struct AppClickUserData {
UIHandler* ui_handler;
std::string app_name;
};
UIHandler::~UIHandler() {
deinit();
}
@@ -18,7 +24,9 @@ esp_err_t UIHandler::init(void) {
return ret;
}
ret = interaction_handler_.init(root_layout_.get_app_container());
// Initialize InteractionHandler with screen as parent (not app_container)
// so keyboard survives app switches
ret = interaction_handler_.init(screen);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize InteractionHandler");
return ret;
@@ -61,7 +69,7 @@ esp_err_t UIHandler::deinit(void) {
return ESP_OK;
}
esp_err_t UIHandler::switch_app(std::shared_ptr<AppDescriptor> app_descriptor) {
esp_err_t UIHandler::switch_app(AppDescriptor* app_descriptor) {
if (!app_descriptor) {
ESP_LOGE(TAG, "Invalid app descriptor");
return ESP_ERR_INVALID_ARG;
@@ -100,7 +108,7 @@ esp_err_t UIHandler::switch_app(std::shared_ptr<AppDescriptor> app_descriptor) {
return ESP_ERR_INVALID_STATE;
}
ret = new_app->init(app_container);
ret = new_app->init(app_container, &interaction_handler_);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize app: %s", new_app->get_name().c_str());
active_descriptor_ = nullptr;
@@ -239,6 +247,45 @@ esp_err_t UIHandler::create_main_screen_(lv_obj_t* parent) {
return ret;
}
// render all apps
for (const auto& [name, descriptor] : AppRegistry::instance()) {
lv_obj_t* app_icon_container = lv_obj_create(root_layout_.get_app_container());
lv_obj_set_size(app_icon_container, 100, 100);
lv_obj_set_style_pad_all(app_icon_container, 10, 0);
lv_obj_set_flex_flow(app_icon_container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(app_icon_container, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
// Draw the app icon
descriptor->draw_icon(app_icon_container);
// App name label
lv_obj_t* label = lv_label_create(app_icon_container);
lv_label_set_text(label, name.c_str());
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
// Center the icon container
lv_obj_center(app_icon_container);
// Register click event to switch to the app
lv_obj_add_event_cb(app_icon_container,
[](lv_event_t* e) {
AppClickUserData* user_data = static_cast<AppClickUserData*>(lv_event_get_user_data(e));
UIHandler* ui_handler = user_data->ui_handler;
std::string app_name = user_data->app_name;
AppDescriptor* descriptor = AppRegistry::instance()[app_name];
if (descriptor) {
ui_handler->switch_app(descriptor);
} else {
ESP_LOGE(TAG, "App descriptor not found for app: %s", app_name.c_str());
}
},
LV_EVENT_CLICKED,
new AppClickUserData { this, name }
);
}
// Register back button callback
lv_event_dsc_t* back_event_dsc = nullptr;
ret = root_layout_.register_back_button_callback(