mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-02-10 19:52:24 +00:00
Merge pull request #111 from jazzysoggy/main
[Linux] Add seperators to tray, add tray option to reopen app and settings, prevent duplicate instances of app from being opened
This commit is contained in:
@@ -9,9 +9,33 @@ ApplicationWindow {
|
|||||||
width: 400
|
width: 400
|
||||||
height: 300
|
height: 300
|
||||||
title: "AirPods Settings"
|
title: "AirPods Settings"
|
||||||
|
objectName: "mainWindowObject"
|
||||||
|
|
||||||
onClosing: mainWindow.visible = false
|
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
|
// Mouse area for handling back/forward navigation
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
120
linux/main.cpp
120
linux/main.cpp
@@ -1,5 +1,6 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QLocalServer>
|
||||||
|
#include <QLocalSocket>
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "airpods_packets.h"
|
#include "airpods_packets.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
@@ -38,7 +39,7 @@ class AirPodsTrayApp : public QObject {
|
|||||||
Q_PROPERTY(bool hideOnStart READ hideOnStart CONSTANT)
|
Q_PROPERTY(bool hideOnStart READ hideOnStart CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AirPodsTrayApp(bool debugMode, bool hideOnStart, QObject *parent = nullptr)
|
AirPodsTrayApp(bool debugMode, bool hideOnStart, QQmlApplicationEngine *parent = nullptr)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, debugMode(debugMode)
|
, debugMode(debugMode)
|
||||||
, m_battery(new Battery(this))
|
, m_battery(new Battery(this))
|
||||||
@@ -46,6 +47,7 @@ public:
|
|||||||
, m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp"))
|
, m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp"))
|
||||||
, m_autoStartManager(new AutoStartManager(this))
|
, m_autoStartManager(new AutoStartManager(this))
|
||||||
, m_hideOnStart(hideOnStart)
|
, m_hideOnStart(hideOnStart)
|
||||||
|
, parent(parent)
|
||||||
{
|
{
|
||||||
if (debugMode) {
|
if (debugMode) {
|
||||||
QLoggingCategory::setFilterRules("airpodsApp.debug=true");
|
QLoggingCategory::setFilterRules("airpodsApp.debug=true");
|
||||||
@@ -58,6 +60,8 @@ public:
|
|||||||
trayManager = new TrayIconManager(this);
|
trayManager = new TrayIconManager(this);
|
||||||
trayManager->setNotificationsEnabled(loadNotificationsEnabled());
|
trayManager->setNotificationsEnabled(loadNotificationsEnabled());
|
||||||
connect(trayManager, &TrayIconManager::trayClicked, this, &AirPodsTrayApp::onTrayIconActivated);
|
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, qOverload<NoiseControlMode>(&AirPodsTrayApp::setNoiseControlMode));
|
||||||
connect(trayManager, &TrayIconManager::conversationalAwarenessToggled, this, &AirPodsTrayApp::setConversationalAwareness);
|
connect(trayManager, &TrayIconManager::conversationalAwarenessToggled, this, &AirPodsTrayApp::setConversationalAwareness);
|
||||||
connect(this, &AirPodsTrayApp::batteryStatusChanged, trayManager, &TrayIconManager::updateBatteryStatus);
|
connect(this, &AirPodsTrayApp::batteryStatusChanged, trayManager, &TrayIconManager::updateBatteryStatus);
|
||||||
@@ -146,6 +150,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool debugMode;
|
bool debugMode;
|
||||||
bool isConnectedLocally = false;
|
bool isConnectedLocally = false;
|
||||||
|
|
||||||
|
QQmlApplicationEngine *parent = nullptr;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool isAvailable = true;
|
bool isAvailable = true;
|
||||||
bool isEnabled = true; // Ability to disable the feature
|
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() {
|
void sendHandshake() {
|
||||||
LOG_INFO("Connected to device, sending initial packets");
|
LOG_INFO("Connected to device, sending initial packets");
|
||||||
writePacketToSocket(AirPodsPackets::Connection::HANDSHAKE, "Handshake packet written: ");
|
writePacketToSocket(AirPodsPackets::Connection::HANDSHAKE, "Handshake packet written: ");
|
||||||
@@ -681,8 +712,12 @@ private slots:
|
|||||||
LOG_INFO("Already connected to the phone");
|
LOG_INFO("Already connected to the phone");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QBluetoothAddress phoneAddress(PHONE_MAC_ADDRESS);
|
QBluetoothAddress phoneAddress(PHONE_MAC_ADDRESS);
|
||||||
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||||
|
if (!env.value("PHONE_MAC_ADDRESS").isEmpty())
|
||||||
|
{
|
||||||
|
QBluetoothAddress phoneAddress = QBluetoothAddress(env.value("PHONE_MAC_ADDRESS"));
|
||||||
|
}
|
||||||
phoneSocket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol);
|
phoneSocket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol);
|
||||||
connect(phoneSocket, &QBluetoothSocket::connected, this, [this]() {
|
connect(phoneSocket, &QBluetoothSocket::connected, this, [this]() {
|
||||||
LOG_INFO("Connected to phone");
|
LOG_INFO("Connected to phone");
|
||||||
@@ -918,6 +953,32 @@ private:
|
|||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
QSharedMemory sharedMemory;
|
||||||
|
sharedMemory.setKey("TcpServer-Key");
|
||||||
|
|
||||||
|
// Check if app is already open
|
||||||
|
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; process already running
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Failed connection, log and open the app (assume it's not running)
|
||||||
|
LOG_ERROR("Failed to connect to the original app instance. Assuming it is not running.");
|
||||||
|
LOG_DEBUG("Socket error: " << socket.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
app.setQuitOnLastWindowClosed(false);
|
app.setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
bool debugMode = false;
|
bool debugMode = false;
|
||||||
@@ -935,6 +996,59 @@ int main(int argc, char *argv[]) {
|
|||||||
AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, hideOnStart, &engine);
|
AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, hideOnStart, &engine);
|
||||||
engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp);
|
engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp);
|
||||||
engine.loadFromModule("linux", "Main");
|
engine.loadFromModule("linux", "Main");
|
||||||
|
|
||||||
|
QLocalServer server;
|
||||||
|
QLocalServer::removeServer("app_server");
|
||||||
|
|
||||||
|
if (!server.listen("app_server"))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unable to start the listening server");
|
||||||
|
LOG_DEBUG("Server error: " << server.errorString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_DEBUG("Server started, waiting for connections...");
|
||||||
|
}
|
||||||
|
QObject::connect(&server, &QLocalServer::newConnection, [&]() {
|
||||||
|
QLocalSocket* socket = server.nextPendingConnection();
|
||||||
|
// Handles Proper Connection
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unknown message received: " << msg);
|
||||||
|
}
|
||||||
|
socket->disconnectFromServer();
|
||||||
|
});
|
||||||
|
// Handles connection errors
|
||||||
|
QObject::connect(socket, &QLocalSocket::errorOccurred, [socket]() {
|
||||||
|
LOG_ERROR("Failed to connect to the duplicate app instance");
|
||||||
|
LOG_DEBUG("Connection error: " << socket->errorString());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle server-level errors
|
||||||
|
QObject::connect(&server, &QLocalServer::serverError, [&]() {
|
||||||
|
LOG_ERROR("Server failed to accept a new connection");
|
||||||
|
LOG_DEBUG("Server error: " << server.errorString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(&app, &QCoreApplication::aboutToQuit, [&]() {
|
||||||
|
LOG_DEBUG("Application is about to quit. Cleaning up...");
|
||||||
|
sharedMemory.detach();
|
||||||
|
});
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,19 @@ void TrayIconManager::updateConversationalAwareness(bool enabled)
|
|||||||
|
|
||||||
void TrayIconManager::setupMenuActions()
|
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
|
// Conversational Awareness Toggle
|
||||||
caToggleAction = new QAction("Toggle Conversational Awareness", trayMenu);
|
caToggleAction = new QAction("Toggle Conversational Awareness", trayMenu);
|
||||||
caToggleAction->setCheckable(true);
|
caToggleAction->setCheckable(true);
|
||||||
@@ -63,6 +76,8 @@ void TrayIconManager::setupMenuActions()
|
|||||||
connect(caToggleAction, &QAction::triggered, this, [this](bool checked)
|
connect(caToggleAction, &QAction::triggered, this, [this](bool checked)
|
||||||
{ emit conversationalAwarenessToggled(checked); });
|
{ emit conversationalAwarenessToggled(checked); });
|
||||||
|
|
||||||
|
trayMenu->addSeparator();
|
||||||
|
|
||||||
// Noise Control Options
|
// Noise Control Options
|
||||||
noiseControlGroup = new QActionGroup(trayMenu);
|
noiseControlGroup = new QActionGroup(trayMenu);
|
||||||
const QPair<QString, NoiseControlMode> noiseOptions[] = {
|
const QPair<QString, NoiseControlMode> noiseOptions[] = {
|
||||||
@@ -82,6 +97,8 @@ void TrayIconManager::setupMenuActions()
|
|||||||
{ emit noiseControlChanged(mode); });
|
{ emit noiseControlChanged(mode); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trayMenu->addSeparator();
|
||||||
|
|
||||||
// Quit action
|
// Quit action
|
||||||
QAction *quitAction = new QAction("Quit", trayMenu);
|
QAction *quitAction = new QAction("Quit", trayMenu);
|
||||||
trayMenu->addAction(quitAction);
|
trayMenu->addAction(quitAction);
|
||||||
|
|||||||
@@ -54,4 +54,6 @@ signals:
|
|||||||
void trayClicked();
|
void trayClicked();
|
||||||
void noiseControlChanged(AirpodsTrayApp::Enums::NoiseControlMode);
|
void noiseControlChanged(AirpodsTrayApp::Enums::NoiseControlMode);
|
||||||
void conversationalAwarenessToggled(bool enabled);
|
void conversationalAwarenessToggled(bool enabled);
|
||||||
|
void openApp();
|
||||||
|
void openSettings();
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user