diff --git a/linux/Main.qml b/linux/Main.qml index f4650b2..de3f502 100644 --- a/linux/Main.qml +++ b/linux/Main.qml @@ -9,9 +9,33 @@ ApplicationWindow { width: 400 height: 300 title: "AirPods Settings" + objectName: "mainWindowObject" onClosing: mainWindow.visible = false + function reopen(pageToLoad) { + if (pageToLoad == "settings") + { + if (stackView.depth == 1) + { + stackView.push(settingsPage) + } + } + else + { + if (stackView.depth > 1) + { + stackView.pop() + } + } + + if (!mainWindow.visible) { + mainWindow.visible = true + } + raise() + requestActivate() + } + // Mouse area for handling back/forward navigation MouseArea { anchors.fill: parent diff --git a/linux/main.cpp b/linux/main.cpp index ae5a10f..3a0dbb8 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -1,5 +1,6 @@ #include - +#include +#include #include "main.h" #include "airpods_packets.h" #include "logger.h" @@ -38,7 +39,7 @@ class AirPodsTrayApp : public QObject { Q_PROPERTY(bool hideOnStart READ hideOnStart CONSTANT) public: - AirPodsTrayApp(bool debugMode, bool hideOnStart, QObject *parent = nullptr) + AirPodsTrayApp(bool debugMode, bool hideOnStart, QQmlApplicationEngine *parent = nullptr) : QObject(parent) , debugMode(debugMode) , m_battery(new Battery(this)) @@ -46,6 +47,7 @@ public: , m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp")) , m_autoStartManager(new AutoStartManager(this)) , m_hideOnStart(hideOnStart) + , parent(parent) { if (debugMode) { QLoggingCategory::setFilterRules("airpodsApp.debug=true"); @@ -58,6 +60,8 @@ public: trayManager = new TrayIconManager(this); trayManager->setNotificationsEnabled(loadNotificationsEnabled()); 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(&AirPodsTrayApp::setNoiseControlMode)); connect(trayManager, &TrayIconManager::conversationalAwarenessToggled, this, &AirPodsTrayApp::setConversationalAwareness); connect(this, &AirPodsTrayApp::batteryStatusChanged, trayManager, &TrayIconManager::updateBatteryStatus); @@ -146,6 +150,9 @@ public: private: bool debugMode; bool isConnectedLocally = false; + + QQmlApplicationEngine *parent = nullptr; + struct { bool isAvailable = true; bool isEnabled = true; // Ability to disable the feature @@ -352,6 +359,30 @@ private slots: } } + void onOpenApp() + { + QObject *rootObject = parent->rootObjects().first(); + if (rootObject) { + QMetaObject::invokeMethod(rootObject, "reopen", Q_ARG(QVariant, "app")); + } + else + { + parent->loadFromModule("linux", "Main"); + } + } + + void onOpenSettings() + { + QObject *rootObject = parent->rootObjects().first(); + if (rootObject) { + QMetaObject::invokeMethod(rootObject, "reopen", Q_ARG(QVariant, "settings")); + } + else + { + parent->loadFromModule("linux", "Main"); + } + } + void sendHandshake() { LOG_INFO("Connected to device, sending initial packets"); writePacketToSocket(AirPodsPackets::Connection::HANDSHAKE, "Handshake packet written: "); @@ -918,6 +949,25 @@ private: int main(int argc, char *argv[]) { QApplication app(argc, argv); + + // Check if app is already open + QSharedMemory sharedMemory; + sharedMemory.setKey("TcpServer-Key"); + if(sharedMemory.create(1) == false) + { + LOG_INFO("Another instance already running! Opening App Window Instead"); + QLocalSocket socket; + // Connect to the original app, then trigger the reopen signal + socket.connectToServer("app_server"); + if (socket.waitForConnected(500)) { + socket.write("reopen"); + socket.flush(); + socket.waitForBytesWritten(500); + socket.disconnectFromServer(); + } + app.exit(); // exit already a process running + return 0; + } app.setQuitOnLastWindowClosed(false); bool debugMode = false; @@ -935,6 +985,30 @@ int main(int argc, char *argv[]) { AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, hideOnStart, &engine); engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp); engine.loadFromModule("linux", "Main"); + + QLocalServer server; + QLocalServer::removeServer("app_server"); + + server.listen("app_server"); + QObject::connect(&server, &QLocalServer::newConnection, [&]() { + QLocalSocket* socket = server.nextPendingConnection(); + QObject::connect(socket, &QLocalSocket::readyRead, [socket, &engine]() { + QString msg = socket->readAll(); + // Check if the message is "reopen", if so, trigger onOpenApp function + if (msg == "reopen") { + LOG_INFO("Reopening app window"); + QObject *rootObject = engine.rootObjects().first(); + if (rootObject) { + QMetaObject::invokeMethod(rootObject, "reopen", Q_ARG(QVariant, "app")); + } + else + { + engine.loadFromModule("linux", "Main"); + } + } + socket->disconnectFromServer(); + }); + }); return app.exec(); } diff --git a/linux/trayiconmanager.cpp b/linux/trayiconmanager.cpp index 137a841..57c0a68 100644 --- a/linux/trayiconmanager.cpp +++ b/linux/trayiconmanager.cpp @@ -56,6 +56,19 @@ void TrayIconManager::updateConversationalAwareness(bool enabled) void TrayIconManager::setupMenuActions() { + // Open action + QAction *openAction = new QAction("Open", trayMenu); + trayMenu->addAction(openAction); + connect(openAction, &QAction::triggered, qApp, [this](){emit openApp();}); + + // Settings Menu + + QAction *settingsMenu = new QAction("Settings", trayMenu); + trayMenu->addAction(settingsMenu); + connect(settingsMenu, &QAction::triggered, qApp, [this](){emit openSettings();}); + + trayMenu->addSeparator(); + // Conversational Awareness Toggle caToggleAction = new QAction("Toggle Conversational Awareness", trayMenu); caToggleAction->setCheckable(true); @@ -63,6 +76,8 @@ void TrayIconManager::setupMenuActions() connect(caToggleAction, &QAction::triggered, this, [this](bool checked) { emit conversationalAwarenessToggled(checked); }); + trayMenu->addSeparator(); + // Noise Control Options noiseControlGroup = new QActionGroup(trayMenu); const QPair noiseOptions[] = { @@ -82,6 +97,8 @@ void TrayIconManager::setupMenuActions() { emit noiseControlChanged(mode); }); } + trayMenu->addSeparator(); + // Quit action QAction *quitAction = new QAction("Quit", trayMenu); trayMenu->addAction(quitAction); diff --git a/linux/trayiconmanager.h b/linux/trayiconmanager.h index adcdbdb..a6384e3 100644 --- a/linux/trayiconmanager.h +++ b/linux/trayiconmanager.h @@ -54,4 +54,6 @@ signals: void trayClicked(); void noiseControlChanged(AirpodsTrayApp::Enums::NoiseControlMode); void conversationalAwarenessToggled(bool enabled); + void openApp(); + void openSettings(); }; \ No newline at end of file