123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- #include <esp_log.h>
- #include <esp_err.h>
- #include <string>
- #include <cstdlib>
- #include <cstring>
- #include "display.h"
- #include "board.h"
- #include "application.h"
- #include "font_awesome_symbols.h"
- #include "audio_codec.h"
- #include "settings.h"
- #include "assets/lang_config.h"
- #define TAG "Display"
- Display::Display() {
- // Notification timer
- esp_timer_create_args_t notification_timer_args = {
- .callback = [](void *arg) {
- Display *display = static_cast<Display*>(arg);
- DisplayLockGuard lock(display);
- lv_obj_add_flag(display->notification_label_, LV_OBJ_FLAG_HIDDEN);
- lv_obj_clear_flag(display->status_label_, LV_OBJ_FLAG_HIDDEN);
- },
- .arg = this,
- .dispatch_method = ESP_TIMER_TASK,
- .name = "notification_timer",
- .skip_unhandled_events = false,
- };
- ESP_ERROR_CHECK(esp_timer_create(¬ification_timer_args, ¬ification_timer_));
- // Create a power management lock
- auto ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "display_update", &pm_lock_);
- if (ret == ESP_ERR_NOT_SUPPORTED) {
- ESP_LOGI(TAG, "Power management not supported");
- } else {
- ESP_ERROR_CHECK(ret);
- }
- }
- Display::~Display() {
- if (notification_timer_ != nullptr) {
- esp_timer_stop(notification_timer_);
- esp_timer_delete(notification_timer_);
- }
- if (network_label_ != nullptr) {
- lv_obj_del(network_label_);
- lv_obj_del(notification_label_);
- lv_obj_del(status_label_);
- lv_obj_del(mute_label_);
- lv_obj_del(battery_label_);
- lv_obj_del(emotion_label_);
- }
- if( low_battery_popup_ != nullptr ) {
- lv_obj_del(low_battery_popup_);
- }
- if (pm_lock_ != nullptr) {
- esp_pm_lock_delete(pm_lock_);
- }
- }
- void Display::SetStatus(const char* status) {
- DisplayLockGuard lock(this);
- if (status_label_ == nullptr) {
- return;
- }
- lv_label_set_text(status_label_, status);
- lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
- lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
- last_status_update_time_ = std::chrono::system_clock::now();
- }
- void Display::ShowNotification(const std::string ¬ification, int duration_ms) {
- ShowNotification(notification.c_str(), duration_ms);
- }
- void Display::ShowNotification(const char* notification, int duration_ms) {
- DisplayLockGuard lock(this);
- if (notification_label_ == nullptr) {
- return;
- }
- lv_label_set_text(notification_label_, notification);
- lv_obj_clear_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
- lv_obj_add_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
- esp_timer_stop(notification_timer_);
- ESP_ERROR_CHECK(esp_timer_start_once(notification_timer_, duration_ms * 1000));
- }
- void Display::UpdateStatusBar(bool update_all) {
- auto& app = Application::GetInstance();
- auto& board = Board::GetInstance();
- auto codec = board.GetAudioCodec();
- // Update mute icon
- {
- DisplayLockGuard lock(this);
- if (mute_label_ == nullptr) {
- return;
- }
- // 如果静音状态改变,则更新图标
- if (codec->output_volume() == 0 && !muted_) {
- muted_ = true;
- lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_MUTE);
- } else if (codec->output_volume() > 0 && muted_) {
- muted_ = false;
- lv_label_set_text(mute_label_, "");
- }
- }
- // Update time
- if (app.GetDeviceState() == kDeviceStateIdle) {
- if (last_status_update_time_ + std::chrono::seconds(10) < std::chrono::system_clock::now()) {
- // Set status to clock "HH:MM"
- time_t now = time(NULL);
- struct tm* tm = localtime(&now);
- // Check if the we have already set the time
- if (tm->tm_year >= 2025 - 1900) {
- char time_str[16];
- strftime(time_str, sizeof(time_str), "%H:%M ", tm);
- SetStatus(time_str);
- } else {
- ESP_LOGW(TAG, "System time is not set, tm_year: %d", tm->tm_year);
- }
- }
- }
- esp_pm_lock_acquire(pm_lock_);
- // 更新电池图标
- int battery_level;
- bool charging, discharging;
- const char* icon = nullptr;
- if (board.GetBatteryLevel(battery_level, charging, discharging)) {
- if (charging) {
- icon = FONT_AWESOME_BATTERY_CHARGING;
- } else {
- const char* levels[] = {
- FONT_AWESOME_BATTERY_EMPTY, // 0-19%
- FONT_AWESOME_BATTERY_1, // 20-39%
- FONT_AWESOME_BATTERY_2, // 40-59%
- FONT_AWESOME_BATTERY_3, // 60-79%
- FONT_AWESOME_BATTERY_FULL, // 80-99%
- FONT_AWESOME_BATTERY_FULL, // 100%
- };
- icon = levels[battery_level / 20];
- }
- DisplayLockGuard lock(this);
- if (battery_label_ != nullptr && battery_icon_ != icon) {
- battery_icon_ = icon;
- lv_label_set_text(battery_label_, battery_icon_);
- }
- if (low_battery_popup_ != nullptr) {
- if (strcmp(icon, FONT_AWESOME_BATTERY_EMPTY) == 0 && discharging) {
- if (lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框隐藏,则显示
- lv_obj_clear_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
- app.PlaySound(Lang::Sounds::P3_LOW_BATTERY);
- }
- } else {
- // Hide the low battery popup when the battery is not empty
- if (!lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框显示,则隐藏
- lv_obj_add_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
- }
- }
- }
- }
- // 每 10 秒更新一次网络图标
- static int seconds_counter = 0;
- if (update_all || seconds_counter++ % 10 == 0) {
- // 升级固件时,不读取 4G 网络状态,避免占用 UART 资源
- auto device_state = Application::GetInstance().GetDeviceState();
- static const std::vector<DeviceState> allowed_states = {
- kDeviceStateIdle,
- kDeviceStateStarting,
- kDeviceStateWifiConfiguring,
- kDeviceStateListening,
- kDeviceStateActivating,
- };
- if (std::find(allowed_states.begin(), allowed_states.end(), device_state) != allowed_states.end()) {
- icon = board.GetNetworkStateIcon();
- if (network_label_ != nullptr && icon != nullptr && network_icon_ != icon) {
- DisplayLockGuard lock(this);
- network_icon_ = icon;
- lv_label_set_text(network_label_, network_icon_);
- }
- }
- }
- esp_pm_lock_release(pm_lock_);
- }
- void Display::SetEmotion(const char* emotion) {
- struct Emotion {
- const char* icon;
- const char* text;
- };
- static const std::vector<Emotion> emotions = {
- {FONT_AWESOME_EMOJI_NEUTRAL, "neutral"},
- {FONT_AWESOME_EMOJI_HAPPY, "happy"},
- {FONT_AWESOME_EMOJI_LAUGHING, "laughing"},
- {FONT_AWESOME_EMOJI_FUNNY, "funny"},
- {FONT_AWESOME_EMOJI_SAD, "sad"},
- {FONT_AWESOME_EMOJI_ANGRY, "angry"},
- {FONT_AWESOME_EMOJI_CRYING, "crying"},
- {FONT_AWESOME_EMOJI_LOVING, "loving"},
- {FONT_AWESOME_EMOJI_EMBARRASSED, "embarrassed"},
- {FONT_AWESOME_EMOJI_SURPRISED, "surprised"},
- {FONT_AWESOME_EMOJI_SHOCKED, "shocked"},
- {FONT_AWESOME_EMOJI_THINKING, "thinking"},
- {FONT_AWESOME_EMOJI_WINKING, "winking"},
- {FONT_AWESOME_EMOJI_COOL, "cool"},
- {FONT_AWESOME_EMOJI_RELAXED, "relaxed"},
- {FONT_AWESOME_EMOJI_DELICIOUS, "delicious"},
- {FONT_AWESOME_EMOJI_KISSY, "kissy"},
- {FONT_AWESOME_EMOJI_CONFIDENT, "confident"},
- {FONT_AWESOME_EMOJI_SLEEPY, "sleepy"},
- {FONT_AWESOME_EMOJI_SILLY, "silly"},
- {FONT_AWESOME_EMOJI_CONFUSED, "confused"}
- };
-
- // 查找匹配的表情
- std::string_view emotion_view(emotion);
- auto it = std::find_if(emotions.begin(), emotions.end(),
- [&emotion_view](const Emotion& e) { return e.text == emotion_view; });
-
- DisplayLockGuard lock(this);
- if (emotion_label_ == nullptr) {
- return;
- }
- // 如果找到匹配的表情就显示对应图标,否则显示默认的neutral表情
- if (it != emotions.end()) {
- lv_label_set_text(emotion_label_, it->icon);
- } else {
- lv_label_set_text(emotion_label_, FONT_AWESOME_EMOJI_NEUTRAL);
- }
- }
- void Display::SetIcon(const char* icon) {
- DisplayLockGuard lock(this);
- if (emotion_label_ == nullptr) {
- return;
- }
- lv_label_set_text(emotion_label_, icon);
- }
- void Display::SetPreviewImage(const lv_img_dsc_t* image) {
- // Do nothing
- }
- void Display::SetChatMessage(const char* role, const char* content) {
- DisplayLockGuard lock(this);
- if (chat_message_label_ == nullptr) {
- return;
- }
- lv_label_set_text(chat_message_label_, content);
- }
- void Display::SetTheme(const std::string& theme_name) {
- current_theme_name_ = theme_name;
- Settings settings("display", true);
- settings.SetString("theme", theme_name);
- }
- void Display::ShowStandbyScreen(bool show) {
- if (show) {
- SetChatMessage("system", "");
- SetEmotion("sleepy");
- } else {
- SetChatMessage("system", "");
- SetEmotion("neutral");
- }
- }
|