From ec1b0c47cad686c8c9a5893a46994379ab158dc9 Mon Sep 17 00:00:00 2001 From: Tim Gromeyer Date: Mon, 21 Apr 2025 12:47:15 +0200 Subject: [PATCH] [Linux] Add autostart setting --- linux/CMakeLists.txt | 1 + linux/Main.qml | 7 +++ linux/autostartmanager.hpp | 98 ++++++++++++++++++++++++++++++++++++++ linux/main.cpp | 23 +++++---- 4 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 linux/autostartmanager.hpp diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index d46fa4b..4d277d4 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -21,6 +21,7 @@ qt_add_executable(applinux battery.hpp BluetoothMonitor.cpp BluetoothMonitor.h + autostartmanager.hpp ) qt_add_qml_module(applinux diff --git a/linux/Main.qml b/linux/Main.qml index f330422..d824edd 100644 --- a/linux/Main.qml +++ b/linux/Main.qml @@ -127,6 +127,7 @@ ApplicationWindow { } Switch { + visible: airPodsTrayApp.airpodsConnected text: "Conversational Awareness" checked: airPodsTrayApp.conversationalAwareness onCheckedChanged: airPodsTrayApp.conversationalAwareness = checked @@ -187,6 +188,12 @@ ApplicationWindow { } } + Switch { + text: "Auto-Start on Login" + checked: airPodsTrayApp.autoStartManager.autoStartEnabled + onCheckedChanged: airPodsTrayApp.autoStartManager.autoStartEnabled = checked + } + Row { spacing: 10 diff --git a/linux/autostartmanager.hpp b/linux/autostartmanager.hpp new file mode 100644 index 0000000..d8e767b --- /dev/null +++ b/linux/autostartmanager.hpp @@ -0,0 +1,98 @@ +#ifndef AUTOSTARTMANAGER_HPP +#define AUTOSTARTMANAGER_HPP + +#include +#include +#include +#include +#include +#include + +class AutoStartManager : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool autoStartEnabled READ autoStartEnabled WRITE setAutoStartEnabled NOTIFY autoStartEnabledChanged) + +public: + explicit AutoStartManager(QObject *parent = nullptr) : QObject(parent) + { + QString autostartDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/autostart"; + QDir().mkpath(autostartDir); + m_autostartFilePath = autostartDir + "/" + QCoreApplication::applicationName() + ".desktop"; + } + + bool autoStartEnabled() const + { + return QFile::exists(m_autostartFilePath); + } + + void setAutoStartEnabled(bool enabled) + { + if (autoStartEnabled() == enabled) + { + return; + } + + if (enabled) + { + createAutoStartEntry(); + } + else + { + removeAutoStartEntry(); + } + + emit autoStartEnabledChanged(enabled); + } + +private: + void createAutoStartEntry() + { + QFile desktopFile(m_autostartFilePath); + if (!desktopFile.open(QIODevice::WriteOnly | QIODevice::Text)) + { + qWarning() << "Failed to create autostart file:" << desktopFile.errorString(); + return; + } + + QString appPath = QCoreApplication::applicationFilePath(); + // Handle cases where the path might contain spaces + if (appPath.contains(' ')) + { + appPath = "\"" + appPath + "\""; + } + + QString content = QStringLiteral( + "[Desktop Entry]\n" + "Type=Application\n" + "Name=%1\n" + "Exec=%2\n" + "Icon=%3\n" + "Comment=%4\n" + "X-GNOME-Autostart-enabled=true\n" + "Terminal=false\n") + .arg( + QCoreApplication::applicationName(), + appPath, + QCoreApplication::applicationName().toLower(), + QCoreApplication::applicationName() + " autostart"); + + desktopFile.write(content.toUtf8()); + desktopFile.close(); + } + + void removeAutoStartEntry() + { + if (QFile::exists(m_autostartFilePath)) + { + QFile::remove(m_autostartFilePath); + } + } + + QString m_autostartFilePath; + +signals: + void autoStartEnabledChanged(bool enabled); +}; + +#endif // AUTOSTARTMANAGER_HPP \ No newline at end of file diff --git a/linux/main.cpp b/linux/main.cpp index e6efcaf..57b0f55 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -8,6 +8,7 @@ #include "enums.h" #include "battery.hpp" #include "BluetoothMonitor.h" +#include "autostartmanager.hpp" using namespace AirpodsTrayApp::Enums; @@ -31,13 +32,17 @@ class AirPodsTrayApp : public QObject { 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) + Q_PROPERTY(AutoStartManager *autoStartManager READ autoStartManager CONSTANT) public: - AirPodsTrayApp(bool debugMode) - : debugMode(debugMode) + AirPodsTrayApp(bool debugMode, QObject *parent = nullptr) + : QObject(parent) + , debugMode(debugMode) , m_battery(new Battery(this)) , monitor(new BluetoothMonitor(this)) - , m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp")){ + , m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp")) + , m_autoStartManager(new AutoStartManager(this)) + { if (debugMode) { QLoggingCategory::setFilterRules("airpodsApp.debug=true"); } else { @@ -92,8 +97,6 @@ public: saveCrossDeviceEnabled(); saveEarDetectionSettings(); - delete trayIcon; - delete trayMenu; delete socket; delete phoneSocket; } @@ -126,6 +129,7 @@ public: bool areAirpodsConnected() const { return socket && socket->isOpen() && socket->state() == QBluetoothSocket::SocketState::ConnectedState; } int earDetectionBehavior() const { return mediaController->getEarDetectionBehavior(); } bool crossDeviceEnabled() const { return CrossDevice.isEnabled; } + AutoStartManager *autoStartManager() const { return m_autoStartManager; } private: bool debugMode; @@ -853,8 +857,7 @@ signals: void earDetectionBehaviorChanged(int behavior); void crossDeviceEnabledChanged(bool enabled); - private : QSystemTrayIcon *trayIcon; - QMenu *trayMenu; +private: QBluetoothSocket *socket = nullptr; QBluetoothSocket *phoneSocket = nullptr; QString connectedDeviceMacAddress; @@ -864,6 +867,7 @@ signals: TrayIconManager *trayManager; BluetoothMonitor *monitor; QSettings *m_settings; + AutoStartManager *m_autoStartManager; QString m_batteryStatus; QString m_earDetectionStatus; @@ -893,10 +897,9 @@ int main(int argc, char *argv[]) { QQmlApplicationEngine engine; qmlRegisterType("me.kavishdevar.Battery", 1, 0, "Battery"); - AirPodsTrayApp trayApp(debugMode); - engine.rootContext()->setContextProperty("airPodsTrayApp", &trayApp); + AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, &engine); + engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp); engine.loadFromModule("linux", "Main"); - return app.exec(); }