mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-01-28 22:01:50 +00:00
[Linux] Improve connection stability (#98)
This commit is contained in:
@@ -2,10 +2,16 @@
|
||||
#include "logger.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDBusObjectPath>
|
||||
#include <QDBusMetaType>
|
||||
|
||||
BluetoothMonitor::BluetoothMonitor(QObject *parent)
|
||||
BluetoothMonitor::BluetoothMonitor(QObject *parent)
|
||||
: QObject(parent), m_dbus(QDBusConnection::systemBus())
|
||||
{
|
||||
// Register meta-types for D-Bus interaction
|
||||
qDBusRegisterMetaType<QDBusObjectPath>();
|
||||
qDBusRegisterMetaType<ManagedObjectList>();
|
||||
|
||||
if (!m_dbus.isConnected())
|
||||
{
|
||||
LOG_WARN("Failed to connect to system D-Bus");
|
||||
@@ -13,6 +19,7 @@ BluetoothMonitor::BluetoothMonitor(QObject *parent)
|
||||
}
|
||||
|
||||
registerDBusService();
|
||||
checkAlreadyConnectedDevices(); // Check for already connected devices on startup
|
||||
}
|
||||
|
||||
BluetoothMonitor::~BluetoothMonitor()
|
||||
@@ -23,18 +30,6 @@ BluetoothMonitor::~BluetoothMonitor()
|
||||
void BluetoothMonitor::registerDBusService()
|
||||
{
|
||||
// Match signals for PropertiesChanged on any BlueZ Device interface
|
||||
QString matchRule = QStringLiteral("type='signal',"
|
||||
"interface='org.freedesktop.DBus.Properties',"
|
||||
"member='PropertiesChanged',"
|
||||
"path_namespace='/org/bluez'");
|
||||
|
||||
m_dbus.connect("org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus",
|
||||
"AddMatch",
|
||||
this,
|
||||
SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
|
||||
|
||||
if (!m_dbus.connect("", "", "org.freedesktop.DBus.Properties", "PropertiesChanged",
|
||||
this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))))
|
||||
{
|
||||
@@ -42,6 +37,86 @@ void BluetoothMonitor::registerDBusService()
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothMonitor::isAirPodsDevice(const QString &devicePath)
|
||||
{
|
||||
QDBusInterface deviceInterface("org.bluez", devicePath, "org.freedesktop.DBus.Properties", m_dbus);
|
||||
|
||||
// Get UUIDs to check if it's an AirPods device
|
||||
QDBusReply<QVariant> uuidsReply = deviceInterface.call("Get", "org.bluez.Device1", "UUIDs");
|
||||
if (!uuidsReply.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList uuids = uuidsReply.value().toStringList();
|
||||
return uuids.contains("74ec2172-0bad-4d01-8f77-997b2be0722a");
|
||||
}
|
||||
|
||||
QString BluetoothMonitor::getDeviceName(const QString &devicePath)
|
||||
{
|
||||
QDBusInterface deviceInterface("org.bluez", devicePath, "org.freedesktop.DBus.Properties", m_dbus);
|
||||
QDBusReply<QVariant> nameReply = deviceInterface.call("Get", "org.bluez.Device1", "Name");
|
||||
if (nameReply.isValid())
|
||||
{
|
||||
return nameReply.value().toString();
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
bool BluetoothMonitor::checkAlreadyConnectedDevices()
|
||||
{
|
||||
QDBusInterface objectManager("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", m_dbus);
|
||||
QDBusMessage reply = objectManager.call("GetManagedObjects");
|
||||
|
||||
if (reply.type() == QDBusMessage::ErrorMessage)
|
||||
{
|
||||
LOG_WARN("Failed to get managed objects: " << reply.errorMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant firstArg = reply.arguments().constFirst();
|
||||
QDBusArgument arg = firstArg.value<QDBusArgument>();
|
||||
ManagedObjectList managedObjects;
|
||||
arg >> managedObjects;
|
||||
|
||||
bool deviceFound = false;
|
||||
|
||||
for (auto it = managedObjects.constBegin(); it != managedObjects.constEnd(); ++it)
|
||||
{
|
||||
const QDBusObjectPath &objPath = it.key();
|
||||
const QMap<QString, QVariantMap> &interfaces = it.value();
|
||||
|
||||
if (interfaces.contains("org.bluez.Device1"))
|
||||
{
|
||||
const QVariantMap &deviceProps = interfaces.value("org.bluez.Device1");
|
||||
|
||||
// Check if the device has the necessary properties
|
||||
if (!deviceProps.contains("UUIDs") || !deviceProps.contains("Connected") ||
|
||||
!deviceProps.contains("Address") || !deviceProps.contains("Name"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList uuids = deviceProps["UUIDs"].toStringList();
|
||||
bool isAirPods = uuids.contains("74ec2172-0bad-4d01-8f77-997b2be0722a");
|
||||
|
||||
if (isAirPods)
|
||||
{
|
||||
bool connected = deviceProps["Connected"].toBool();
|
||||
if (connected)
|
||||
{
|
||||
QString macAddress = deviceProps["Address"].toString();
|
||||
QString deviceName = deviceProps["Name"].toString();
|
||||
emit deviceConnected(macAddress, deviceName);
|
||||
LOG_DEBUG("Found already connected AirPods: " << macAddress << " Name: " << deviceName);
|
||||
deviceFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deviceFound;
|
||||
}
|
||||
|
||||
void BluetoothMonitor::onPropertiesChanged(const QString &interface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
|
||||
{
|
||||
Q_UNUSED(invalidatedProps);
|
||||
@@ -56,8 +131,13 @@ void BluetoothMonitor::onPropertiesChanged(const QString &interface, const QVari
|
||||
bool connected = changedProps["Connected"].toBool();
|
||||
QString path = QDBusContext::message().path();
|
||||
|
||||
if (!isAirPodsDevice(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusInterface deviceInterface("org.bluez", path, "org.freedesktop.DBus.Properties", m_dbus);
|
||||
|
||||
|
||||
// Get the device address
|
||||
QDBusReply<QVariant> addrReply = deviceInterface.call("Get", "org.bluez.Device1", "Address");
|
||||
if (!addrReply.isValid())
|
||||
@@ -65,29 +145,17 @@ void BluetoothMonitor::onPropertiesChanged(const QString &interface, const QVari
|
||||
return;
|
||||
}
|
||||
QString macAddress = addrReply.value().toString();
|
||||
|
||||
// Get UUIDs to check if it's an AirPods device
|
||||
QDBusReply<QVariant> uuidsReply = deviceInterface.call("Get", "org.bluez.Device1", "UUIDs");
|
||||
if (!uuidsReply.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList uuids = uuidsReply.value().toStringList();
|
||||
if (!uuids.contains("74ec2172-0bad-4d01-8f77-997b2be0722a"))
|
||||
{
|
||||
return; // Not an AirPods device
|
||||
}
|
||||
QString deviceName = getDeviceName(path);
|
||||
|
||||
if (connected)
|
||||
{
|
||||
emit deviceConnected(macAddress);
|
||||
LOG_DEBUG("AirPods device connected:" << macAddress);
|
||||
emit deviceConnected(macAddress, deviceName);
|
||||
LOG_DEBUG("AirPods device connected:" << macAddress << " Name:" << deviceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit deviceDisconnected(macAddress);
|
||||
LOG_DEBUG("AirPods device disconnected:" << macAddress);
|
||||
emit deviceDisconnected(macAddress, deviceName);
|
||||
LOG_DEBUG("AirPods device disconnected:" << macAddress << " Name:" << deviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@
|
||||
#include <QObject>
|
||||
#include <QtDBus/QtDBus>
|
||||
|
||||
// Forward declarations for D-Bus types
|
||||
typedef QMap<QDBusObjectPath, QMap<QString, QVariantMap>> ManagedObjectList;
|
||||
Q_DECLARE_METATYPE(ManagedObjectList)
|
||||
|
||||
class BluetoothMonitor : public QObject, protected QDBusContext
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -11,9 +15,11 @@ public:
|
||||
explicit BluetoothMonitor(QObject *parent = nullptr);
|
||||
~BluetoothMonitor();
|
||||
|
||||
bool checkAlreadyConnectedDevices();
|
||||
|
||||
signals:
|
||||
void deviceConnected(const QString &macAddress);
|
||||
void deviceDisconnected(const QString &macAddress);
|
||||
void deviceConnected(const QString &macAddress, const QString &deviceName);
|
||||
void deviceDisconnected(const QString &macAddress, const QString &deviceName);
|
||||
|
||||
private slots:
|
||||
void onPropertiesChanged(const QString &interface, const QVariantMap &changedProps, const QStringList &invalidatedProps);
|
||||
@@ -21,6 +27,8 @@ private slots:
|
||||
private:
|
||||
QDBusConnection m_dbus;
|
||||
void registerDBusService();
|
||||
bool isAirPodsDevice(const QString &devicePath);
|
||||
QString getDeviceName(const QString &devicePath);
|
||||
};
|
||||
|
||||
#endif // BLUETOOTHMONITOR_H
|
||||
@@ -104,6 +104,7 @@ ApplicationWindow {
|
||||
model: ["Off", "Noise Cancellation", "Transparency", "Adaptive"]
|
||||
currentIndex: airPodsTrayApp.noiseControlMode
|
||||
onCurrentIndexChanged: airPodsTrayApp.noiseControlMode = currentIndex
|
||||
visible: airPodsTrayApp.airpodsConnected
|
||||
}
|
||||
|
||||
Text {
|
||||
|
||||
219
linux/main.cpp
219
linux/main.cpp
@@ -28,6 +28,7 @@ class AirPodsTrayApp : public QObject {
|
||||
Q_PROPERTY(QString caseIcon READ caseIcon NOTIFY modelChanged)
|
||||
Q_PROPERTY(bool leftPodInEar READ isLeftPodInEar NOTIFY primaryChanged)
|
||||
Q_PROPERTY(bool rightPodInEar READ isRightPodInEar NOTIFY primaryChanged)
|
||||
Q_PROPERTY(bool airpodsConnected READ areAirpodsConnected NOTIFY airPodsStatusChanged)
|
||||
|
||||
public:
|
||||
AirPodsTrayApp(bool debugMode)
|
||||
@@ -64,13 +65,8 @@ public:
|
||||
|
||||
CrossDevice.isEnabled = loadCrossDeviceEnabled();
|
||||
|
||||
discoveryAgent = new QBluetoothDeviceDiscoveryAgent();
|
||||
discoveryAgent->setLowEnergyDiscoveryTimeout(15000);
|
||||
|
||||
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &AirPodsTrayApp::onDeviceDiscovered);
|
||||
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &AirPodsTrayApp::onDiscoveryFinished);
|
||||
discoveryAgent->start();
|
||||
LOG_INFO("AirPodsTrayApp initialized and started device discovery");
|
||||
monitor->checkAlreadyConnectedDevices();
|
||||
LOG_INFO("AirPodsTrayApp initialized");
|
||||
|
||||
QBluetoothLocalDevice localDevice;
|
||||
|
||||
@@ -92,7 +88,6 @@ public:
|
||||
|
||||
delete trayIcon;
|
||||
delete trayMenu;
|
||||
delete discoveryAgent;
|
||||
delete socket;
|
||||
delete phoneSocket;
|
||||
}
|
||||
@@ -122,6 +117,7 @@ public:
|
||||
return m_secoundaryInEar;
|
||||
}
|
||||
}
|
||||
bool areAirpodsConnected() const { return socket && socket->isOpen() && socket->state() == QBluetoothSocket::SocketState::ConnectedState; }
|
||||
|
||||
private:
|
||||
bool debugMode;
|
||||
@@ -140,6 +136,10 @@ private:
|
||||
|
||||
void notifyAndroidDevice()
|
||||
{
|
||||
if (!CrossDevice.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (phoneSocket && phoneSocket->isOpen())
|
||||
{
|
||||
phoneSocket->write(AirPodsPackets::Phone::NOTIFICATION);
|
||||
@@ -163,21 +163,6 @@ public slots:
|
||||
connectToDevice(device);
|
||||
}
|
||||
|
||||
void showAvailableDevices() {
|
||||
LOG_INFO("Showing available devices");
|
||||
QStringList devices;
|
||||
const QList<QBluetoothDeviceInfo> discoveredDevices = discoveryAgent->discoveredDevices();
|
||||
for (const QBluetoothDeviceInfo &device : discoveredDevices) {
|
||||
devices << device.address().toString() + " - " + device.name();
|
||||
}
|
||||
bool ok;
|
||||
QString selectedDevice = QInputDialog::getItem(nullptr, "Select Device", "Devices:", devices, 0, false, &ok);
|
||||
if (ok && !selectedDevice.isEmpty()) {
|
||||
QString address = selectedDevice.split(" - ").first();
|
||||
connectToDevice(address);
|
||||
}
|
||||
}
|
||||
|
||||
void setNoiseControlMode(NoiseControlMode mode)
|
||||
{
|
||||
LOG_INFO("Setting noise control mode to: " << mode);
|
||||
@@ -308,46 +293,12 @@ private slots:
|
||||
|
||||
void sendHandshake() {
|
||||
LOG_INFO("Connected to device, sending initial packets");
|
||||
discoveryAgent->stop();
|
||||
writePacketToSocket(AirPodsPackets::Connection::HANDSHAKE, "Handshake packet written: ");
|
||||
}
|
||||
|
||||
void onDeviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
QByteArray manufacturerData = device.manufacturerData(MANUFACTURER_ID);
|
||||
if (manufacturerData.startsWith(MANUFACTURER_DATA)) {
|
||||
LOG_INFO("Detected AirPods via BLE manufacturer data");
|
||||
connectToDevice(device.address().toString());
|
||||
}
|
||||
LOG_INFO("Device discovered: " << device.name() << " (" << device.address().toString() << ")");
|
||||
if (isAirPodsDevice(device)) {
|
||||
LOG_DEBUG("Found AirPods device: " + device.name());
|
||||
connectToDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
void onDiscoveryFinished() {
|
||||
LOG_INFO("Device discovery finished");
|
||||
discoveryAgent->start();
|
||||
const QList<QBluetoothDeviceInfo> discoveredDevices = discoveryAgent->discoveredDevices();
|
||||
for (const QBluetoothDeviceInfo &device : discoveredDevices) {
|
||||
if (isAirPodsDevice(device)) {
|
||||
connectToDevice(device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG_WARN("No device with the specified UUID found");
|
||||
}
|
||||
|
||||
void onDeviceConnected(const QBluetoothAddress &address) {
|
||||
LOG_INFO("Device connected: " << address.toString());
|
||||
QBluetoothDeviceInfo device(address, "", 0);
|
||||
if (isAirPodsDevice(device)) {
|
||||
connectToDevice(device);
|
||||
}
|
||||
}
|
||||
void bluezDeviceConnected(const QString &address)
|
||||
void bluezDeviceConnected(const QString &address, const QString &name)
|
||||
{
|
||||
QBluetoothDeviceInfo device(QBluetoothAddress(address), "", 0);
|
||||
QBluetoothDeviceInfo device(QBluetoothAddress(address), name, 0);
|
||||
connectToDevice(device);
|
||||
}
|
||||
|
||||
@@ -366,42 +317,43 @@ private slots:
|
||||
LOG_DEBUG("AIRPODS_DISCONNECTED packet written: " << AirPodsPackets::Connection::AIRPODS_DISCONNECTED.toHex());
|
||||
}
|
||||
|
||||
// Clear the device name and model
|
||||
m_deviceName.clear();
|
||||
connectedDeviceMacAddress.clear();
|
||||
m_model = AirPodsModel::Unknown;
|
||||
emit deviceNameChanged(m_deviceName);
|
||||
emit modelChanged();
|
||||
|
||||
// Reset battery status
|
||||
m_battery->reset();
|
||||
m_batteryStatus.clear();
|
||||
emit batteryStatusChanged(m_batteryStatus);
|
||||
|
||||
// Reset ear detection
|
||||
m_earDetectionStatus.clear();
|
||||
m_primaryInEar = false;
|
||||
m_secoundaryInEar = false;
|
||||
emit earDetectionStatusChanged(m_earDetectionStatus);
|
||||
emit primaryChanged();
|
||||
|
||||
// Reset noise control mode
|
||||
m_noiseControlMode = NoiseControlMode::Off;
|
||||
emit noiseControlModeChanged(m_noiseControlMode);
|
||||
|
||||
mediaController->pause(); // Since the device is deconnected, we don't know if it was the active output device. Pause to be safe
|
||||
discoveryAgent->start();
|
||||
emit airPodsStatusChanged();
|
||||
|
||||
// Show system notification
|
||||
trayManager->showNotification(
|
||||
tr("AirPods Disconnected"),
|
||||
tr("Your AirPods have been disconnected"));
|
||||
}
|
||||
void bluezDeviceDisconnected(const QString &address)
|
||||
|
||||
void bluezDeviceDisconnected(const QString &address, const QString &name)
|
||||
{
|
||||
if (address == connectedDeviceMacAddress.replace("_", ":"))
|
||||
{
|
||||
onDeviceDisconnected(QBluetoothAddress(address));
|
||||
|
||||
// Clear the device name and model
|
||||
m_deviceName.clear();
|
||||
m_model = AirPodsModel::Unknown;
|
||||
emit deviceNameChanged(m_deviceName);
|
||||
emit modelChanged();
|
||||
|
||||
// Reset battery status
|
||||
m_battery->reset();
|
||||
m_batteryStatus.clear();
|
||||
emit batteryStatusChanged(m_batteryStatus);
|
||||
|
||||
// Reset ear detection
|
||||
m_earDetectionStatus.clear();
|
||||
m_primaryInEar = false;
|
||||
m_secoundaryInEar = false;
|
||||
emit earDetectionStatusChanged(m_earDetectionStatus);
|
||||
emit primaryChanged();
|
||||
|
||||
// Reset noise control mode
|
||||
m_noiseControlMode = NoiseControlMode::Off;
|
||||
emit noiseControlModeChanged(m_noiseControlMode);
|
||||
}
|
||||
onDeviceDisconnected(QBluetoothAddress(address)); }
|
||||
else {
|
||||
LOG_WARN("Disconnected device does not match connected device: " << address << " != " << connectedDeviceMacAddress);
|
||||
}
|
||||
@@ -484,53 +436,74 @@ private slots:
|
||||
LOG_INFO("Trailing Byte: " << trailingByte);
|
||||
}
|
||||
|
||||
void connectToDevice(const QBluetoothDeviceInfo &device) {
|
||||
if (socket && socket->isOpen() && socket->peerAddress() == device.address()) {
|
||||
QString getEarStatus(char value)
|
||||
{
|
||||
return (value == 0x00) ? "In Ear" : (value == 0x01) ? "Out of Ear"
|
||||
: "In case";
|
||||
}
|
||||
|
||||
void connectToDevice(const QBluetoothDeviceInfo &device)
|
||||
{
|
||||
if (socket && socket->isOpen() && socket->peerAddress() == device.address())
|
||||
{
|
||||
LOG_INFO("Already connected to the device: " << device.name());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Connecting to device: " << device.name());
|
||||
|
||||
// Clean up any existing socket
|
||||
if (socket)
|
||||
{
|
||||
socket->close();
|
||||
socket->deleteLater();
|
||||
socket = nullptr;
|
||||
}
|
||||
|
||||
QBluetoothSocket *localSocket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol);
|
||||
connect(localSocket, &QBluetoothSocket::connected, this, [this, localSocket]() {
|
||||
// Start periodic magic pairing attempts
|
||||
QTimer *magicPairingTimer = new QTimer(this);
|
||||
connect(magicPairingTimer, &QTimer::timeout, this, [this, magicPairingTimer]() {
|
||||
if (m_magicAccIRK.isEmpty() || m_magicAccEncKey.isEmpty()) {
|
||||
initiateMagicPairing();
|
||||
} else {
|
||||
magicPairingTimer->stop();
|
||||
magicPairingTimer->deleteLater();
|
||||
}
|
||||
});
|
||||
magicPairingTimer->start(500);
|
||||
|
||||
connect(localSocket, &QBluetoothSocket::readyRead, this, [this, localSocket]() {
|
||||
QByteArray data = localSocket->readAll();
|
||||
QMetaObject::invokeMethod(this, "parseData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
|
||||
QMetaObject::invokeMethod(this, "relayPacketToPhone", Qt::QueuedConnection, Q_ARG(QByteArray, data));
|
||||
});
|
||||
|
||||
sendHandshake();
|
||||
});
|
||||
|
||||
connect(localSocket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::errorOccurred), this, [this, localSocket](QBluetoothSocket::SocketError error) {
|
||||
LOG_ERROR("Socket error: " << error << ", " << localSocket->errorString());
|
||||
});
|
||||
|
||||
socket = localSocket;
|
||||
|
||||
// Connection handler
|
||||
auto handleConnection = [this, localSocket]()
|
||||
{
|
||||
connect(localSocket, &QBluetoothSocket::readyRead, this, [this, localSocket]()
|
||||
{
|
||||
QByteArray data = localSocket->readAll();
|
||||
QMetaObject::invokeMethod(this, "parseData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
|
||||
QMetaObject::invokeMethod(this, "relayPacketToPhone", Qt::QueuedConnection, Q_ARG(QByteArray, data)); });
|
||||
sendHandshake();
|
||||
};
|
||||
|
||||
// Error handler with retry
|
||||
auto handleError = [this, device, localSocket](QBluetoothSocket::SocketError error)
|
||||
{
|
||||
LOG_ERROR("Socket error: " << error << ", " << localSocket->errorString());
|
||||
|
||||
static int retryCount = 0;
|
||||
if (retryCount < 3)
|
||||
{
|
||||
retryCount++;
|
||||
LOG_INFO("Retrying connection (attempt " << retryCount << ")");
|
||||
QTimer::singleShot(1500, this, [this, device]()
|
||||
{ connectToDevice(device); });
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to connect after 3 attempts");
|
||||
retryCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
connect(localSocket, &QBluetoothSocket::connected, this, handleConnection);
|
||||
connect(localSocket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::errorOccurred),
|
||||
this, handleError);
|
||||
|
||||
localSocket->connectToService(device.address(), QBluetoothUuid("74ec2172-0bad-4d01-8f77-997b2be0722a"));
|
||||
connectedDeviceMacAddress = device.address().toString().replace(":", "_");
|
||||
mediaController->setConnectedDeviceMacAddress(connectedDeviceMacAddress);
|
||||
notifyAndroidDevice();
|
||||
}
|
||||
|
||||
QString getEarStatus(char value)
|
||||
{
|
||||
return (value == 0x00) ? "In Ear" : (value == 0x01) ? "Out of Ear"
|
||||
: "In case";
|
||||
}
|
||||
|
||||
void parseData(const QByteArray &data)
|
||||
{
|
||||
LOG_DEBUG("Received: " << data.toHex());
|
||||
@@ -539,7 +512,7 @@ private slots:
|
||||
{
|
||||
writePacketToSocket(AirPodsPackets::Connection::SET_SPECIFIC_FEATURES, "Set specific features packet written: ");
|
||||
}
|
||||
if (data.startsWith(AirPodsPackets::Parse::FEATURES_ACK))
|
||||
else if (data.startsWith(AirPodsPackets::Parse::FEATURES_ACK))
|
||||
{
|
||||
writePacketToSocket(AirPodsPackets::Connection::REQUEST_NOTIFICATIONS, "Request notifications packet written: ");
|
||||
|
||||
@@ -626,6 +599,8 @@ private slots:
|
||||
else if (data.startsWith(AirPodsPackets::Parse::METADATA))
|
||||
{
|
||||
parseMetadata(data);
|
||||
initiateMagicPairing();
|
||||
emit airPodsStatusChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -754,6 +729,8 @@ private slots:
|
||||
|
||||
void sendDisconnectRequestToAndroid()
|
||||
{
|
||||
if (!CrossDevice.isEnabled) return;
|
||||
|
||||
if (phoneSocket && phoneSocket->isOpen())
|
||||
{
|
||||
phoneSocket->write(AirPodsPackets::Phone::DISCONNECT_REQUEST);
|
||||
@@ -841,11 +818,11 @@ signals:
|
||||
void deviceNameChanged(const QString &name);
|
||||
void modelChanged();
|
||||
void primaryChanged();
|
||||
void airPodsStatusChanged();
|
||||
|
||||
private:
|
||||
QSystemTrayIcon *trayIcon;
|
||||
QMenu *trayMenu;
|
||||
QBluetoothDeviceDiscoveryAgent *discoveryAgent;
|
||||
QBluetoothSocket *socket = nullptr;
|
||||
QBluetoothSocket *phoneSocket = nullptr;
|
||||
QString connectedDeviceMacAddress;
|
||||
|
||||
Reference in New Issue
Block a user