[Linux] Add reopen to tray options, enhance app tray, add ability to detect duplicate app instances, prevent duplicate app instances, and allow for original instance to be brought to front using the sockets

This commit is contained in:
Henry Cheng
2025-04-24 19:16:24 -04:00
parent 1571c6d300
commit a51efe35dc
4 changed files with 119 additions and 2 deletions

View File

@@ -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

View File

@@ -1,5 +1,6 @@
#include <QSettings>
#include <QLocalServer>
#include <QLocalSocket>
#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<NoiseControlMode>(&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();
}

View File

@@ -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<QString, NoiseControlMode> noiseOptions[] = {
@@ -82,6 +97,8 @@ void TrayIconManager::setupMenuActions()
{ emit noiseControlChanged(mode); });
}
trayMenu->addSeparator();
// Quit action
QAction *quitAction = new QAction("Quit", trayMenu);
trayMenu->addAction(quitAction);

View File

@@ -54,4 +54,6 @@ signals:
void trayClicked();
void noiseControlChanged(AirpodsTrayApp::Enums::NoiseControlMode);
void conversationalAwarenessToggled(bool enabled);
void openApp();
void openSettings();
};