summaryrefslogtreecommitdiff
path: root/src/ui/screen_settings.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-09-22 15:48:41 +1000
committerjacqueline <me@jacqueline.id.au>2023-09-22 21:19:19 +1000
commitb192975cb1eeb4e6b7c7bf53cdf42701c55f034a (patch)
tree39755851543d596f2630704be9efb56be1f39bfc /src/ui/screen_settings.cpp
parentcbd99b2134c6c471708deb409a4b0fcc4c31516d (diff)
downloadtangara-fw-b192975cb1eeb4e6b7c7bf53cdf42701c55f034a.tar.gz
make bluetooth pairing ui functional
Diffstat (limited to 'src/ui/screen_settings.cpp')
-rw-r--r--src/ui/screen_settings.cpp152
1 files changed, 140 insertions, 12 deletions
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) {