linux: improve disconnection behaviour (#97)

Merge pull request #97 from tim-gromeyer/linux-disconnect-behaviour
This commit is contained in:
Kavish Devar
2025-04-16 14:27:22 +05:30
committed by GitHub
6 changed files with 82 additions and 15 deletions

View File

@@ -21,11 +21,17 @@ class Battery : public QObject
public:
explicit Battery(QObject *parent = nullptr) : QObject(parent)
{
reset();
}
void reset()
{
// Initialize all components to unknown state
states[Component::Left] = {};
states[Component::Right] = {};
states[Component::Case] = {};
emit batteryStatusChanged();
}
// Enum for AirPods components

View File

@@ -359,18 +359,48 @@ private slots:
LOG_WARN("Socket is still open, closing it");
socket->close();
socket = nullptr;
discoveryAgent->start();
}
if (phoneSocket && phoneSocket->isOpen())
{
phoneSocket->write(AirPodsPackets::Connection::AIRPODS_DISCONNECTED);
LOG_DEBUG("AIRPODS_DISCONNECTED packet written: " << AirPodsPackets::Connection::AIRPODS_DISCONNECTED.toHex());
}
mediaController->pause(); // Since the device is deconnected, we don't know if it was the active output device. Pause to be safe
discoveryAgent->start();
// Show system notification
trayManager->showNotification(
tr("AirPods Disconnected"),
tr("Your AirPods have been disconnected"));
}
void bluezDeviceDisconnected(const QString &address)
{
if (address == connectedDeviceMacAddress.replace("_", ":")) {
if (address == connectedDeviceMacAddress.replace("_", ":"))
{
onDeviceDisconnected(QBluetoothAddress(address));
// Clear the device name and model
m_deviceName.clear();
m_model = AirPodsModel::Unknown;
emit deviceNameChanged(m_deviceName);
emit modelChanged();
// Reset battery status
m_battery->reset();
m_batteryStatus.clear();
emit batteryStatusChanged(m_batteryStatus);
// Reset ear detection
m_earDetectionStatus.clear();
m_primaryInEar = false;
m_secoundaryInEar = false;
emit earDetectionStatusChanged(m_earDetectionStatus);
emit primaryChanged();
// Reset noise control mode
m_noiseControlMode = NoiseControlMode::Off;
emit noiseControlModeChanged(m_noiseControlMode);
}
else {
LOG_WARN("Disconnected device does not match connected device: " << address << " != " << connectedDeviceMacAddress);

View File

@@ -39,9 +39,14 @@ void MediaController::initializeMprisInterface() {
}
void MediaController::handleEarDetection(const QString &status) {
bool primaryInEar = false;
bool secondaryInEar = false;
QStringList parts = status.split(", ");
bool primaryInEar = parts[0].contains("In Ear");
bool secondaryInEar = parts[1].contains("In Ear");
if (parts.size() == 2) {
primaryInEar = parts[0].contains("In Ear");
secondaryInEar = parts[1].contains("In Ear");
}
LOG_DEBUG("Ear detection status: primaryInEar="
<< primaryInEar << ", secondaryInEar=" << secondaryInEar
@@ -73,14 +78,7 @@ void MediaController::handleEarDetection(const QString &status) {
QString playbackStatus = process.readAllStandardOutput().trimmed();
LOG_DEBUG("Playback status: " << playbackStatus);
if (playbackStatus == "Playing") {
int result = QProcess::execute("playerctl", QStringList() << "pause");
LOG_DEBUG("Executed 'playerctl pause' with result: " << result);
if (result == 0) {
LOG_INFO("Paused playback via Playerctl");
wasPausedByApp = true;
} else {
LOG_ERROR("Failed to pause playback via Playerctl");
}
pause();
}
}
}
@@ -183,6 +181,20 @@ MediaController::MediaState MediaController::mediaStateFromPlayerctlOutput(
}
}
void MediaController::pause() {
int result = QProcess::execute("playerctl", QStringList() << "pause");
LOG_DEBUG("Executed 'playerctl pause' with result: " << result);
if (result == 0)
{
LOG_INFO("Paused playback via Playerctl");
wasPausedByApp = true;
}
else
{
LOG_ERROR("Failed to pause playback via Playerctl");
}
}
MediaController::~MediaController() {
if (playerctlProcess) {
playerctlProcess->terminate();

View File

@@ -23,6 +23,8 @@ public:
void removeAudioOutputDevice();
void setConnectedDeviceMacAddress(const QString &macAddress);
void pause();
Q_SIGNALS:
void mediaStateChanged(MediaState state);

View File

@@ -27,6 +27,11 @@ TrayIconManager::TrayIconManager(QObject *parent) : QObject(parent)
trayIcon->show();
}
void TrayIconManager::showNotification(const QString &title, const QString &message)
{
trayIcon->showMessage(title, message, QSystemTrayIcon::Information, 3000);
}
void TrayIconManager::TrayIconManager::updateBatteryStatus(const QString &status)
{
trayIcon->setToolTip("Battery Status: " + status);
@@ -83,10 +88,20 @@ void TrayIconManager::setupMenuActions()
void TrayIconManager::updateIconFromBattery(const QString &status)
{
QStringList parts = status.split(", ");
int leftLevel = parts[0].split(": ")[1].replace("%", "").toInt();
int rightLevel = parts[1].split(": ")[1].replace("%", "").toInt();
int leftLevel = 0;
int rightLevel = 0;
if (!status.isEmpty())
{
// Parse the battery status string
QStringList parts = status.split(", ");
if (parts.size() >= 2)
{
leftLevel = parts[0].split(": ")[1].replace("%", "").toInt();
rightLevel = parts[1].split(": ")[1].replace("%", "").toInt();
}
}
int minLevel = (leftLevel == 0) ? rightLevel : (rightLevel == 0) ? leftLevel
: qMin(leftLevel, rightLevel);

View File

@@ -20,6 +20,8 @@ public:
void updateConversationalAwareness(bool enabled);
void showNotification(const QString &title, const QString &message);
private slots:
void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason);