display.cc 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #include <esp_log.h>
  2. #include <esp_err.h>
  3. #include <string>
  4. #include <cstdlib>
  5. #include <cstring>
  6. #include "display.h"
  7. #include "board.h"
  8. #include "application.h"
  9. #include "font_awesome_symbols.h"
  10. #include "audio_codec.h"
  11. #include "settings.h"
  12. #include "assets/lang_config.h"
  13. #define TAG "Display"
  14. Display::Display() {
  15. // Load theme from settings
  16. Settings settings("display", false);
  17. current_theme_name_ = settings.GetString("theme", "light");
  18. // Notification timer
  19. esp_timer_create_args_t notification_timer_args = {
  20. .callback = [](void *arg) {
  21. Display *display = static_cast<Display*>(arg);
  22. DisplayLockGuard lock(display);
  23. lv_obj_add_flag(display->notification_label_, LV_OBJ_FLAG_HIDDEN);
  24. lv_obj_clear_flag(display->status_label_, LV_OBJ_FLAG_HIDDEN);
  25. },
  26. .arg = this,
  27. .dispatch_method = ESP_TIMER_TASK,
  28. .name = "notification_timer",
  29. .skip_unhandled_events = false,
  30. };
  31. ESP_ERROR_CHECK(esp_timer_create(&notification_timer_args, &notification_timer_));
  32. // Update display timer
  33. esp_timer_create_args_t update_display_timer_args = {
  34. .callback = [](void *arg) {
  35. Display *display = static_cast<Display*>(arg);
  36. display->Update();
  37. },
  38. .arg = this,
  39. .dispatch_method = ESP_TIMER_TASK,
  40. .name = "display_update_timer",
  41. .skip_unhandled_events = true,
  42. };
  43. ESP_ERROR_CHECK(esp_timer_create(&update_display_timer_args, &update_timer_));
  44. ESP_ERROR_CHECK(esp_timer_start_periodic(update_timer_, 1000000));
  45. // Create a power management lock
  46. auto ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "display_update", &pm_lock_);
  47. if (ret == ESP_ERR_NOT_SUPPORTED) {
  48. ESP_LOGI(TAG, "Power management not supported");
  49. } else {
  50. ESP_ERROR_CHECK(ret);
  51. }
  52. }
  53. Display::~Display() {
  54. if (notification_timer_ != nullptr) {
  55. esp_timer_stop(notification_timer_);
  56. esp_timer_delete(notification_timer_);
  57. }
  58. if (update_timer_ != nullptr) {
  59. esp_timer_stop(update_timer_);
  60. esp_timer_delete(update_timer_);
  61. }
  62. if (network_label_ != nullptr) {
  63. lv_obj_del(network_label_);
  64. lv_obj_del(notification_label_);
  65. lv_obj_del(status_label_);
  66. lv_obj_del(mute_label_);
  67. lv_obj_del(battery_label_);
  68. lv_obj_del(emotion_label_);
  69. }
  70. if (pm_lock_ != nullptr) {
  71. esp_pm_lock_delete(pm_lock_);
  72. }
  73. }
  74. void Display::SetStatus(const char* status) {
  75. DisplayLockGuard lock(this);
  76. if (status_label_ == nullptr) {
  77. return;
  78. }
  79. lv_label_set_text(status_label_, status);
  80. lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
  81. lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
  82. }
  83. void Display::ShowNotification(const std::string &notification, int duration_ms) {
  84. ShowNotification(notification.c_str(), duration_ms);
  85. }
  86. void Display::ShowNotification(const char* notification, int duration_ms) {
  87. DisplayLockGuard lock(this);
  88. if (notification_label_ == nullptr) {
  89. return;
  90. }
  91. lv_label_set_text(notification_label_, notification);
  92. lv_obj_clear_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
  93. lv_obj_add_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
  94. esp_timer_stop(notification_timer_);
  95. ESP_ERROR_CHECK(esp_timer_start_once(notification_timer_, duration_ms * 1000));
  96. }
  97. void Display::Update() {
  98. auto& board = Board::GetInstance();
  99. auto codec = board.GetAudioCodec();
  100. {
  101. DisplayLockGuard lock(this);
  102. if (mute_label_ == nullptr) {
  103. return;
  104. }
  105. // 如果静音状态改变,则更新图标
  106. if (codec->output_volume() == 0 && !muted_) {
  107. muted_ = true;
  108. lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_MUTE);
  109. } else if (codec->output_volume() > 0 && muted_) {
  110. muted_ = false;
  111. lv_label_set_text(mute_label_, "");
  112. }
  113. }
  114. esp_pm_lock_acquire(pm_lock_);
  115. // 更新电池图标
  116. int battery_level;
  117. bool charging, discharging;
  118. const char* icon = nullptr;
  119. if (board.GetBatteryLevel(battery_level, charging, discharging)) {
  120. if (charging) {
  121. icon = FONT_AWESOME_BATTERY_CHARGING;
  122. } else {
  123. const char* levels[] = {
  124. FONT_AWESOME_BATTERY_EMPTY, // 0-19%
  125. FONT_AWESOME_BATTERY_1, // 20-39%
  126. FONT_AWESOME_BATTERY_2, // 40-59%
  127. FONT_AWESOME_BATTERY_3, // 60-79%
  128. FONT_AWESOME_BATTERY_FULL, // 80-99%
  129. FONT_AWESOME_BATTERY_FULL, // 100%
  130. };
  131. icon = levels[battery_level / 20];
  132. }
  133. DisplayLockGuard lock(this);
  134. if (battery_label_ != nullptr && battery_icon_ != icon) {
  135. battery_icon_ = icon;
  136. lv_label_set_text(battery_label_, battery_icon_);
  137. }
  138. if (low_battery_popup_ != nullptr) {
  139. if (strcmp(icon, FONT_AWESOME_BATTERY_EMPTY) == 0 && discharging) {
  140. if (lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框隐藏,则显示
  141. lv_obj_clear_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
  142. auto& app = Application::GetInstance();
  143. app.PlaySound(Lang::Sounds::P3_LOW_BATTERY);
  144. }
  145. } else {
  146. // Hide the low battery popup when the battery is not empty
  147. if (!lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框显示,则隐藏
  148. lv_obj_add_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
  149. }
  150. }
  151. }
  152. }
  153. // 升级固件时,不读取 4G 网络状态,避免占用 UART 资源
  154. auto device_state = Application::GetInstance().GetDeviceState();
  155. static const std::vector<DeviceState> allowed_states = {
  156. kDeviceStateIdle,
  157. kDeviceStateStarting,
  158. kDeviceStateWifiConfiguring,
  159. kDeviceStateListening,
  160. };
  161. if (std::find(allowed_states.begin(), allowed_states.end(), device_state) != allowed_states.end()) {
  162. icon = board.GetNetworkStateIcon();
  163. if (network_label_ != nullptr && icon != nullptr && network_icon_ != icon) {
  164. DisplayLockGuard lock(this);
  165. network_icon_ = icon;
  166. lv_label_set_text(network_label_, network_icon_);
  167. }
  168. }
  169. esp_pm_lock_release(pm_lock_);
  170. }
  171. void Display::SetEmotion(const char* emotion) {
  172. struct Emotion {
  173. const char* icon;
  174. const char* text;
  175. };
  176. static const std::vector<Emotion> emotions = {
  177. {FONT_AWESOME_EMOJI_NEUTRAL, "neutral"},
  178. {FONT_AWESOME_EMOJI_HAPPY, "happy"},
  179. {FONT_AWESOME_EMOJI_LAUGHING, "laughing"},
  180. {FONT_AWESOME_EMOJI_FUNNY, "funny"},
  181. {FONT_AWESOME_EMOJI_SAD, "sad"},
  182. {FONT_AWESOME_EMOJI_ANGRY, "angry"},
  183. {FONT_AWESOME_EMOJI_CRYING, "crying"},
  184. {FONT_AWESOME_EMOJI_LOVING, "loving"},
  185. {FONT_AWESOME_EMOJI_EMBARRASSED, "embarrassed"},
  186. {FONT_AWESOME_EMOJI_SURPRISED, "surprised"},
  187. {FONT_AWESOME_EMOJI_SHOCKED, "shocked"},
  188. {FONT_AWESOME_EMOJI_THINKING, "thinking"},
  189. {FONT_AWESOME_EMOJI_WINKING, "winking"},
  190. {FONT_AWESOME_EMOJI_COOL, "cool"},
  191. {FONT_AWESOME_EMOJI_RELAXED, "relaxed"},
  192. {FONT_AWESOME_EMOJI_DELICIOUS, "delicious"},
  193. {FONT_AWESOME_EMOJI_KISSY, "kissy"},
  194. {FONT_AWESOME_EMOJI_CONFIDENT, "confident"},
  195. {FONT_AWESOME_EMOJI_SLEEPY, "sleepy"},
  196. {FONT_AWESOME_EMOJI_SILLY, "silly"},
  197. {FONT_AWESOME_EMOJI_CONFUSED, "confused"}
  198. };
  199. // 查找匹配的表情
  200. std::string_view emotion_view(emotion);
  201. auto it = std::find_if(emotions.begin(), emotions.end(),
  202. [&emotion_view](const Emotion& e) { return e.text == emotion_view; });
  203. DisplayLockGuard lock(this);
  204. if (emotion_label_ == nullptr) {
  205. return;
  206. }
  207. // 如果找到匹配的表情就显示对应图标,否则显示默认的neutral表情
  208. if (it != emotions.end()) {
  209. lv_label_set_text(emotion_label_, it->icon);
  210. } else {
  211. lv_label_set_text(emotion_label_, FONT_AWESOME_EMOJI_NEUTRAL);
  212. }
  213. }
  214. void Display::SetIcon(const char* icon) {
  215. DisplayLockGuard lock(this);
  216. if (emotion_label_ == nullptr) {
  217. return;
  218. }
  219. lv_label_set_text(emotion_label_, icon);
  220. }
  221. void Display::SetChatMessage(const char* role, const char* content) {
  222. DisplayLockGuard lock(this);
  223. if (chat_message_label_ == nullptr) {
  224. return;
  225. }
  226. lv_label_set_text(chat_message_label_, content);
  227. }
  228. void Display::SetTheme(const std::string& theme_name) {
  229. current_theme_name_ = theme_name;
  230. Settings settings("display", true);
  231. settings.SetString("theme", theme_name);
  232. }