diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index af261412..906e9e1f 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -9,7 +9,8 @@ idf_component_register( "modal_progress.cpp" "modal.cpp" "modal_confirm.cpp" "screen_settings.cpp" "splash.c" "font_fusion.c" "font_symbols.c" "icons/battery_empty.c" "icons/battery_full.c" "icons/battery_20.c" - "icons/battery_40.c" "icons/battery_60.c" "icons/battery_80.c" + "icons/battery_40.c" "icons/battery_60.c" "icons/battery_80.c" "icons/play.c" + "icons/pause.c" "icons/bluetooth.c" INCLUDE_DIRS "include" REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/ui/icons/battery_20.c b/src/ui/icons/battery_20.c index 0ae909b9..3be6b614 100644 --- a/src/ui/icons/battery_20.c +++ b/src/ui/icons/battery_20.c @@ -21,7 +21,7 @@ #define LV_ATTRIBUTE_IMG_BATTERY_20 #endif -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_20 uint8_t battery_20_map[] = { +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_20 uint8_t battery_20_map[] = { 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/ 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/ 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/ @@ -41,7 +41,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_2 0x54, 0x00, 0x15, }; -const lv_img_dsc_t battery_20 = { +const lv_img_dsc_t kIconBattery20 = { .header.cf = LV_IMG_CF_INDEXED_2BIT, .header.always_zero = 0, .header.reserved = 0, diff --git a/src/ui/icons/battery_40.c b/src/ui/icons/battery_40.c index 07310273..4a6ead0c 100644 --- a/src/ui/icons/battery_40.c +++ b/src/ui/icons/battery_40.c @@ -21,7 +21,7 @@ #define LV_ATTRIBUTE_IMG_BATTERY_40 #endif -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_40 uint8_t battery_40_map[] = { +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_40 uint8_t battery_40_map[] = { 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/ 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/ 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/ @@ -41,7 +41,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_4 0x54, 0x00, 0x15, }; -const lv_img_dsc_t battery_40 = { +const lv_img_dsc_t kIconBattery40 = { .header.cf = LV_IMG_CF_INDEXED_2BIT, .header.always_zero = 0, .header.reserved = 0, diff --git a/src/ui/icons/battery_60.c b/src/ui/icons/battery_60.c index ac7db567..4695cb73 100644 --- a/src/ui/icons/battery_60.c +++ b/src/ui/icons/battery_60.c @@ -21,7 +21,7 @@ #define LV_ATTRIBUTE_IMG_BATTERY_60 #endif -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_60 uint8_t battery_60_map[] = { +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_60 uint8_t battery_60_map[] = { 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/ 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/ 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/ @@ -41,7 +41,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_6 0x54, 0x00, 0x15, }; -const lv_img_dsc_t battery_60 = { +const lv_img_dsc_t kIconBattery60 = { .header.cf = LV_IMG_CF_INDEXED_2BIT, .header.always_zero = 0, .header.reserved = 0, diff --git a/src/ui/icons/battery_80.c b/src/ui/icons/battery_80.c index 086c0708..e0b60dfe 100644 --- a/src/ui/icons/battery_80.c +++ b/src/ui/icons/battery_80.c @@ -21,7 +21,7 @@ #define LV_ATTRIBUTE_IMG_BATTERY_80 #endif -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_80 uint8_t battery_80_map[] = { +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_80 uint8_t battery_80_map[] = { 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/ 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/ 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/ @@ -41,7 +41,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_8 0x54, 0x00, 0x15, }; -const lv_img_dsc_t battery_80 = { +const lv_img_dsc_t kIconBattery80 = { .header.cf = LV_IMG_CF_INDEXED_2BIT, .header.always_zero = 0, .header.reserved = 0, diff --git a/src/ui/icons/battery_empty.c b/src/ui/icons/battery_empty.c index ed356167..26f84863 100644 --- a/src/ui/icons/battery_empty.c +++ b/src/ui/icons/battery_empty.c @@ -41,7 +41,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_E 0x01, 0x55, 0x40, }; -const lv_img_dsc_t battery_empty = { +const lv_img_dsc_t kIconBatteryEmpty = { .header.cf = LV_IMG_CF_INDEXED_2BIT, .header.always_zero = 0, .header.reserved = 0, diff --git a/src/ui/icons/battery_full.c b/src/ui/icons/battery_full.c index fffd5a73..1e3b17e3 100644 --- a/src/ui/icons/battery_full.c +++ b/src/ui/icons/battery_full.c @@ -41,7 +41,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_F 0x54, 0x00, 0x15, }; -const lv_img_dsc_t battery_full = { +const lv_img_dsc_t kIconBatteryFull = { .header.cf = LV_IMG_CF_INDEXED_2BIT, .header.always_zero = 0, .header.reserved = 0, diff --git a/src/ui/icons/bluetooth.c b/src/ui/icons/bluetooth.c new file mode 100644 index 00000000..66322f8f --- /dev/null +++ b/src/ui/icons/bluetooth.c @@ -0,0 +1,54 @@ +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH +#define LV_ATTRIBUTE_IMG_BLUETOOTH +#endif + +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH uint8_t bluetooth_map[] = { + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 0*/ + 0xff, 0x75, 0x00, 0xff, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ + + 0x00, 0x55, 0x40, 0x00, + 0x01, 0x51, 0x50, 0x00, + 0x05, 0x50, 0x54, 0x00, + 0x05, 0x51, 0x14, 0x00, + 0x05, 0x11, 0x04, 0x00, + 0x15, 0x40, 0x15, 0x00, + 0x15, 0x50, 0x55, 0x00, + 0x15, 0x40, 0x15, 0x00, + 0x05, 0x11, 0x04, 0x00, + 0x05, 0x51, 0x14, 0x00, + 0x05, 0x50, 0x54, 0x00, + 0x01, 0x51, 0x50, 0x00, + 0x00, 0x55, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t kIconBluetooth = { + .header.cf = LV_IMG_CF_INDEXED_2BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 14, + .header.h = 14, + .data_size = 72, + .data = bluetooth_map, +}; diff --git a/src/ui/icons/pause.c b/src/ui/icons/pause.c new file mode 100644 index 00000000..8201b5bb --- /dev/null +++ b/src/ui/icons/pause.c @@ -0,0 +1,54 @@ +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_PAUSE +#define LV_ATTRIBUTE_IMG_PAUSE +#endif + +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_PAUSE uint8_t pause_map[] = { + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x50, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t kIconPause = { + .header.cf = LV_IMG_CF_INDEXED_2BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 14, + .header.h = 14, + .data_size = 72, + .data = pause_map, +}; diff --git a/src/ui/icons/play.c b/src/ui/icons/play.c new file mode 100644 index 00000000..8984eae4 --- /dev/null +++ b/src/ui/icons/play.c @@ -0,0 +1,54 @@ +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_PLAY +#define LV_ATTRIBUTE_IMG_PLAY +#endif + +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_PLAY uint8_t play_map[] = { + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x50, 0x00, 0x00, + 0x00, 0x55, 0x00, 0x00, + 0x00, 0x55, 0x50, 0x00, + 0x00, 0x55, 0x54, 0x00, + 0x00, 0x55, 0x54, 0x00, + 0x00, 0x55, 0x50, 0x00, + 0x00, 0x55, 0x00, 0x00, + 0x00, 0x50, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t kIconPlay = { + .header.cf = LV_IMG_CF_INDEXED_2BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 14, + .header.h = 14, + .data_size = 72, + .data = play_map, +}; diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 8f1aa1bc..17214e7b 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -48,6 +48,7 @@ class UiState : public tinyfsm::Fsm { virtual void react(const audio::PlaybackStarted&) {} virtual void react(const audio::PlaybackUpdate&) {} + virtual void react(const audio::PlaybackFinished&) {} virtual void react(const audio::QueueUpdate&) {} virtual void react(const system_fsm::KeyLockChanged&); @@ -125,6 +126,7 @@ class Playing : public UiState { void react(const audio::PlaybackStarted&) override; void react(const audio::PlaybackUpdate&) override; + void react(const audio::PlaybackFinished&) override; void react(const audio::QueueUpdate&) override; using UiState::react; }; diff --git a/src/ui/include/widget_top_bar.hpp b/src/ui/include/widget_top_bar.hpp index adb889fc..87920338 100644 --- a/src/ui/include/widget_top_bar.hpp +++ b/src/ui/include/widget_top_bar.hpp @@ -48,6 +48,7 @@ class TopBar { lv_obj_t* title_; lv_obj_t* playback_; lv_obj_t* battery_; + lv_obj_t* charging_; }; } // namespace widgets diff --git a/src/ui/themes.cpp b/src/ui/themes.cpp index bcbf01f2..dd9ef423 100644 --- a/src/ui/themes.cpp +++ b/src/ui/themes.cpp @@ -35,7 +35,6 @@ void Theme::Apply(void) { } void Theme::Callback(lv_obj_t* obj) { - ESP_LOGI("Theme", "Callback called on object %p", obj); lv_obj_set_style_text_font(obj, &font_fusion, 0); lv_obj_set_style_text_color(obj, lv_color_black(), 0); @@ -47,7 +46,6 @@ void Theme::Callback(lv_obj_t* obj) { } void Theme::ApplyStyle(lv_obj_t* obj, Style style) { - ESP_LOGI("Theme", "Apply style called on object %p", obj); if (style == Style::kTopBar) { lv_obj_set_style_border_color(obj, lv_palette_darken(LV_PALETTE_BLUE_GREY, 2), LV_PART_MAIN); lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN); diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 32032978..dd3ba8fd 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -8,6 +8,7 @@ #include +#include "audio_fsm.hpp" #include "battery.hpp" #include "core/lv_obj.h" #include "misc/lv_gc.h" @@ -110,12 +111,21 @@ void UiState::react(const system_fsm::BatteryStateChanged&) { void UiState::UpdateTopBar() { auto battery_state = sBattery->State(); + bool has_queue = sQueue->GetCurrent().has_value(); + bool is_playing = audio::AudioState::is_in_state(); + widgets::TopBar::State state{ .playback_state = widgets::TopBar::PlaybackState::kIdle, .battery_percent = static_cast( battery_state.has_value() ? battery_state->percent : 100), .is_charging = !battery_state.has_value() || battery_state->is_charging, }; + + if (has_queue) { + state.playback_state = is_playing ? widgets::TopBar::PlaybackState::kPlaying + : widgets::TopBar::PlaybackState::kPaused; + } + if (sCurrentScreen) { sCurrentScreen->UpdateTopBar(state); } @@ -208,6 +218,7 @@ void Playing::exit() { } void Playing::react(const audio::PlaybackStarted& ev) { + UpdateTopBar(); sPlayingScreen->OnTrackUpdate(); } @@ -215,6 +226,10 @@ void Playing::react(const audio::PlaybackUpdate& ev) { sPlayingScreen->OnPlaybackUpdate(ev.seconds_elapsed, ev.seconds_total); } +void Playing::react(const audio::PlaybackFinished& ev) { + UpdateTopBar(); +} + void Playing::react(const audio::QueueUpdate& ev) { sPlayingScreen->OnQueueUpdate(); } diff --git a/src/ui/widget_top_bar.cpp b/src/ui/widget_top_bar.cpp index 6b82b20f..1ee14bbd 100644 --- a/src/ui/widget_top_bar.cpp +++ b/src/ui/widget_top_bar.cpp @@ -11,18 +11,21 @@ #include "extra/layouts/flex/lv_flex.h" #include "font/lv_symbol_def.h" #include "font_symbols.hpp" +#include "themes.hpp" #include "ui_events.hpp" #include "ui_fsm.hpp" #include "widgets/lv_img.h" #include "widgets/lv_label.h" -#include "themes.hpp" -LV_IMG_DECLARE(battery_empty); -LV_IMG_DECLARE(battery_20); -LV_IMG_DECLARE(battery_40); -LV_IMG_DECLARE(battery_60); -LV_IMG_DECLARE(battery_80); -LV_IMG_DECLARE(battery_full); +LV_IMG_DECLARE(kIconBluetooth); +LV_IMG_DECLARE(kIconPlay); +LV_IMG_DECLARE(kIconPause); +LV_IMG_DECLARE(kIconBatteryEmpty); +LV_IMG_DECLARE(kIconBattery20); +LV_IMG_DECLARE(kIconBattery40); +LV_IMG_DECLARE(kIconBattery60); +LV_IMG_DECLARE(kIconBattery80); +LV_IMG_DECLARE(kIconBatteryFull); namespace ui { namespace widgets { @@ -37,7 +40,7 @@ TopBar::TopBar(lv_obj_t* parent, const Configuration& config) { lv_obj_set_flex_flow(container_, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(container_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_END); - + themes::Theme::instance()->ApplyStyle(container_, themes::Style::kTopBar); if (config.show_back_button) { @@ -55,39 +58,42 @@ TopBar::TopBar(lv_obj_t* parent, const Configuration& config) { lv_label_set_text(title_, config.title.c_str()); lv_obj_set_flex_grow(title_, 1); - playback_ = lv_label_create(container_); - lv_label_set_text(playback_, ""); - + playback_ = lv_img_create(container_); battery_ = lv_img_create(container_); + charging_ = lv_label_create(container_); } auto TopBar::Update(const State& state) -> void { switch (state.playback_state) { case PlaybackState::kIdle: - lv_label_set_text(playback_, "-"); + lv_img_set_src(playback_, NULL); break; case PlaybackState::kPaused: - lv_label_set_text(playback_, LV_SYMBOL_PAUSE); + lv_img_set_src(playback_, &kIconPause); break; case PlaybackState::kPlaying: - lv_label_set_text(playback_, LV_SYMBOL_PLAY); + lv_img_set_src(playback_, &kIconPlay); break; } + if (state.is_charging) { + lv_label_set_text(charging_, "+"); + } else { + lv_label_set_text(charging_, ""); + } + if (state.battery_percent >= 95) { - lv_img_set_src(battery_, &battery_full); + lv_img_set_src(battery_, &kIconBatteryFull); } else if (state.battery_percent >= 75) { - lv_img_set_src(battery_, &battery_80); - lv_label_set_text(battery_, ">70"); + lv_img_set_src(battery_, &kIconBattery80); } else if (state.battery_percent >= 55) { - lv_img_set_src(battery_, &battery_60); - lv_label_set_text(battery_, ">40"); + lv_img_set_src(battery_, &kIconBattery60); } else if (state.battery_percent >= 35) { - lv_img_set_src(battery_, &battery_40); + lv_img_set_src(battery_, &kIconBattery40); } else if (state.battery_percent >= 15) { - lv_img_set_src(battery_, &battery_20); + lv_img_set_src(battery_, &kIconBattery20); } else { - lv_img_set_src(battery_, &battery_empty); + lv_img_set_src(battery_, &kIconBatteryEmpty); } } diff --git a/tools/icons/raw/audio.png b/tools/icons/raw/audio.png new file mode 100644 index 00000000..b8ad9071 Binary files /dev/null and b/tools/icons/raw/audio.png differ diff --git a/tools/icons/raw/bluetooth.png b/tools/icons/raw/bluetooth.png new file mode 100644 index 00000000..7a5f2b27 Binary files /dev/null and b/tools/icons/raw/bluetooth.png differ diff --git a/tools/icons/raw/pause.png b/tools/icons/raw/pause.png new file mode 100644 index 00000000..ec388cd5 Binary files /dev/null and b/tools/icons/raw/pause.png differ diff --git a/tools/icons/raw/play.png b/tools/icons/raw/play.png new file mode 100644 index 00000000..0d0bb34d Binary files /dev/null and b/tools/icons/raw/play.png differ