mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-01-28 22:01:50 +00:00
[Linux] Move all device related properties to new class (#135)
* Clean up code * Move all device releated properties to new class
This commit is contained in:
@@ -18,22 +18,12 @@ namespace ControlCommand
|
||||
return packet;
|
||||
}
|
||||
|
||||
// Parse activated/not activated
|
||||
inline std::optional<bool> parseActive(const QByteArray &data)
|
||||
inline std::optional<char> parseActive(const QByteArray &data)
|
||||
{
|
||||
if (!data.startsWith(ControlCommand::HEADER))
|
||||
return std::nullopt;
|
||||
|
||||
quint8 statusByte = static_cast<quint8>(data.at(7));
|
||||
switch (statusByte)
|
||||
{
|
||||
case 0x01: // Enabled
|
||||
return true;
|
||||
case 0x02: // Disabled
|
||||
return false;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<quint8>(data.at(7));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +44,19 @@ struct BasicControlCommand
|
||||
|
||||
// Basically returns the byte at the index 7
|
||||
static std::optional<bool> parseState(const QByteArray &data)
|
||||
{
|
||||
switch (ControlCommand::parseActive(data).value_or(0x00))
|
||||
{
|
||||
case 0x01: // Enabled
|
||||
return true;
|
||||
case 0x02: // Disabled
|
||||
return false;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<char> getValue(const QByteArray &data)
|
||||
{
|
||||
return ControlCommand::parseActive(data);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ qt_add_executable(applinux
|
||||
BluetoothMonitor.h
|
||||
autostartmanager.hpp
|
||||
BasicControlCommand.hpp
|
||||
deviceinfo.hpp
|
||||
)
|
||||
|
||||
qt_add_qml_module(applinux
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace AirPodsPackets
|
||||
// Noise Control Mode Packets
|
||||
namespace NoiseControl
|
||||
{
|
||||
using NoiseControlMode = AirpodsTrayApp::Enums::NoiseControlMode;
|
||||
static const QByteArray HEADER = ControlCommand::HEADER + 0x0D;
|
||||
static const QByteArray OFF = ControlCommand::createCommand(0x0D, 0x01);
|
||||
static const QByteArray NOISE_CANCELLATION = ControlCommand::createCommand(0x0D, 0x02);
|
||||
@@ -21,7 +22,6 @@ namespace AirPodsPackets
|
||||
|
||||
static const QByteArray getPacketForMode(AirpodsTrayApp::Enums::NoiseControlMode mode)
|
||||
{
|
||||
using NoiseControlMode = AirpodsTrayApp::Enums::NoiseControlMode;
|
||||
switch (mode)
|
||||
{
|
||||
case NoiseControlMode::Off:
|
||||
@@ -36,6 +36,17 @@ namespace AirPodsPackets
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
inline std::optional<NoiseControlMode> parseMode(const QByteArray &data)
|
||||
{
|
||||
char mode = ControlCommand::parseActive(data).value_or(CHAR_MAX);
|
||||
if (mode < static_cast<quint8>(NoiseControlMode::MinValue) ||
|
||||
mode > static_cast<quint8>(NoiseControlMode::MaxValue))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<NoiseControlMode>(mode - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// One Bud ANC Mode
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
209
linux/deviceinfo.hpp
Normal file
209
linux/deviceinfo.hpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include "battery.hpp"
|
||||
#include "enums.h"
|
||||
|
||||
using namespace AirpodsTrayApp::Enums;
|
||||
|
||||
class DeviceInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString batteryStatus READ batteryStatus WRITE setBatteryStatus NOTIFY batteryStatusChanged)
|
||||
Q_PROPERTY(QString earDetectionStatus READ earDetectionStatus WRITE setEarDetectionStatus NOTIFY earDetectionStatusChanged)
|
||||
Q_PROPERTY(int noiseControlMode READ noiseControlModeInt WRITE setNoiseControlModeInt NOTIFY noiseControlModeChangedInt)
|
||||
Q_PROPERTY(bool conversationalAwareness READ conversationalAwareness WRITE setConversationalAwareness NOTIFY conversationalAwarenessChanged)
|
||||
Q_PROPERTY(int adaptiveNoiseLevel READ adaptiveNoiseLevel WRITE setAdaptiveNoiseLevel NOTIFY adaptiveNoiseLevelChanged)
|
||||
Q_PROPERTY(QString deviceName READ deviceName WRITE setDeviceName NOTIFY deviceNameChanged)
|
||||
Q_PROPERTY(Battery *battery READ getBattery CONSTANT)
|
||||
Q_PROPERTY(bool primaryInEar READ isPrimaryInEar WRITE setPrimaryInEar NOTIFY primaryChanged)
|
||||
Q_PROPERTY(bool secondaryInEar READ isSecondaryInEar WRITE setSecondaryInEar NOTIFY primaryChanged)
|
||||
Q_PROPERTY(bool oneBudANCMode READ oneBudANCMode WRITE setOneBudANCMode NOTIFY oneBudANCModeChanged)
|
||||
Q_PROPERTY(AirPodsModel model READ model WRITE setModel NOTIFY modelChanged)
|
||||
Q_PROPERTY(bool adaptiveModeActive READ adaptiveModeActive NOTIFY noiseControlModeChangedInt)
|
||||
Q_PROPERTY(QString podIcon READ podIcon NOTIFY modelChanged)
|
||||
Q_PROPERTY(QString caseIcon READ caseIcon NOTIFY modelChanged)
|
||||
Q_PROPERTY(bool leftPodInEar READ isLeftPodInEar NOTIFY primaryChanged)
|
||||
Q_PROPERTY(bool rightPodInEar READ isRightPodInEar NOTIFY primaryChanged)
|
||||
|
||||
public:
|
||||
explicit DeviceInfo(QObject *parent = nullptr) : QObject(parent), m_battery(new Battery(this)) {}
|
||||
|
||||
QString batteryStatus() const { return m_batteryStatus; }
|
||||
void setBatteryStatus(const QString &status)
|
||||
{
|
||||
if (m_batteryStatus != status)
|
||||
{
|
||||
m_batteryStatus = status;
|
||||
emit batteryStatusChanged(status);
|
||||
}
|
||||
}
|
||||
|
||||
QString earDetectionStatus() const { return m_earDetectionStatus; }
|
||||
void setEarDetectionStatus(const QString &status)
|
||||
{
|
||||
if (m_earDetectionStatus != status)
|
||||
{
|
||||
m_earDetectionStatus = status;
|
||||
emit earDetectionStatusChanged(status);
|
||||
}
|
||||
}
|
||||
|
||||
NoiseControlMode noiseControlMode() const { return m_noiseControlMode; }
|
||||
void setNoiseControlMode(NoiseControlMode mode)
|
||||
{
|
||||
if (m_noiseControlMode != mode)
|
||||
{
|
||||
m_noiseControlMode = mode;
|
||||
emit noiseControlModeChanged(mode);
|
||||
emit noiseControlModeChangedInt(static_cast<int>(mode));
|
||||
}
|
||||
}
|
||||
int noiseControlModeInt() const { return static_cast<int>(noiseControlMode()); }
|
||||
void setNoiseControlModeInt(int mode) { setNoiseControlMode(static_cast<NoiseControlMode>(mode)); }
|
||||
|
||||
bool conversationalAwareness() const { return m_conversationalAwareness; }
|
||||
void setConversationalAwareness(bool enabled)
|
||||
{
|
||||
if (m_conversationalAwareness != enabled)
|
||||
{
|
||||
m_conversationalAwareness = enabled;
|
||||
emit conversationalAwarenessChanged(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
int adaptiveNoiseLevel() const { return m_adaptiveNoiseLevel; }
|
||||
void setAdaptiveNoiseLevel(int level)
|
||||
{
|
||||
if (m_adaptiveNoiseLevel != level)
|
||||
{
|
||||
m_adaptiveNoiseLevel = level;
|
||||
emit adaptiveNoiseLevelChanged(level);
|
||||
}
|
||||
}
|
||||
|
||||
QString deviceName() const { return m_deviceName; }
|
||||
void setDeviceName(const QString &name)
|
||||
{
|
||||
if (m_deviceName != name)
|
||||
{
|
||||
m_deviceName = name;
|
||||
emit deviceNameChanged(name);
|
||||
}
|
||||
}
|
||||
|
||||
Battery *getBattery() const { return m_battery; }
|
||||
|
||||
bool isPrimaryInEar() const { return m_primaryInEar; }
|
||||
void setPrimaryInEar(bool inEar)
|
||||
{
|
||||
if (m_primaryInEar != inEar)
|
||||
{
|
||||
m_primaryInEar = inEar;
|
||||
emit primaryChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool isSecondaryInEar() const { return m_secoundaryInEar; }
|
||||
void setSecondaryInEar(bool inEar)
|
||||
{
|
||||
if (m_secoundaryInEar != inEar)
|
||||
{
|
||||
m_secoundaryInEar = inEar;
|
||||
emit primaryChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool oneBudANCMode() const { return m_oneBudANCMode; }
|
||||
void setOneBudANCMode(bool enabled)
|
||||
{
|
||||
if (m_oneBudANCMode != enabled)
|
||||
{
|
||||
m_oneBudANCMode = enabled;
|
||||
emit oneBudANCModeChanged(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
AirPodsModel model() const { return m_model; }
|
||||
void setModel(AirPodsModel model)
|
||||
{
|
||||
if (m_model != model)
|
||||
{
|
||||
m_model = model;
|
||||
emit modelChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray magicAccIRK() const { return m_magicAccIRK; }
|
||||
void setMagicAccIRK(const QByteArray &irk) { m_magicAccIRK = irk; }
|
||||
|
||||
QByteArray magicAccEncKey() const { return m_magicAccEncKey; }
|
||||
void setMagicAccEncKey(const QByteArray &key) { m_magicAccEncKey = key; }
|
||||
|
||||
QString modelNumber() const { return m_modelNumber; }
|
||||
void setModelNumber(const QString &modelNumber) { m_modelNumber = modelNumber; }
|
||||
|
||||
QString manufacturer() const { return m_manufacturer; }
|
||||
void setManufacturer(const QString &manufacturer) { m_manufacturer = manufacturer; }
|
||||
|
||||
QString podIcon() const { return getModelIcon(model()).first; }
|
||||
QString caseIcon() const { return getModelIcon(model()).second; }
|
||||
bool isLeftPodInEar() const
|
||||
{
|
||||
if (getBattery()->getPrimaryPod() == Battery::Component::Left) return isPrimaryInEar();
|
||||
else return isSecondaryInEar();
|
||||
}
|
||||
bool isRightPodInEar() const
|
||||
{
|
||||
if (getBattery()->getPrimaryPod() == Battery::Component::Right) return isPrimaryInEar();
|
||||
else return isSecondaryInEar();
|
||||
}
|
||||
|
||||
bool adaptiveModeActive() const { return noiseControlMode() == NoiseControlMode::Adaptive; }
|
||||
bool oneOrMorePodsInCase() const { return earDetectionStatus().contains("In case"); }
|
||||
bool oneOrMorePodsInEar() const { return isPrimaryInEar() || isSecondaryInEar(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
setDeviceName("");
|
||||
setModel(AirPodsModel::Unknown);
|
||||
m_battery->reset();
|
||||
setBatteryStatus("");
|
||||
setEarDetectionStatus("");
|
||||
setPrimaryInEar(false);
|
||||
setSecondaryInEar(false);
|
||||
setNoiseControlMode(NoiseControlMode::Off);
|
||||
}
|
||||
|
||||
signals:
|
||||
void batteryStatusChanged(const QString &status);
|
||||
void earDetectionStatusChanged(const QString &status);
|
||||
void noiseControlModeChanged(NoiseControlMode mode);
|
||||
void noiseControlModeChangedInt(int mode);
|
||||
void conversationalAwarenessChanged(bool enabled);
|
||||
void adaptiveNoiseLevelChanged(int level);
|
||||
void deviceNameChanged(const QString &name);
|
||||
void primaryChanged();
|
||||
void oneBudANCModeChanged(bool enabled);
|
||||
void modelChanged();
|
||||
|
||||
private:
|
||||
QString m_batteryStatus;
|
||||
QString m_earDetectionStatus;
|
||||
NoiseControlMode m_noiseControlMode = NoiseControlMode::Off;
|
||||
bool m_conversationalAwareness = false;
|
||||
int m_adaptiveNoiseLevel = 50;
|
||||
QString m_deviceName;
|
||||
Battery *m_battery;
|
||||
bool m_primaryInEar = false;
|
||||
bool m_secoundaryInEar = false;
|
||||
QByteArray m_magicAccIRK;
|
||||
QByteArray m_magicAccEncKey;
|
||||
bool m_oneBudANCMode = false;
|
||||
AirPodsModel m_model = AirPodsModel::Unknown;
|
||||
|
||||
// Additional metadata fields
|
||||
QString m_modelNumber;
|
||||
QString m_manufacturer;
|
||||
};
|
||||
261
linux/main.cpp
261
linux/main.cpp
@@ -10,6 +10,7 @@
|
||||
#include "battery.hpp"
|
||||
#include "BluetoothMonitor.h"
|
||||
#include "autostartmanager.hpp"
|
||||
#include "deviceinfo.hpp"
|
||||
|
||||
using namespace AirpodsTrayApp::Enums;
|
||||
|
||||
@@ -17,19 +18,6 @@ Q_LOGGING_CATEGORY(airpodsApp, "airpodsApp")
|
||||
|
||||
class AirPodsTrayApp : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString batteryStatus READ batteryStatus NOTIFY batteryStatusChanged)
|
||||
Q_PROPERTY(QString earDetectionStatus READ earDetectionStatus NOTIFY earDetectionStatusChanged)
|
||||
Q_PROPERTY(int noiseControlMode READ noiseControlMode WRITE setNoiseControlMode NOTIFY noiseControlModeChanged)
|
||||
Q_PROPERTY(bool conversationalAwareness READ conversationalAwareness WRITE setConversationalAwareness NOTIFY conversationalAwarenessChanged)
|
||||
Q_PROPERTY(int adaptiveNoiseLevel READ adaptiveNoiseLevel WRITE setAdaptiveNoiseLevel NOTIFY adaptiveNoiseLevelChanged)
|
||||
Q_PROPERTY(bool adaptiveModeActive READ adaptiveModeActive NOTIFY noiseControlModeChanged)
|
||||
Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
|
||||
Q_PROPERTY(Battery* battery READ getBattery NOTIFY batteryStatusChanged)
|
||||
Q_PROPERTY(bool oneOrMorePodsInCase READ oneOrMorePodsInCase NOTIFY earDetectionStatusChanged)
|
||||
Q_PROPERTY(QString podIcon READ podIcon NOTIFY modelChanged)
|
||||
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)
|
||||
Q_PROPERTY(int earDetectionBehavior READ earDetectionBehavior WRITE setEarDetectionBehavior NOTIFY earDetectionBehaviorChanged)
|
||||
Q_PROPERTY(bool crossDeviceEnabled READ crossDeviceEnabled WRITE setCrossDeviceEnabled NOTIFY crossDeviceEnabledChanged)
|
||||
@@ -37,24 +25,13 @@ class AirPodsTrayApp : public QObject {
|
||||
Q_PROPERTY(bool notificationsEnabled READ notificationsEnabled WRITE setNotificationsEnabled NOTIFY notificationsEnabledChanged)
|
||||
Q_PROPERTY(int retryAttempts READ retryAttempts WRITE setRetryAttempts NOTIFY retryAttemptsChanged)
|
||||
Q_PROPERTY(bool hideOnStart READ hideOnStart CONSTANT)
|
||||
Q_PROPERTY(bool oneBudANCMode READ oneBudANCMode WRITE setOneBudANCMode NOTIFY oneBudANCModeChanged)
|
||||
Q_PROPERTY(DeviceInfo *deviceInfo READ deviceInfo CONSTANT)
|
||||
|
||||
public:
|
||||
AirPodsTrayApp(bool debugMode, bool hideOnStart, QQmlApplicationEngine *parent = nullptr)
|
||||
: QObject(parent)
|
||||
, debugMode(debugMode)
|
||||
, m_battery(new Battery(this))
|
||||
, monitor(new BluetoothMonitor(this))
|
||||
, m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp"))
|
||||
, m_autoStartManager(new AutoStartManager(this))
|
||||
, m_hideOnStart(hideOnStart)
|
||||
, parent(parent)
|
||||
: QObject(parent), debugMode(debugMode), m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp")), m_autoStartManager(new AutoStartManager(this)), m_hideOnStart(hideOnStart), parent(parent), m_deviceInfo(new DeviceInfo(this))
|
||||
{
|
||||
if (debugMode) {
|
||||
QLoggingCategory::setFilterRules("airpodsApp.debug=true");
|
||||
} else {
|
||||
QLoggingCategory::setFilterRules("airpodsApp.debug=false");
|
||||
}
|
||||
QLoggingCategory::setFilterRules(QString("airpodsApp.debug=%1").arg(debugMode ? "true" : "false"));
|
||||
LOG_INFO("Initializing AirPodsTrayApp");
|
||||
|
||||
// Initialize tray icon and connect signals
|
||||
@@ -63,25 +40,26 @@ public:
|
||||
connect(trayManager, &TrayIconManager::trayClicked, this, &AirPodsTrayApp::onTrayIconActivated);
|
||||
connect(trayManager, &TrayIconManager::openApp, this, &AirPodsTrayApp::onOpenApp);
|
||||
connect(trayManager, &TrayIconManager::openSettings, this, &AirPodsTrayApp::onOpenSettings);
|
||||
connect(trayManager, &TrayIconManager::noiseControlChanged, this, qOverload<NoiseControlMode>(&AirPodsTrayApp::setNoiseControlMode));
|
||||
connect(trayManager, &TrayIconManager::noiseControlChanged, this, &AirPodsTrayApp::setNoiseControlMode);
|
||||
connect(trayManager, &TrayIconManager::conversationalAwarenessToggled, this, &AirPodsTrayApp::setConversationalAwareness);
|
||||
connect(this, &AirPodsTrayApp::batteryStatusChanged, trayManager, &TrayIconManager::updateBatteryStatus);
|
||||
connect(this, &AirPodsTrayApp::noiseControlModeChanged, trayManager, &TrayIconManager::updateNoiseControlState);
|
||||
connect(this, &AirPodsTrayApp::conversationalAwarenessChanged, trayManager, &TrayIconManager::updateConversationalAwareness);
|
||||
connect(m_deviceInfo, &DeviceInfo::batteryStatusChanged, trayManager, &TrayIconManager::updateBatteryStatus);
|
||||
connect(m_deviceInfo, &DeviceInfo::noiseControlModeChanged, trayManager, &TrayIconManager::updateNoiseControlState);
|
||||
connect(m_deviceInfo, &DeviceInfo::conversationalAwarenessChanged, trayManager, &TrayIconManager::updateConversationalAwareness);
|
||||
connect(trayManager, &TrayIconManager::notificationsEnabledChanged, this, &AirPodsTrayApp::saveNotificationsEnabled);
|
||||
connect(trayManager, &TrayIconManager::notificationsEnabledChanged, this, &AirPodsTrayApp::notificationsEnabledChanged);
|
||||
|
||||
// Initialize MediaController and connect signals
|
||||
mediaController = new MediaController(this);
|
||||
connect(this, &AirPodsTrayApp::earDetectionStatusChanged, mediaController, &MediaController::handleEarDetection);
|
||||
connect(m_deviceInfo, &DeviceInfo::earDetectionStatusChanged, mediaController, &MediaController::handleEarDetection);
|
||||
connect(mediaController, &MediaController::mediaStateChanged, this, &AirPodsTrayApp::handleMediaStateChange);
|
||||
mediaController->initializeMprisInterface();
|
||||
mediaController->followMediaChanges();
|
||||
|
||||
monitor = new BluetoothMonitor(this);
|
||||
connect(monitor, &BluetoothMonitor::deviceConnected, this, &AirPodsTrayApp::bluezDeviceConnected);
|
||||
connect(monitor, &BluetoothMonitor::deviceDisconnected, this, &AirPodsTrayApp::bluezDeviceDisconnected);
|
||||
|
||||
connect(m_battery, &Battery::primaryChanged, this, &AirPodsTrayApp::primaryChanged);
|
||||
connect(m_deviceInfo->getBattery(), &Battery::primaryChanged, this, &AirPodsTrayApp::primaryChanged);
|
||||
|
||||
// Load settings
|
||||
CrossDevice.isEnabled = loadCrossDeviceEnabled();
|
||||
@@ -114,31 +92,6 @@ public:
|
||||
delete phoneSocket;
|
||||
}
|
||||
|
||||
QString batteryStatus() const { return m_batteryStatus; }
|
||||
QString earDetectionStatus() const { return m_earDetectionStatus; }
|
||||
int noiseControlMode() const { return static_cast<int>(m_noiseControlMode); }
|
||||
bool conversationalAwareness() const { return m_conversationalAwareness; }
|
||||
bool adaptiveModeActive() const { return m_noiseControlMode == NoiseControlMode::Adaptive; }
|
||||
int adaptiveNoiseLevel() const { return m_adaptiveNoiseLevel; }
|
||||
QString deviceName() const { return m_deviceName; }
|
||||
Battery *getBattery() const { return m_battery; }
|
||||
bool oneOrMorePodsInCase() const { return m_earDetectionStatus.contains("In case"); }
|
||||
QString podIcon() const { return getModelIcon(m_model).first; }
|
||||
QString caseIcon() const { return getModelIcon(m_model).second; }
|
||||
bool isLeftPodInEar() const {
|
||||
if (m_battery->getPrimaryPod() == Battery::Component::Left) {
|
||||
return m_primaryInEar;
|
||||
} else {
|
||||
return m_secoundaryInEar;
|
||||
}
|
||||
}
|
||||
bool isRightPodInEar() const {
|
||||
if (m_battery->getPrimaryPod() == Battery::Component::Right) {
|
||||
return m_primaryInEar;
|
||||
} else {
|
||||
return m_secoundaryInEar;
|
||||
}
|
||||
}
|
||||
bool areAirpodsConnected() const { return socket && socket->isOpen() && socket->state() == QBluetoothSocket::SocketState::ConnectedState; }
|
||||
int earDetectionBehavior() const { return mediaController->getEarDetectionBehavior(); }
|
||||
bool crossDeviceEnabled() const { return CrossDevice.isEnabled; }
|
||||
@@ -147,7 +100,7 @@ public:
|
||||
void setNotificationsEnabled(bool enabled) { trayManager->setNotificationsEnabled(enabled); }
|
||||
int retryAttempts() const { return m_retryAttempts; }
|
||||
bool hideOnStart() const { return m_hideOnStart; }
|
||||
bool oneBudANCMode() const { return m_oneBudANCMode; }
|
||||
DeviceInfo *deviceInfo() const { return m_deviceInfo; }
|
||||
|
||||
private:
|
||||
bool debugMode;
|
||||
@@ -199,39 +152,32 @@ public slots:
|
||||
void setNoiseControlMode(NoiseControlMode mode)
|
||||
{
|
||||
LOG_INFO("Setting noise control mode to: " << mode);
|
||||
if (m_noiseControlMode == mode)
|
||||
{
|
||||
LOG_INFO("Noise control mode is already " << mode);
|
||||
return;
|
||||
}
|
||||
QByteArray packet = AirPodsPackets::NoiseControl::getPacketForMode(mode);
|
||||
writePacketToSocket(packet, "Noise control mode packet written: ");
|
||||
}
|
||||
void setNoiseControlMode(int mode)
|
||||
void setNoiseControlModeInt(int mode)
|
||||
{
|
||||
if (mode < 0 || mode > static_cast<int>(NoiseControlMode::Adaptive))
|
||||
{
|
||||
LOG_ERROR("Invalid noise control mode: " << mode);
|
||||
return;
|
||||
}
|
||||
setNoiseControlMode(static_cast<NoiseControlMode>(mode));
|
||||
}
|
||||
|
||||
void setConversationalAwareness(bool enabled)
|
||||
{
|
||||
if (m_conversationalAwareness == enabled)
|
||||
{
|
||||
LOG_INFO("Conversational awareness is already " << (enabled ? "enabled" : "disabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Setting conversational awareness to: " << (enabled ? "enabled" : "disabled"));
|
||||
QByteArray packet = enabled ? AirPodsPackets::ConversationalAwareness::ENABLED
|
||||
: AirPodsPackets::ConversationalAwareness::DISABLED;
|
||||
|
||||
writePacketToSocket(packet, "Conversational awareness packet written: ");
|
||||
m_conversationalAwareness = enabled;
|
||||
emit conversationalAwarenessChanged(enabled);
|
||||
m_deviceInfo->setConversationalAwareness(enabled);
|
||||
}
|
||||
|
||||
void setOneBudANCMode(bool enabled)
|
||||
{
|
||||
if (m_oneBudANCMode == enabled)
|
||||
if (m_deviceInfo->oneBudANCMode() == enabled)
|
||||
{
|
||||
LOG_INFO("One Bud ANC mode is already " << (enabled ? "enabled" : "disabled"));
|
||||
return;
|
||||
@@ -243,8 +189,7 @@ public slots:
|
||||
|
||||
if (writePacketToSocket(packet, "One Bud ANC mode packet written: "))
|
||||
{
|
||||
m_oneBudANCMode = enabled;
|
||||
emit oneBudANCModeChanged(enabled);
|
||||
m_deviceInfo->setOneBudANCMode(enabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -277,12 +222,11 @@ public slots:
|
||||
void setAdaptiveNoiseLevel(int level)
|
||||
{
|
||||
level = qBound(0, level, 100);
|
||||
if (m_adaptiveNoiseLevel != level && adaptiveModeActive())
|
||||
if (m_deviceInfo->adaptiveNoiseLevel() != level && m_deviceInfo->adaptiveModeActive())
|
||||
{
|
||||
m_adaptiveNoiseLevel = level;
|
||||
QByteArray packet = AirPodsPackets::AdaptiveNoise::getPacket(level);
|
||||
writePacketToSocket(packet, "Adaptive noise level packet written: ");
|
||||
emit adaptiveNoiseLevelChanged(level);
|
||||
m_deviceInfo->setAdaptiveNoiseLevel(level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +242,7 @@ public slots:
|
||||
LOG_WARN("Name is too long, must be 32 characters or less");
|
||||
return;
|
||||
}
|
||||
if (newName == m_deviceName)
|
||||
if (newName == m_deviceInfo->deviceName())
|
||||
{
|
||||
LOG_INFO("Name is already set to: " << newName);
|
||||
return;
|
||||
@@ -308,8 +252,7 @@ public slots:
|
||||
if (writePacketToSocket(packet, "Rename packet written: "))
|
||||
{
|
||||
LOG_INFO("Sent rename command for new name: " << newName);
|
||||
m_deviceName = newName;
|
||||
emit deviceNameChanged(newName);
|
||||
m_deviceInfo->setDeviceName(newName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -435,31 +378,7 @@ private slots:
|
||||
}
|
||||
|
||||
// Clear the device name and model
|
||||
m_deviceName.clear();
|
||||
connectedDeviceMacAddress.clear();
|
||||
mediaController->setConnectedDeviceMacAddress(connectedDeviceMacAddress);
|
||||
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
|
||||
emit airPodsStatusChanged();
|
||||
m_deviceInfo->reset();
|
||||
|
||||
// Show system notification
|
||||
trayManager->showNotification(
|
||||
@@ -516,43 +435,18 @@ private slots:
|
||||
return str;
|
||||
};
|
||||
|
||||
m_deviceName = extractString();
|
||||
QString modelNumber = extractString();
|
||||
QString manufacturer = extractString();
|
||||
QString hardwareVersion = extractString();
|
||||
QString firmwareVersion = extractString();
|
||||
QString firmwareVersion2 = extractString();
|
||||
QString softwareVersion = extractString();
|
||||
QString appIdentifier = extractString();
|
||||
QString serialNumber1 = extractString();
|
||||
QString serialNumber2 = extractString();
|
||||
QString unknownNumeric = extractString();
|
||||
QString unknownHash = extractString();
|
||||
QString trailingByte = extractString();
|
||||
|
||||
m_model = parseModelNumber(modelNumber);
|
||||
m_deviceInfo->setDeviceName(extractString());
|
||||
m_deviceInfo->setModelNumber(extractString());
|
||||
m_deviceInfo->setManufacturer(extractString());
|
||||
|
||||
m_deviceInfo->setModel(parseModelNumber(m_deviceInfo->modelNumber()));
|
||||
emit modelChanged();
|
||||
m_model = parseModelNumber(modelNumber);
|
||||
|
||||
emit modelChanged();
|
||||
emit deviceNameChanged(m_deviceName);
|
||||
|
||||
// Log extracted metadata
|
||||
LOG_INFO("Parsed AirPods metadata:");
|
||||
LOG_INFO("Device Name: " << m_deviceName);
|
||||
LOG_INFO("Model Number: " << modelNumber);
|
||||
LOG_INFO("Manufacturer: " << manufacturer);
|
||||
LOG_INFO("Hardware Version: " << hardwareVersion);
|
||||
LOG_INFO("Firmware Version: " << firmwareVersion);
|
||||
LOG_INFO("Firmware Version2: " << firmwareVersion2);
|
||||
LOG_INFO("Software Version: " << softwareVersion);
|
||||
LOG_INFO("App Identifier: " << appIdentifier);
|
||||
LOG_INFO("Serial Number 1: " << serialNumber1);
|
||||
LOG_INFO("Serial Number 2: " << serialNumber2);
|
||||
LOG_INFO("Unknown Numeric: " << unknownNumeric);
|
||||
LOG_INFO("Unknown Hash: " << unknownHash);
|
||||
LOG_INFO("Trailing Byte: " << trailingByte);
|
||||
LOG_INFO("Device Name: " << m_deviceInfo->deviceName());
|
||||
LOG_INFO("Model Number: " << m_deviceInfo->modelNumber());
|
||||
LOG_INFO("Manufacturer: " << m_deviceInfo->manufacturer());
|
||||
}
|
||||
|
||||
QString getEarStatus(char value)
|
||||
@@ -635,7 +529,7 @@ private slots:
|
||||
writePacketToSocket(AirPodsPackets::Connection::REQUEST_NOTIFICATIONS, "Request notifications packet written: ");
|
||||
|
||||
QTimer::singleShot(2000, this, [this]() {
|
||||
if (m_batteryStatus.isEmpty()) {
|
||||
if (m_deviceInfo->batteryStatus().isEmpty()) {
|
||||
writePacketToSocket(AirPodsPackets::Connection::REQUEST_NOTIFICATIONS, "Request notifications packet written: ");
|
||||
}
|
||||
});
|
||||
@@ -648,34 +542,26 @@ private slots:
|
||||
LOG_INFO("MagicAccIRK: " << keys.magicAccIRK.toHex());
|
||||
LOG_INFO("MagicAccEncKey: " << keys.magicAccEncKey.toHex());
|
||||
|
||||
// Store the keys for later use if needed
|
||||
m_magicAccIRK = keys.magicAccIRK;
|
||||
m_magicAccEncKey = keys.magicAccEncKey;
|
||||
// Store the keys
|
||||
m_deviceInfo->setMagicAccIRK(keys.magicAccIRK);
|
||||
m_deviceInfo->setMagicAccEncKey(keys.magicAccEncKey);
|
||||
}
|
||||
// Get CA state
|
||||
else if (data.startsWith(AirPodsPackets::ConversationalAwareness::HEADER)) {
|
||||
auto result = AirPodsPackets::ConversationalAwareness::parseState(data);
|
||||
if (result.has_value()) {
|
||||
m_conversationalAwareness = result.value();
|
||||
LOG_INFO("Conversational awareness state received: " << m_conversationalAwareness);
|
||||
emit conversationalAwarenessChanged(m_conversationalAwareness);
|
||||
} else {
|
||||
LOG_ERROR("Failed to parse conversational awareness state");
|
||||
if (auto result = AirPodsPackets::ConversationalAwareness::parseState(data))
|
||||
{
|
||||
m_deviceInfo->setConversationalAwareness(result.value());
|
||||
LOG_INFO("Conversational awareness state received: " << m_deviceInfo->conversationalAwareness());
|
||||
}
|
||||
}
|
||||
// Noise Control Mode
|
||||
else if (data.size() == 11 && data.startsWith(AirPodsPackets::NoiseControl::HEADER))
|
||||
{
|
||||
quint8 rawMode = data[7] - 1; // Offset still needed due to protocol
|
||||
if (rawMode >= (int)NoiseControlMode::MinValue && rawMode <= (int)NoiseControlMode::MaxValue)
|
||||
if (auto value = AirPodsPackets::NoiseControl::parseMode(data))
|
||||
{
|
||||
m_noiseControlMode = static_cast<NoiseControlMode>(rawMode);
|
||||
LOG_INFO("Noise control mode: " << rawMode);
|
||||
emit noiseControlModeChanged(m_noiseControlMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Invalid noise control mode value received: " << rawMode);
|
||||
LOG_INFO("Received noise control mode: " << value.value());
|
||||
m_deviceInfo->setNoiseControlMode(value.value());
|
||||
LOG_INFO("Noise control mode received: " << m_deviceInfo->noiseControlMode());
|
||||
}
|
||||
}
|
||||
// Ear Detection
|
||||
@@ -683,28 +569,25 @@ private slots:
|
||||
{
|
||||
char primary = data[6];
|
||||
char secondary = data[7];
|
||||
m_primaryInEar = data[6] == 0x00;
|
||||
m_secoundaryInEar = data[7] == 0x00;
|
||||
m_earDetectionStatus = QString("Primary: %1, Secondary: %2")
|
||||
.arg(getEarStatus(primary), getEarStatus(secondary));
|
||||
LOG_INFO("Ear detection status: " << m_earDetectionStatus);
|
||||
emit earDetectionStatusChanged(m_earDetectionStatus);
|
||||
emit primaryChanged();
|
||||
m_deviceInfo->setPrimaryInEar(data[6] == 0x00);
|
||||
m_deviceInfo->setSecondaryInEar(data[7] == 0x00);
|
||||
m_deviceInfo->setEarDetectionStatus(QString("Primary: %1, Secondary: %2")
|
||||
.arg(getEarStatus(primary), getEarStatus(secondary)));
|
||||
LOG_INFO("Ear detection status: " << m_deviceInfo->earDetectionStatus());
|
||||
}
|
||||
// Battery Status
|
||||
else if (data.size() == 22 && data.startsWith(AirPodsPackets::Parse::BATTERY_STATUS))
|
||||
{
|
||||
m_battery->parsePacket(data);
|
||||
m_deviceInfo->getBattery()->parsePacket(data);
|
||||
|
||||
int leftLevel = m_battery->getState(Battery::Component::Left).level;
|
||||
int rightLevel = m_battery->getState(Battery::Component::Right).level;
|
||||
int caseLevel = m_battery->getState(Battery::Component::Case).level;
|
||||
m_batteryStatus = QString("Left: %1%, Right: %2%, Case: %3%")
|
||||
int leftLevel = m_deviceInfo->getBattery()->getState(Battery::Component::Left).level;
|
||||
int rightLevel = m_deviceInfo->getBattery()->getState(Battery::Component::Right).level;
|
||||
int caseLevel = m_deviceInfo->getBattery()->getState(Battery::Component::Case).level;
|
||||
m_deviceInfo->setBatteryStatus(QString("Left: %1%, Right: %2%, Case: %3%")
|
||||
.arg(leftLevel)
|
||||
.arg(rightLevel)
|
||||
.arg(caseLevel);
|
||||
LOG_INFO("Battery status: " << m_batteryStatus);
|
||||
emit batteryStatusChanged(m_batteryStatus);
|
||||
.arg(caseLevel));
|
||||
LOG_INFO("Battery status: " << m_deviceInfo->batteryStatus());
|
||||
}
|
||||
// Conversational Awareness Data
|
||||
else if (data.size() == 10 && data.startsWith(AirPodsPackets::ConversationalAwareness::DATA_HEADER))
|
||||
@@ -717,23 +600,17 @@ private slots:
|
||||
parseMetadata(data);
|
||||
initiateMagicPairing();
|
||||
mediaController->setConnectedDeviceMacAddress(connectedDeviceMacAddress);
|
||||
if (isLeftPodInEar() || isRightPodInEar()) // AirPods get added as output device only after this
|
||||
if (m_deviceInfo->oneOrMorePodsInEar()) // AirPods get added as output device only after this
|
||||
{
|
||||
mediaController->activateA2dpProfile();
|
||||
}
|
||||
emit airPodsStatusChanged();
|
||||
}
|
||||
else if (data.startsWith(AirPodsPackets::OneBudANCMode::HEADER)) {
|
||||
auto result = AirPodsPackets::OneBudANCMode::parseState(data);
|
||||
if (result.has_value())
|
||||
if (auto value = AirPodsPackets::OneBudANCMode::parseState(data))
|
||||
{
|
||||
m_oneBudANCMode = result.value();
|
||||
LOG_INFO("One Bud ANC mode received: " << m_conversationalAwareness);
|
||||
emit oneBudANCModeChanged(m_conversationalAwareness);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to parse One Bud ANC mode");
|
||||
m_deviceInfo->setOneBudANCMode(value.value());
|
||||
LOG_INFO("One Bud ANC mode received: " << m_deviceInfo->oneBudANCMode());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -980,20 +857,7 @@ private:
|
||||
AutoStartManager *m_autoStartManager;
|
||||
int m_retryAttempts = 3;
|
||||
bool m_hideOnStart = false;
|
||||
|
||||
QString m_batteryStatus;
|
||||
QString m_earDetectionStatus;
|
||||
NoiseControlMode m_noiseControlMode = NoiseControlMode::Off;
|
||||
bool m_conversationalAwareness = false;
|
||||
int m_adaptiveNoiseLevel = 50;
|
||||
QString m_deviceName;
|
||||
Battery *m_battery;
|
||||
AirPodsModel m_model = AirPodsModel::Unknown;
|
||||
bool m_primaryInEar = false;
|
||||
bool m_secoundaryInEar = false;
|
||||
QByteArray m_magicAccIRK;
|
||||
QByteArray m_magicAccEncKey;
|
||||
bool m_oneBudANCMode = false;
|
||||
DeviceInfo *m_deviceInfo;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@@ -1038,6 +902,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
qmlRegisterType<Battery>("me.kavishdevar.Battery", 1, 0, "Battery");
|
||||
qmlRegisterType<DeviceInfo>("me.kavishdevar.DeviceInfo", 1, 0, "DeviceInfo");
|
||||
AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, hideOnStart, &engine);
|
||||
engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp);
|
||||
trayApp->loadMainModule();
|
||||
|
||||
Reference in New Issue
Block a user