diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-09-22 15:48:41 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-09-22 21:19:19 +1000 |
| commit | b192975cb1eeb4e6b7c7bf53cdf42701c55f034a (patch) | |
| tree | 39755851543d596f2630704be9efb56be1f39bfc /src/ui | |
| parent | cbd99b2134c6c471708deb409a4b0fcc4c31516d (diff) | |
| download | tangara-fw-b192975cb1eeb4e6b7c7bf53cdf42701c55f034a.tar.gz | |
make bluetooth pairing ui functional
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/include/screen_settings.hpp | 22 | ||||
| -rw-r--r-- | src/ui/include/ui_fsm.hpp | 9 | ||||
| -rw-r--r-- | src/ui/screen_settings.cpp | 152 | ||||
| -rw-r--r-- | src/ui/ui_fsm.cpp | 13 |
4 files changed, 182 insertions, 14 deletions
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 <stdint.h> #include <cstdint> +#include <list> #include <memory> #include <vector> +#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<drivers::bluetooth::mac_addr_t> 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<UiState> { 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<Screen>); @@ -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<screens::Bluetooth> 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 <string> #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<Bluetooth*>(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<Bluetooth*>(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<drivers::bluetooth::mac_addr_t> 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> screen; + std::shared_ptr<screens::Bluetooth> 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<screens::Bluetooth>(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<screens::Playing> sPlayingScreen; void Playing::entry() { |
