[Linux] Let users edit Phone bluetooth MAC via GUI (#195)

initial commit
This commit is contained in:
TCH
2025-08-25 17:45:36 +02:00
committed by GitHub
parent dde5d1e808
commit eb1b633aff
2 changed files with 98 additions and 0 deletions

View File

@@ -154,8 +154,28 @@ ApplicationWindow {
visible: airPodsTrayApp.airpodsConnected visible: airPodsTrayApp.airpodsConnected
text: "Conversational Awareness" text: "Conversational Awareness"
checked: airPodsTrayApp.deviceInfo.conversationalAwareness checked: airPodsTrayApp.deviceInfo.conversationalAwareness
// Disable when no phone MAC set or using default placeholder
enabled: !(PHONE_MAC_ADDRESS === "" || PHONE_MAC_ADDRESS === "00:00:00:00:00:00")
onCheckedChanged: airPodsTrayApp.setConversationalAwareness(checked) onCheckedChanged: airPodsTrayApp.setConversationalAwareness(checked)
} }
// Instruction when Conversational Awareness is disabled due to missing phone MAC
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 8
visible: airPodsTrayApp.airpodsConnected && (PHONE_MAC_ADDRESS === "" || PHONE_MAC_ADDRESS === "00:00:00:00:00:00")
Label {
text: "Set your phone's MAC in Settings to use this feature"
font.pixelSize: 12
color: "gray"
}
Button {
text: "Open Settings"
onClicked: stackView.push(settingsPage)
}
}
} }
RoundButton { RoundButton {
@@ -269,6 +289,23 @@ ApplicationWindow {
} }
} }
Row {
spacing: 10
visible: airPodsTrayApp.airpodsConnected
TextField {
id: newPhoneMacField
placeholderText: (PHONE_MAC_ADDRESS !== "" ? PHONE_MAC_ADDRESS : "00:00:00:00:00:00")
maximumLength: 32
}
Button {
text: "Change Phone MAC"
onClicked: airPodsTrayApp.setPhoneMac(newPhoneMacField.text)
}
}
Button { Button {
text: "Show Magic Cloud Keys QR" text: "Show Magic Cloud Keys QR"
onClicked: keysQrDialog.show() onClicked: keysQrDialog.show()

View File

@@ -11,6 +11,7 @@
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QProcess> #include <QProcess>
#include <QRegularExpression>
#include "airpods_packets.h" #include "airpods_packets.h"
#include "logger.h" #include "logger.h"
@@ -40,6 +41,7 @@ class AirPodsTrayApp : public QObject {
Q_PROPERTY(int retryAttempts READ retryAttempts WRITE setRetryAttempts NOTIFY retryAttemptsChanged) Q_PROPERTY(int retryAttempts READ retryAttempts WRITE setRetryAttempts NOTIFY retryAttemptsChanged)
Q_PROPERTY(bool hideOnStart READ hideOnStart CONSTANT) Q_PROPERTY(bool hideOnStart READ hideOnStart CONSTANT)
Q_PROPERTY(DeviceInfo *deviceInfo READ deviceInfo CONSTANT) Q_PROPERTY(DeviceInfo *deviceInfo READ deviceInfo CONSTANT)
Q_PROPERTY(QString phoneMacStatus READ phoneMacStatus NOTIFY phoneMacStatusChanged)
public: public:
AirPodsTrayApp(bool debugMode, bool hideOnStart, QQmlApplicationEngine *parent = nullptr) AirPodsTrayApp(bool debugMode, bool hideOnStart, QQmlApplicationEngine *parent = nullptr)
@@ -119,6 +121,7 @@ public:
int retryAttempts() const { return m_retryAttempts; } int retryAttempts() const { return m_retryAttempts; }
bool hideOnStart() const { return m_hideOnStart; } bool hideOnStart() const { return m_hideOnStart; }
DeviceInfo *deviceInfo() const { return m_deviceInfo; } DeviceInfo *deviceInfo() const { return m_deviceInfo; }
QString phoneMacStatus() const { return m_phoneMacStatus; }
private: private:
bool debugMode; bool debugMode;
@@ -310,6 +313,51 @@ public slots:
emit crossDeviceEnabledChanged(enabled); emit crossDeviceEnabledChanged(enabled);
} }
void setPhoneMac(const QString &mac)
{
if (mac.isEmpty()) {
LOG_WARN("Empty MAC provided, ignoring");
m_phoneMacStatus = QStringLiteral("No MAC provided (ignoring)");
emit phoneMacStatusChanged();
return;
}
// Basic MAC address validation (accepts formats like AA:BB:CC:DD:EE:FF, AABBCCDDEEFF, AA-BB-CC-DD-EE-FF)
QRegularExpression re("^([0-9A-Fa-f]{2}([-:]?)){5}[0-9A-Fa-f]{2}$");
if (!re.match(mac).hasMatch()) {
LOG_ERROR("Invalid MAC address format: " << mac);
m_phoneMacStatus = QStringLiteral("Invalid MAC: ") + mac;
emit phoneMacStatusChanged();
return;
}
// Set environment variable for the running process
qputenv("PHONE_MAC_ADDRESS", mac.toUtf8());
LOG_INFO("PHONE_MAC_ADDRESS environment variable set to: " << mac);
m_phoneMacStatus = QStringLiteral("Updated MAC: ") + mac;
emit phoneMacStatusChanged();
// Update QML context property so UI placeholders reflect the new value
if (parent) {
parent->rootContext()->setContextProperty("PHONE_MAC_ADDRESS", mac);
}
// If a phone socket exists, restart connection using the new MAC
if (phoneSocket && phoneSocket->isOpen()) {
phoneSocket->close();
phoneSocket->deleteLater();
phoneSocket = nullptr;
}
connectToPhone();
}
void updatePhoneMacStatus(const QString &status)
{
m_phoneMacStatus = status;
emit phoneMacStatusChanged();
}
bool writePacketToSocket(const QByteArray &packet, const QString &logMessage) bool writePacketToSocket(const QByteArray &packet, const QString &logMessage)
{ {
if (socket && socket->isOpen()) if (socket && socket->isOpen())
@@ -658,6 +706,7 @@ private slots:
} }
QBluetoothAddress phoneAddress("00:00:00:00:00:00"); // Default address, will be overwritten if PHONE_MAC_ADDRESS is set QBluetoothAddress phoneAddress("00:00:00:00:00:00"); // Default address, will be overwritten if PHONE_MAC_ADDRESS is set
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
if (!env.value("PHONE_MAC_ADDRESS").isEmpty()) if (!env.value("PHONE_MAC_ADDRESS").isEmpty())
{ {
phoneAddress = QBluetoothAddress(env.value("PHONE_MAC_ADDRESS")); phoneAddress = QBluetoothAddress(env.value("PHONE_MAC_ADDRESS"));
@@ -854,6 +903,7 @@ signals:
void notificationsEnabledChanged(bool enabled); void notificationsEnabledChanged(bool enabled);
void retryAttemptsChanged(int attempts); void retryAttemptsChanged(int attempts);
void oneBudANCModeChanged(bool enabled); void oneBudANCModeChanged(bool enabled);
void phoneMacStatusChanged();
private: private:
QBluetoothSocket *socket = nullptr; QBluetoothSocket *socket = nullptr;
@@ -870,6 +920,7 @@ private:
DeviceInfo *m_deviceInfo; DeviceInfo *m_deviceInfo;
BleManager *m_bleManager; BleManager *m_bleManager;
SystemSleepMonitor *m_systemSleepMonitor = nullptr; SystemSleepMonitor *m_systemSleepMonitor = nullptr;
QString m_phoneMacStatus;
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@@ -917,6 +968,16 @@ int main(int argc, char *argv[]) {
qmlRegisterType<DeviceInfo>("me.kavishdevar.DeviceInfo", 1, 0, "DeviceInfo"); qmlRegisterType<DeviceInfo>("me.kavishdevar.DeviceInfo", 1, 0, "DeviceInfo");
AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, hideOnStart, &engine); AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, hideOnStart, &engine);
engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp); engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp);
// Expose PHONE_MAC_ADDRESS environment variable to QML for placeholder in settings
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString phoneMacEnv = env.value("PHONE_MAC_ADDRESS", "");
engine.rootContext()->setContextProperty("PHONE_MAC_ADDRESS", phoneMacEnv);
// Initialize the visible status in the GUI
trayApp->updatePhoneMacStatus(phoneMacEnv.isEmpty() ? QStringLiteral("No phone MAC set") : phoneMacEnv);
}
engine.addImageProvider("qrcode", new QRCodeImageProvider()); engine.addImageProvider("qrcode", new QRCodeImageProvider());
trayApp->loadMainModule(); trayApp->loadMainModule();