diff options
author | Scott Murray <scott.murray@konsulko.com> | 2022-02-28 13:04:45 -0500 |
---|---|---|
committer | Scott Murray <scott.murray@konsulko.com> | 2022-02-28 13:04:45 -0500 |
commit | 1332cc7d0a618ee88b4d19813340665332d406ca (patch) | |
tree | fe500276505160f5fd8ba8acb46334633ed0788a /bluetooth/bluetooth.cpp | |
parent | 0de8ac83e6a190d5fc124587d1f9f0a7f0198ce3 (diff) |
Add Bluetooth media control supportmarlin_12.93.0marlin/12.93.012.93.0
Rework to expose Bluetooth AVRCP media control in the Bluetooth
class, and use that support to implement a Bluetooth backend in the
Mediaplayer class. This replaces the scheme that existed with the
agl-service-mediaplayer binding where it abstracted away the
Bluetooth support itself. However, care has been take to make sure
that the exposed API to users of libqtappfw-mediaplayer has not
changed.
Bug-AGL: SPEC-4231
Signed-off-by: Scott Murray <scott.murray@konsulko.com>
Change-Id: I76b4f75621ce0121364eea3259b074bf3067ee88
Diffstat (limited to 'bluetooth/bluetooth.cpp')
-rw-r--r-- | bluetooth/bluetooth.cpp | 195 |
1 files changed, 181 insertions, 14 deletions
diff --git a/bluetooth/bluetooth.cpp b/bluetooth/bluetooth.cpp index c58b64b..5c7fd31 100644 --- a/bluetooth/bluetooth.cpp +++ b/bluetooth/bluetooth.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2021 Konsulko Group + * Copyright (C) 2018-2022 Konsulko Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,10 +23,17 @@ #include "bluetootheventhandler.h" -Bluetooth::Bluetooth (bool register_agent, QQmlContext *context, QObject * parent) : +Bluetooth::Bluetooth(bool register_agent, + QQmlContext *context, + bool handle_media, + QObject * parent) : QObject(parent), m_context(context), - m_agent(register_agent) + m_agent(register_agent), + m_handle_media(handle_media), + m_connected(false), + m_connected_device(""), + m_media_connected(false) { m_bluetooth = new BluetoothModel(); BluetoothModelFilter *m_model = new BluetoothModelFilter(); @@ -39,7 +46,7 @@ Bluetooth::Bluetooth (bool register_agent, QQmlContext *context, QObject * paren m_model->setFilterFixedString("false"); context->setContextProperty("BluetoothDiscoveryModel", m_model); - m_event_handler = new BluetoothEventHandler(this, register_agent); + m_event_handler = new BluetoothEventHandler(this, register_agent, handle_media); uuids.insert("a2dp", "0000110a-0000-1000-8000-00805f9b34fb"); uuids.insert("avrcp", "0000110e-0000-1000-8000-00805f9b34fb"); @@ -61,12 +68,19 @@ void Bluetooth::setDiscoverable(bool state) m_discoverable = state; - emit discoverableChanged(); + emit discoverableChanged(state); } void Bluetooth::start() { - bluez_init(m_agent, m_agent, m_event_handler->init_cb, m_event_handler); + // NOTE: An explicit "start" is somewhat required at present as calling + // bluez_init in the constructor means state update signals get + // emitted before the QML in an app seems able to receive them. + // A slightly better alternative might be something like a + // "refresh" function, or documenting that apps must explicitly + // call getters of relevant values on start. + + bluez_init(m_agent, m_agent, m_event_handler->init_cb, m_event_handler); } void Bluetooth::discovery_command(bool state) @@ -153,7 +167,7 @@ void Bluetooth::disconnect(QString device) bluez_device_disconnect(device_cstr, NULL); } -void Bluetooth::send_confirmation(int pincode) +void Bluetooth::send_confirmation(const int pincode) { QString pincode_str; pincode_str.setNum(pincode); @@ -163,7 +177,75 @@ void Bluetooth::send_confirmation(int pincode) bluez_confirm_pairing(pincode_cstr); } -void Bluetooth::init_adapter_state(QString adapter) +void Bluetooth::media_control(MediaAction action) +{ + QString action_name; + bool action_allowed = true; + bluez_media_control_t bluez_action; + switch (action) { + case Connect: + bluez_action = BLUEZ_MEDIA_CONTROL_CONNECT; + action_name = "Connect"; + action_allowed = !m_media_connected; + break; + case Disconnect: + bluez_action = BLUEZ_MEDIA_CONTROL_DISCONNECT; + action_name = "Disconnect"; + action_allowed = m_media_connected; + break; + case Play: + bluez_action = BLUEZ_MEDIA_CONTROL_PLAY; + action_name = "Play"; + break; + case Pause: + bluez_action = BLUEZ_MEDIA_CONTROL_PAUSE; + action_name = "Pause"; + break; + case Stop: + bluez_action = BLUEZ_MEDIA_CONTROL_STOP; + action_name = "Stop"; + break; + case Next: + bluez_action = BLUEZ_MEDIA_CONTROL_NEXT; + action_name = "Next"; + break; + case Previous: + bluez_action = BLUEZ_MEDIA_CONTROL_PREVIOUS; + action_name = "Previous"; + break; + case FastForward: + bluez_action = BLUEZ_MEDIA_CONTROL_FASTFORWARD; + action_name = "Fastforward"; + break; + case Rewind: + bluez_action = BLUEZ_MEDIA_CONTROL_REWIND; + action_name = "Rewind"; + break; + case Loop: + // Not implemented, but potentially possible with bluez-glib addition + default: + break; + } +#ifdef BLUETOOTH_EVENT_DEBUG + qDebug() << "Bluetooth::media_control: enter, action = " << action_name; + qDebug() << "Bluetooth::media_control: m_connected = " << m_connected + << ", m_media_connected = " << m_media_connected; +#endif + + if (!(m_connected && action_allowed)) { + qDebug() << "Bluetooth::media_control: not connected or invalid action!"; + return; + } + + QByteArray device_ba = m_connected_device.toLocal8Bit(); + const char *device_cstr = device_ba.data(); + bluez_device_avrcp_controls(device_cstr, bluez_action); +} + + +// Private + +void Bluetooth::init_adapter_state(const QString &adapter) { // Get initial power state GVariant *reply = NULL; @@ -171,7 +253,7 @@ void Bluetooth::init_adapter_state(QString adapter) if (rc && reply) { GVariantDict *props_dict = g_variant_dict_new(reply); gboolean powered = FALSE; - if (g_variant_dict_lookup(props_dict, "Powered", "b", &powered)) { + if (g_variant_dict_lookup(props_dict, "Powered", "b", &powered)) { if (m_power != powered) { m_power = powered; emit powerChanged(m_power); @@ -179,10 +261,16 @@ void Bluetooth::init_adapter_state(QString adapter) } g_variant_dict_unref(props_dict); g_variant_unref(reply); - } + } // Get initial device list refresh_device_list(); + + // Do a refresh of the media state to handle the situation where + // a client app has been started after a phone has been connected + // (and thus misses seeing the related events go by). + if (m_handle_media) + refresh_media_state(); } void Bluetooth::refresh_device_list(void) @@ -202,8 +290,12 @@ void Bluetooth::refresh_device_list(void) GVariant *var = NULL; while (g_variant_iter_next(array, "{&sv}", &key, &var)) { BluetoothDevice *device = m_bluetooth->updateDeviceProperties(nullptr, key, var); - if (device) + if (device) { m_bluetooth->addDevice(device); + if (device->connected()) { + update_connected_state(device->id(), true); + } + } g_variant_unref(var); } @@ -211,6 +303,42 @@ void Bluetooth::refresh_device_list(void) g_variant_unref(reply); } +void Bluetooth::refresh_media_state() +{ + if (!(m_handle_media && m_connected && m_connected_device.count())) + return; + + QByteArray device_ba = m_connected_device.toLocal8Bit(); + const char *device_cstr = device_ba.data(); + + GVariant *reply = NULL; + if (!bluez_get_media_control_properties(device_cstr, &reply)) + return; + + GVariantDict *props_dict = g_variant_dict_new(reply); + if (!props_dict) { + g_variant_unref(reply); + return; + } + + gboolean connected = FALSE; + if (g_variant_dict_lookup(props_dict, "Connected", "b", &connected)) { + update_media_connected_state(connected); + + GVariant *player_reply = NULL; + if(bluez_get_media_player_properties(device_cstr, &player_reply)) { + QVariantMap tmp; + m_event_handler->parse_media_player_properties(player_reply, tmp); + if (!tmp.empty()) + update_media_properties(tmp); + + g_variant_unref(player_reply); + } + } + g_variant_dict_unref(props_dict); + g_variant_unref(reply); +} + void Bluetooth::set_discovery_filter(void) { QList<QString> values = uuids.values(); @@ -233,7 +361,7 @@ void Bluetooth::set_discovery_filter(void) g_free(transport); } -void Bluetooth::update_adapter_power(bool powered) +void Bluetooth::update_adapter_power(const bool powered) { if (!powered) m_bluetooth->removeAllDevices(); @@ -251,11 +379,50 @@ void Bluetooth::update_adapter_power(bool powered) bool discoverable = m_discoverable; m_discoverable = false; if (discoverable != m_discoverable) - emit discoverableChanged(); + emit discoverableChanged(false); + } +} + +void Bluetooth::update_connected_state(const QString &device, const bool connected) +{ +#ifdef BLUETOOTH_EVENT_DEBUG + qDebug() << "Bluetooth::update_connected_state: device = " << device + << ", connected = " << connected; +#endif + if (m_connected != connected) { + if (!m_connected) { + // Connecting + m_connected = true; + m_connected_device = device; + emit connectedChanged(true); + } else if (m_connected_device == device) { + // Disconnecting + m_connected = false; + m_connected_device = ""; + emit connectedChanged(false); + } else { + qDebug() << "Bluetooth::update_connected_state: ignoring " << device; + } } } -void Bluetooth::request_confirmation(int pincode) +void Bluetooth::update_media_connected_state(const bool connected) +{ +#ifdef BLUETOOTH_EVENT_DEBUG + qDebug() << "Bluetooth::update_media_connected_state: connected = " << connected; +#endif + if (m_media_connected != connected) { + m_media_connected = connected; + emit mediaConnectedChanged(connected); + } +} + +void Bluetooth::update_media_properties(const QVariantMap &metadata) +{ + emit mediaPropertiesChanged(metadata); +} + +void Bluetooth::request_confirmation(const int pincode) { QString pincode_str; pincode_str.setNum(pincode); |