summaryrefslogtreecommitdiffstats
path: root/bluetooth/bluetooth.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bluetooth/bluetooth.cpp')
-rw-r--r--bluetooth/bluetooth.cpp195
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);