From b192975cb1eeb4e6b7c7bf53cdf42701c55f034a Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 22 Sep 2023 15:48:41 +1000 Subject: [PATCH] make bluetooth pairing ui functional --- src/audio/audio_fsm.cpp | 25 +++- src/audio/bt_audio_output.cpp | 6 +- src/audio/include/audio_events.hpp | 2 + src/audio/include/audio_fsm.hpp | 2 + src/audio/include/bt_audio_output.hpp | 4 +- src/drivers/bluetooth.cpp | 34 +++-- src/drivers/include/bluetooth.hpp | 9 +- src/drivers/include/bluetooth_types.hpp | 5 + src/drivers/nvs.cpp | 5 +- src/events/include/event_queue.hpp | 4 + src/system_fsm/booting.cpp | 19 ++- src/system_fsm/include/system_events.hpp | 2 + src/ui/include/screen_settings.hpp | 22 +++- src/ui/include/ui_fsm.hpp | 9 ++ src/ui/screen_settings.cpp | 152 +++++++++++++++++++++-- src/ui/ui_fsm.cpp | 13 +- 16 files changed, 278 insertions(+), 35 deletions(-) diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index af43c9b9..06b98420 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -26,6 +26,7 @@ #include "future_fetcher.hpp" #include "i2s_audio_output.hpp" #include "i2s_dac.hpp" +#include "nvs.hpp" #include "service_locator.hpp" #include "system_events.hpp" #include "track.hpp" @@ -42,6 +43,7 @@ std::shared_ptr AudioState::sFileSource; std::unique_ptr AudioState::sDecoder; std::shared_ptr AudioState::sSampleConverter; std::shared_ptr AudioState::sI2SOutput; +std::shared_ptr AudioState::sBtOutput; std::shared_ptr AudioState::sOutput; std::optional AudioState::sCurrentTrack; @@ -75,6 +77,20 @@ void AudioState::react(const ChangeMaxVolume& ev) { sServices->nvs().AmpMaxVolume(ev.new_max); } +void AudioState::react(const OutputModeChanged& ev) { + // TODO: handle SetInUse + ESP_LOGI(kTag, "output mode changed"); + auto new_mode = sServices->nvs().OutputMode(); + switch (new_mode.get()) { + case drivers::NvsStorage::Output::kBluetooth: + sOutput = sBtOutput; + break; + case drivers::NvsStorage::Output::kHeadphones: + sOutput = sI2SOutput; + break; + } +} + namespace states { void Uninitialised::react(const system_fsm::BootComplete& ev) { @@ -90,13 +106,18 @@ void Uninitialised::react(const system_fsm::BootComplete& ev) { sFileSource.reset(new FatfsAudioInput(sServices->tag_parser())); sI2SOutput.reset(new I2SAudioOutput(sServices->gpios(), std::unique_ptr{*dac})); + sBtOutput.reset(new BluetoothAudioOutput(sServices->bluetooth())); auto& nvs = sServices->nvs(); sI2SOutput->SetMaxVolume(nvs.AmpMaxVolume().get()); sI2SOutput->SetVolumeDb(nvs.AmpCurrentVolume().get()); - sOutput = sI2SOutput; - // sOutput.reset(new BluetoothAudioOutput(bluetooth)); + if (sServices->nvs().OutputMode().get() == + drivers::NvsStorage::Output::kHeadphones) { + sOutput = sI2SOutput; + } else { + sOutput = sBtOutput; + } sSampleConverter.reset(new SampleConverter()); sSampleConverter->SetOutput(sOutput); diff --git a/src/audio/bt_audio_output.cpp b/src/audio/bt_audio_output.cpp index 2e54f69a..374906fd 100644 --- a/src/audio/bt_audio_output.cpp +++ b/src/audio/bt_audio_output.cpp @@ -29,16 +29,16 @@ namespace audio { static constexpr size_t kDrainBufferSize = 48 * 1024; -BluetoothAudioOutput::BluetoothAudioOutput(drivers::Bluetooth* bt) +BluetoothAudioOutput::BluetoothAudioOutput(drivers::Bluetooth& bt) : IAudioOutput(kDrainBufferSize, MALLOC_CAP_SPIRAM), bluetooth_(bt) {} BluetoothAudioOutput::~BluetoothAudioOutput() {} auto BluetoothAudioOutput::SetInUse(bool in_use) -> void { if (in_use) { - bluetooth_->SetSource(stream()); + bluetooth_.SetSource(stream()); } else { - bluetooth_->SetSource(nullptr); + bluetooth_.SetSource(nullptr); } } diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp index db6a3297..5af419ab 100644 --- a/src/audio/include/audio_events.hpp +++ b/src/audio/include/audio_events.hpp @@ -41,6 +41,8 @@ struct ChangeMaxVolume : tinyfsm::Event { struct TogglePlayPause : tinyfsm::Event {}; +struct OutputModeChanged : tinyfsm::Event {}; + namespace internal { struct InputFileOpened : tinyfsm::Event {}; diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index 46d3d338..1c0b8aaa 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -45,6 +45,7 @@ class AudioState : public tinyfsm::Fsm { void react(const system_fsm::KeyDownChanged&); void react(const system_fsm::HasPhonesChanged&); void react(const ChangeMaxVolume&); + void react(const OutputModeChanged&); virtual void react(const system_fsm::BootComplete&) {} @@ -65,6 +66,7 @@ class AudioState : public tinyfsm::Fsm { static std::unique_ptr sDecoder; static std::shared_ptr sSampleConverter; static std::shared_ptr sI2SOutput; + static std::shared_ptr sBtOutput; static std::shared_ptr sOutput; static std::optional sCurrentTrack; diff --git a/src/audio/include/bt_audio_output.hpp b/src/audio/include/bt_audio_output.hpp index a8e44f31..734a7ed1 100644 --- a/src/audio/include/bt_audio_output.hpp +++ b/src/audio/include/bt_audio_output.hpp @@ -21,7 +21,7 @@ namespace audio { class BluetoothAudioOutput : public IAudioOutput { public: - BluetoothAudioOutput(drivers::Bluetooth* bt); + BluetoothAudioOutput(drivers::Bluetooth& bt); ~BluetoothAudioOutput(); auto SetInUse(bool) -> void override; @@ -39,7 +39,7 @@ class BluetoothAudioOutput : public IAudioOutput { BluetoothAudioOutput& operator=(const BluetoothAudioOutput&) = delete; private: - drivers::Bluetooth* bluetooth_; + drivers::Bluetooth& bluetooth_; }; } // namespace audio diff --git a/src/drivers/bluetooth.cpp b/src/drivers/bluetooth.cpp index f3373849..924cdf42 100644 --- a/src/drivers/bluetooth.cpp +++ b/src/drivers/bluetooth.cpp @@ -8,6 +8,7 @@ #include #include +#include "bluetooth_types.hpp" #include "esp_a2dp_api.h" #include "esp_avrc_api.h" #include "esp_bt.h" @@ -56,7 +57,7 @@ auto a2dp_data_cb(uint8_t* buf, int32_t buf_size) -> int32_t { return xStreamBufferReceive(stream, buf, buf_size, 0); } -Bluetooth::Bluetooth(NvsStorage* storage) { +Bluetooth::Bluetooth(NvsStorage& storage) { bluetooth::BluetoothState::Init(storage); } @@ -72,6 +73,10 @@ auto Bluetooth::Disable() -> void { bluetooth::events::Disable{}); } +auto Bluetooth::IsEnabled() -> bool { + return !bluetooth::BluetoothState::is_in_state(); +} + auto Bluetooth::KnownDevices() -> std::vector { std::vector out = bluetooth::BluetoothState::devices(); std::sort(out.begin(), out.end(), [](const auto& a, const auto& b) -> bool { @@ -98,6 +103,11 @@ auto Bluetooth::SetSource(StreamBufferHandle_t src) -> void { bluetooth::events::SourceChanged{}); } +auto Bluetooth::SetEventHandler(std::function cb) + -> void { + bluetooth::BluetoothState::event_handler(cb); +} + auto DeviceName() -> std::string { uint8_t mac[8]{0}; esp_efuse_mac_get_default(mac); @@ -111,16 +121,17 @@ namespace bluetooth { NvsStorage* BluetoothState::sStorage_; -std::mutex BluetoothState::sDevicesMutex_; -std::map BluetoothState::sDevices_; -std::optional BluetoothState::sPreferredDevice_; +std::mutex BluetoothState::sDevicesMutex_{}; +std::map BluetoothState::sDevices_{}; +std::optional BluetoothState::sPreferredDevice_{}; mac_addr_t BluetoothState::sCurrentDevice_; std::atomic BluetoothState::sSource_; +std::function BluetoothState::sEventHandler_; -auto BluetoothState::Init(NvsStorage* storage) -> void { - sStorage_ = storage; - sPreferredDevice_ = storage->PreferredBluetoothDevice().get(); +auto BluetoothState::Init(NvsStorage& storage) -> void { + sStorage_ = &storage; + sPreferredDevice_ = storage.PreferredBluetoothDevice().get(); tinyfsm::FsmList::start(); } @@ -153,6 +164,11 @@ auto BluetoothState::source(StreamBufferHandle_t src) -> void { sSource_.store(src); } +auto BluetoothState::event_handler(std::function cb) -> void { + std::lock_guard lock{sDevicesMutex_}; + sEventHandler_ = cb; +} + static bool sIsFirstEntry = true; void Disabled::entry() { @@ -303,6 +319,10 @@ auto Scanning::OnDeviceDiscovered(esp_bt_gap_cb_param_t* param) -> void { sCurrentDevice_ = device.address; is_preferred = true; } + + if (sEventHandler_) { + std::invoke(sEventHandler_, Event::kKnownDevicesChanged); + } } if (is_preferred) { diff --git a/src/drivers/include/bluetooth.hpp b/src/drivers/include/bluetooth.hpp index fc72d393..1489b790 100644 --- a/src/drivers/include/bluetooth.hpp +++ b/src/drivers/include/bluetooth.hpp @@ -26,15 +26,17 @@ namespace drivers { */ class Bluetooth { public: - Bluetooth(NvsStorage* storage); + Bluetooth(NvsStorage& storage); auto Enable() -> bool; auto Disable() -> void; + auto IsEnabled() -> bool; auto KnownDevices() -> std::vector; auto SetPreferredDevice(const bluetooth::mac_addr_t& mac) -> void; auto SetSource(StreamBufferHandle_t) -> void; + auto SetEventHandler(std::function cb) -> void; }; namespace bluetooth { @@ -64,7 +66,7 @@ struct Avrc : public tinyfsm::Event { class BluetoothState : public tinyfsm::Fsm { public: - static auto Init(NvsStorage* storage) -> void; + static auto Init(NvsStorage& storage) -> void; static auto devices() -> std::vector; static auto preferred_device() -> std::optional; @@ -73,6 +75,8 @@ class BluetoothState : public tinyfsm::Fsm { static auto source() -> StreamBufferHandle_t; static auto source(StreamBufferHandle_t) -> void; + static auto event_handler(std::function) -> void; + virtual ~BluetoothState(){}; virtual void entry() {} @@ -96,6 +100,7 @@ class BluetoothState : public tinyfsm::Fsm { static mac_addr_t sCurrentDevice_; static std::atomic sSource_; + static std::function sEventHandler_; }; class Disabled : public BluetoothState { diff --git a/src/drivers/include/bluetooth_types.hpp b/src/drivers/include/bluetooth_types.hpp index 03100651..12ed5cb3 100644 --- a/src/drivers/include/bluetooth_types.hpp +++ b/src/drivers/include/bluetooth_types.hpp @@ -16,5 +16,10 @@ struct Device { int8_t signal_strength; }; +enum class Event { + kKnownDevicesChanged, + kConnectionStateChanged, +}; + } // namespace bluetooth } // namespace drivers diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index 11dde08c..67867c07 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -128,7 +128,7 @@ auto NvsStorage::OutputMode() -> std::future { nvs_get_u8(handle_, kKeyOutput, &out); switch (out) { case static_cast(Output::kBluetooth): - return Output::kHeadphones; + return Output::kBluetooth; case static_cast(Output::kHeadphones): default: return Output::kHeadphones; @@ -138,7 +138,8 @@ auto NvsStorage::OutputMode() -> std::future { auto NvsStorage::OutputMode(Output out) -> std::future { return writer_->Dispatch([&]() { - nvs_set_u8(handle_, kKeyOutput, static_cast(out)); + uint8_t as_int = static_cast(out); + nvs_set_u8(handle_, kKeyOutput, as_int); return nvs_commit(handle_) == ESP_OK; }); } diff --git a/src/events/include/event_queue.hpp b/src/events/include/event_queue.hpp index 1ea67446..332f7be5 100644 --- a/src/events/include/event_queue.hpp +++ b/src/events/include/event_queue.hpp @@ -80,6 +80,10 @@ class Dispatcher { queue_->Add(dispatch_fn); } + auto RunOnTask(const std::function& fn) -> void { + queue_->Add(fn); + } + Dispatcher(Dispatcher const&) = delete; void operator=(Dispatcher const&) = delete; diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp index 006ed395..28cd8cf6 100644 --- a/src/system_fsm/booting.cpp +++ b/src/system_fsm/booting.cpp @@ -11,6 +11,7 @@ #include "audio_fsm.hpp" #include "battery.hpp" #include "bluetooth.hpp" +#include "bluetooth_types.hpp" #include "core/lv_obj.h" #include "display_init.hpp" #include "esp_err.h" @@ -41,6 +42,12 @@ namespace states { static const char kTag[] = "BOOT"; +static auto bt_event_cb(drivers::bluetooth::Event ev) -> void { + if (ev == drivers::bluetooth::Event::kKnownDevicesChanged) { + events::Ui().Dispatch(BluetoothDevicesChanged{}); + } +} + auto Booting::entry() -> void { ESP_LOGI(kTag, "beginning tangara boot"); sServices.reset(new ServiceLocator()); @@ -71,9 +78,15 @@ auto Booting::entry() -> void { sServices->track_queue(std::make_unique()); sServices->tag_parser(std::make_unique()); - // ESP_LOGI(kTag, "starting bluetooth"); - // sBluetooth.reset(new drivers::Bluetooth(sNvs.get())); - // sBluetooth->Enable(); + ESP_LOGI(kTag, "init bluetooth"); + sServices->bluetooth(std::make_unique(sServices->nvs())); + sServices->bluetooth().SetEventHandler(bt_event_cb); + + if (sServices->nvs().OutputMode().get() == + drivers::NvsStorage::Output::kBluetooth) { + ESP_LOGI(kTag, "enabling bluetooth"); + sServices->bluetooth().Enable(); + } BootComplete ev{.services = sServices}; events::Audio().Dispatch(ev); diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp index e22fe2ae..9cd75d4d 100644 --- a/src/system_fsm/include/system_events.hpp +++ b/src/system_fsm/include/system_events.hpp @@ -56,6 +56,8 @@ struct HasPhonesChanged : tinyfsm::Event { struct ChargingStatusChanged : tinyfsm::Event {}; struct BatteryStateChanged : tinyfsm::Event {}; +struct BluetoothDevicesChanged : tinyfsm::Event {}; + namespace internal { struct GpioInterrupt : tinyfsm::Event {}; diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp index caa23fd4..4e1936a2 100644 --- a/src/ui/include/screen_settings.hpp +++ b/src/ui/include/screen_settings.hpp @@ -8,9 +8,12 @@ #include #include +#include #include #include +#include "bluetooth.hpp" +#include "bluetooth_types.hpp" #include "display.hpp" #include "index.hpp" #include "lvgl.h" @@ -28,7 +31,24 @@ class Settings : public MenuScreen { class Bluetooth : public MenuScreen { public: - Bluetooth(); + Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs); + + auto ChangeEnabledState(bool enabled) -> void; + auto RefreshDevicesList() -> void; + auto OnDeviceSelected(size_t index) -> void; + + private: + auto RemoveAllDevices() -> void; + auto AddPreferredDevice(const drivers::bluetooth::Device&) -> void; + auto AddDevice(const drivers::bluetooth::Device&) -> void; + + drivers::Bluetooth& bt_; + drivers::NvsStorage& nvs_; + + lv_obj_t* devices_list_; + lv_obj_t* preferred_device_; + + std::list macs_in_list_; }; class Headphones : public MenuScreen { diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 5363e1a4..9980dac6 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -17,6 +17,7 @@ #include "nvs.hpp" #include "relative_wheel.hpp" #include "screen_playing.hpp" +#include "screen_settings.hpp" #include "service_locator.hpp" #include "tinyfsm.hpp" @@ -72,6 +73,7 @@ class UiState : public tinyfsm::Fsm { virtual void react(const system_fsm::DisplayReady&) {} virtual void react(const system_fsm::BootComplete&) {} virtual void react(const system_fsm::StorageMounted&) {} + virtual void react(const system_fsm::BluetoothDevicesChanged&) {} protected: void PushScreen(std::shared_ptr); @@ -112,6 +114,7 @@ class Onboarding : public UiState { }; class Browse : public UiState { + public: void entry() override; void react(const internal::RecordSelected&) override; @@ -122,10 +125,16 @@ class Browse : public UiState { void react(const internal::ShowSettingsPage&) override; void react(const system_fsm::StorageMounted&) override; + void react(const system_fsm::BluetoothDevicesChanged&) override; + using UiState::react; + + private: + std::weak_ptr bluetooth_screen_; }; class Playing : public UiState { + public: void entry() override; void exit() override; diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp index d8c867dc..faeac865 100644 --- a/src/ui/screen_settings.cpp +++ b/src/ui/screen_settings.cpp @@ -9,8 +9,11 @@ #include #include "audio_events.hpp" +#include "bluetooth.hpp" +#include "bluetooth_types.hpp" #include "core/lv_event.h" #include "core/lv_obj.h" +#include "core/lv_obj_tree.h" #include "display.hpp" #include "esp_log.h" @@ -100,7 +103,18 @@ static auto label_pair(lv_obj_t* parent, return right_label; } -Bluetooth::Bluetooth() : MenuScreen("Bluetooth") { +static auto toggle_bt_cb(lv_event_t* ev) { + Bluetooth* instance = reinterpret_cast(ev->user_data); + instance->ChangeEnabledState(lv_obj_has_state(ev->target, LV_STATE_CHECKED)); +} + +static auto select_device_cb(lv_event_t* ev) { + Bluetooth* instance = reinterpret_cast(ev->user_data); + instance->OnDeviceSelected(lv_obj_get_index(ev->target)); +} + +Bluetooth::Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs) + : MenuScreen("Bluetooth"), bt_(bt), nvs_(nvs) { lv_obj_t* toggle_container = settings_container(content_); lv_obj_t* toggle_label = lv_label_create(toggle_container); lv_label_set_text(toggle_label, "Enable"); @@ -108,20 +122,134 @@ Bluetooth::Bluetooth() : MenuScreen("Bluetooth") { lv_obj_t* toggle = lv_switch_create(toggle_container); lv_group_add_obj(group_, toggle); + if (bt.IsEnabled()) { + lv_obj_add_state(toggle, LV_STATE_CHECKED); + } + + lv_obj_add_event_cb(toggle, toggle_bt_cb, LV_EVENT_VALUE_CHANGED, this); + lv_obj_t* devices_label = lv_label_create(content_); lv_label_set_text(devices_label, "Devices"); - lv_obj_t* devices_list = lv_list_create(content_); - lv_list_add_text(devices_list, "My Headphones"); - lv_group_add_obj(group_, - lv_list_add_btn(devices_list, NULL, "Something else")); - lv_group_add_obj(group_, lv_list_add_btn(devices_list, NULL, "A car??")); - lv_group_add_obj(group_, - lv_list_add_btn(devices_list, NULL, "OLED TV ANDROID")); - lv_group_add_obj( - group_, lv_list_add_btn(devices_list, NULL, "there could be another")); - lv_group_add_obj(group_, lv_list_add_btn(devices_list, NULL, - "this one has a really long name")); + devices_list_ = lv_list_create(content_); + RefreshDevicesList(); +} + +auto Bluetooth::ChangeEnabledState(bool enabled) -> void { + if (enabled) { + events::System().RunOnTask([&]() { bt_.Enable(); }); + nvs_.OutputMode(drivers::NvsStorage::Output::kBluetooth).get(); + } else { + events::System().RunOnTask([&]() { bt_.Disable(); }); + nvs_.OutputMode(drivers::NvsStorage::Output::kHeadphones).get(); + } + events::Audio().Dispatch(audio::OutputModeChanged{}); + RefreshDevicesList(); +} + +auto Bluetooth::RefreshDevicesList() -> void { + if (!bt_.IsEnabled()) { + // Bluetooth is disabled, so we just clear the list. + RemoveAllDevices(); + return; + } + + auto devices = bt_.KnownDevices(); + std::optional preferred_device = + nvs_.PreferredBluetoothDevice().get(); + + // If the user's current selection is within the devices list, then we need + // to be careful not to rearrange the list items underneath them. + lv_obj_t* current_selection = lv_group_get_focused(group_); + bool is_in_list = current_selection != NULL && + lv_obj_get_parent(current_selection) == devices_list_; + + if (!is_in_list) { + // The user isn't in the list! We can blow everything away and recreate it + // without issues. + RemoveAllDevices(); + + // First look to see if the user's preferred device is in the list. If it + // is, we hoist it up to the top of the list. + if (preferred_device) { + for (size_t i = 0; i < devices.size(); i++) { + if (devices[i].address == *preferred_device) { + AddPreferredDevice(devices[i]); + devices.erase(devices.begin() + i); + break; + } + } + } + + // The rest of the list is already sorted by signal strength. + for (const auto& device : devices) { + AddDevice(device); + } + } else { + // The user's selection is within the device list. We need to work out + // which devices are new, then add them to the end. + for (const auto& mac : macs_in_list_) { + auto pos = std::find_if( + devices.begin(), devices.end(), + [&mac](const auto& device) { return device.address == mac; }); + + if (pos != devices.end()) { + devices.erase(pos); + } + } + + // The remaining list is now just the new devices. + for (const auto& device : devices) { + if (preferred_device && device.address == *preferred_device) { + AddPreferredDevice(device); + } else { + AddDevice(device); + } + } + } +} + +auto Bluetooth::RemoveAllDevices() -> void { + while (lv_obj_get_child_cnt(devices_list_) > 0) { + lv_obj_del(lv_obj_get_child(devices_list_, 0)); + } + macs_in_list_.clear(); + preferred_device_ = nullptr; +} + +auto Bluetooth::AddPreferredDevice(const drivers::bluetooth::Device& dev) + -> void { + preferred_device_ = lv_list_add_btn(devices_list_, NULL, dev.name.c_str()); + macs_in_list_.push_back(dev.address); +} + +auto Bluetooth::AddDevice(const drivers::bluetooth::Device& dev) -> void { + lv_obj_t* item = lv_list_add_btn(devices_list_, NULL, dev.name.c_str()); + lv_group_add_obj(group_, item); + lv_obj_add_event_cb(item, select_device_cb, LV_EVENT_CLICKED, this); + macs_in_list_.push_back(dev.address); +} + +auto Bluetooth::OnDeviceSelected(size_t index) -> void { + // Tell the bluetooth driver that our preference changed. + auto it = macs_in_list_.begin(); + std::advance(it, index); + events::System().RunOnTask([=]() { bt_.SetPreferredDevice(*it); }); + + // Update which devices are selectable. + if (preferred_device_) { + lv_group_add_obj(group_, preferred_device_); + // Bubble the newly added object up to its visible position in the list. + size_t pos = lv_obj_get_index(preferred_device_); + while (pos > 0) { + lv_group_swap_obj(preferred_device_, + lv_obj_get_child(devices_list_, pos - 1)); + pos--; + } + } + + preferred_device_ = lv_obj_get_child(devices_list_, index); + lv_group_remove_obj(preferred_device_); } static void change_vol_limit_cb(lv_event_t* ev) { diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index c0c06bb0..18e9caf4 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -241,12 +241,16 @@ void Browse::react(const internal::ShowNowPlaying& ev) { void Browse::react(const internal::ShowSettingsPage& ev) { std::shared_ptr screen; + std::shared_ptr bt_screen; switch (ev.page) { case internal::ShowSettingsPage::Page::kRoot: screen.reset(new screens::Settings()); break; case internal::ShowSettingsPage::Page::kBluetooth: - screen.reset(new screens::Bluetooth()); + bt_screen = std::make_shared(sServices->bluetooth(), + sServices->nvs()); + screen = bt_screen; + bluetooth_screen_ = bt_screen; break; case internal::ShowSettingsPage::Page::kHeadphones: screen.reset(new screens::Headphones(sServices->nvs())); @@ -315,6 +319,13 @@ void Browse::react(const internal::BackPressed& ev) { PopScreen(); } +void Browse::react(const system_fsm::BluetoothDevicesChanged&) { + auto bt = bluetooth_screen_.lock(); + if (bt) { + bt->RefreshDevicesList(); + } +} + static std::shared_ptr sPlayingScreen; void Playing::entry() {