mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-02-10 19:52:24 +00:00
[Linux] Allow setting ear detection behaviour
This commit is contained in:
committed by
Tim Gromeyer
parent
c2db0afdf1
commit
0846c3eb48
@@ -34,7 +34,8 @@ public:
|
|||||||
AirPodsTrayApp(bool debugMode)
|
AirPodsTrayApp(bool debugMode)
|
||||||
: debugMode(debugMode)
|
: debugMode(debugMode)
|
||||||
, m_battery(new Battery(this))
|
, m_battery(new Battery(this))
|
||||||
, monitor(new BluetoothMonitor(this)) {
|
, monitor(new BluetoothMonitor(this))
|
||||||
|
, m_settings(new QSettings("AirPodsTrayApp", "AirPodsTrayApp")){
|
||||||
if (debugMode) {
|
if (debugMode) {
|
||||||
QLoggingCategory::setFilterRules("airpodsApp.debug=true");
|
QLoggingCategory::setFilterRules("airpodsApp.debug=true");
|
||||||
} else {
|
} else {
|
||||||
@@ -85,6 +86,7 @@ public:
|
|||||||
|
|
||||||
~AirPodsTrayApp() {
|
~AirPodsTrayApp() {
|
||||||
saveCrossDeviceEnabled();
|
saveCrossDeviceEnabled();
|
||||||
|
saveEarDetectionSettings();
|
||||||
|
|
||||||
delete trayIcon;
|
delete trayIcon;
|
||||||
delete trayMenu;
|
delete trayMenu;
|
||||||
@@ -265,18 +267,11 @@ public slots:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool loadCrossDeviceEnabled()
|
bool loadCrossDeviceEnabled() { return m_settings->value("crossdevice/enabled", false).toBool(); }
|
||||||
{
|
void saveCrossDeviceEnabled() { m_settings->setValue("crossdevice/enabled", CrossDevice.isEnabled); }
|
||||||
QSettings settings;
|
|
||||||
return settings.value("crossdevice/enabled", false).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveCrossDeviceEnabled()
|
int loadEarDetectionSettings() { return m_settings->value("earDetection/setting", MediaController::EarDetectionBehavior::PauseWhenOneRemoved).toInt(); }
|
||||||
{
|
void saveEarDetectionSettings() { m_settings->setValue("earDetection/setting", mediaController->getEarDetectionBehavior()); }
|
||||||
QSettings settings;
|
|
||||||
settings.setValue("crossdevice/enabled", CrossDevice.isEnabled);
|
|
||||||
settings.sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onTrayIconActivated()
|
void onTrayIconActivated()
|
||||||
@@ -836,7 +831,7 @@ private:
|
|||||||
MediaController* mediaController;
|
MediaController* mediaController;
|
||||||
TrayIconManager *trayManager;
|
TrayIconManager *trayManager;
|
||||||
BluetoothMonitor *monitor;
|
BluetoothMonitor *monitor;
|
||||||
QSettings *settings;
|
QSettings *m_settings;
|
||||||
|
|
||||||
QString m_batteryStatus;
|
QString m_batteryStatus;
|
||||||
QString m_earDetectionStatus;
|
QString m_earDetectionStatus;
|
||||||
|
|||||||
@@ -38,12 +38,20 @@ void MediaController::initializeMprisInterface() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaController::handleEarDetection(const QString &status) {
|
void MediaController::handleEarDetection(const QString &status)
|
||||||
|
{
|
||||||
|
if (earDetectionBehavior == Disabled)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("Ear detection is disabled, ignoring status");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool primaryInEar = false;
|
bool primaryInEar = false;
|
||||||
bool secondaryInEar = false;
|
bool secondaryInEar = false;
|
||||||
|
|
||||||
QStringList parts = status.split(", ");
|
QStringList parts = status.split(", ");
|
||||||
if (parts.size() == 2) {
|
if (parts.size() == 2)
|
||||||
|
{
|
||||||
primaryInEar = parts[0].contains("In Ear");
|
primaryInEar = parts[0].contains("In Ear");
|
||||||
secondaryInEar = parts[1].contains("In Ear");
|
secondaryInEar = parts[1].contains("In Ear");
|
||||||
}
|
}
|
||||||
@@ -51,37 +59,68 @@ void MediaController::handleEarDetection(const QString &status) {
|
|||||||
LOG_DEBUG("Ear detection status: primaryInEar="
|
LOG_DEBUG("Ear detection status: primaryInEar="
|
||||||
<< primaryInEar << ", secondaryInEar=" << secondaryInEar
|
<< primaryInEar << ", secondaryInEar=" << secondaryInEar
|
||||||
<< ", isAirPodsActive=" << isActiveOutputDeviceAirPods());
|
<< ", isAirPodsActive=" << isActiveOutputDeviceAirPods());
|
||||||
if (primaryInEar || secondaryInEar) {
|
|
||||||
LOG_INFO("At least one AirPod is in ear");
|
// First handle playback pausing based on selected behavior
|
||||||
activateA2dpProfile();
|
bool shouldPause = false;
|
||||||
} else {
|
bool shouldResume = false;
|
||||||
LOG_INFO("Both AirPods are out of ear");
|
|
||||||
removeAudioOutputDevice();
|
if (earDetectionBehavior == PauseWhenOneRemoved)
|
||||||
|
{
|
||||||
|
shouldPause = !primaryInEar || !secondaryInEar;
|
||||||
|
shouldResume = primaryInEar && secondaryInEar;
|
||||||
|
}
|
||||||
|
else if (earDetectionBehavior == PauseWhenBothRemoved)
|
||||||
|
{
|
||||||
|
shouldPause = !primaryInEar && !secondaryInEar;
|
||||||
|
shouldResume = primaryInEar || secondaryInEar;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primaryInEar && secondaryInEar) {
|
if (shouldPause && isActiveOutputDeviceAirPods())
|
||||||
if (wasPausedByApp && isActiveOutputDeviceAirPods()) {
|
{
|
||||||
|
QProcess process;
|
||||||
|
process.start("playerctl", QStringList() << "status");
|
||||||
|
process.waitForFinished();
|
||||||
|
QString playbackStatus = process.readAllStandardOutput().trimmed();
|
||||||
|
LOG_DEBUG("Playback status: " << playbackStatus);
|
||||||
|
if (playbackStatus == "Playing")
|
||||||
|
{
|
||||||
|
pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then handle device profile switching
|
||||||
|
if (primaryInEar || secondaryInEar)
|
||||||
|
{
|
||||||
|
LOG_INFO("At least one AirPod is in ear");
|
||||||
|
activateA2dpProfile();
|
||||||
|
|
||||||
|
// Resume if conditions are met and we previously paused
|
||||||
|
if (shouldResume && wasPausedByApp && isActiveOutputDeviceAirPods())
|
||||||
|
{
|
||||||
int result = QProcess::execute("playerctl", QStringList() << "play");
|
int result = QProcess::execute("playerctl", QStringList() << "play");
|
||||||
LOG_DEBUG("Executed 'playerctl play' with result: " << result);
|
LOG_DEBUG("Executed 'playerctl play' with result: " << result);
|
||||||
if (result == 0) {
|
if (result == 0)
|
||||||
|
{
|
||||||
LOG_INFO("Resumed playback via Playerctl");
|
LOG_INFO("Resumed playback via Playerctl");
|
||||||
wasPausedByApp = false;
|
wasPausedByApp = false;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
LOG_ERROR("Failed to resume playback via Playerctl");
|
LOG_ERROR("Failed to resume playback via Playerctl");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (isActiveOutputDeviceAirPods()) {
|
|
||||||
QProcess process;
|
|
||||||
process.start("playerctl", QStringList() << "status");
|
|
||||||
process.waitForFinished();
|
|
||||||
QString playbackStatus = process.readAllStandardOutput().trimmed();
|
|
||||||
LOG_DEBUG("Playback status: " << playbackStatus);
|
|
||||||
if (playbackStatus == "Playing") {
|
|
||||||
pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_INFO("Both AirPods are out of ear");
|
||||||
|
removeAudioOutputDevice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaController::setEarDetectionBehavior(EarDetectionBehavior behavior)
|
||||||
|
{
|
||||||
|
earDetectionBehavior = behavior;
|
||||||
|
LOG_INFO("Set ear detection behavior to: " << behavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaController::followMediaChanges() {
|
void MediaController::followMediaChanges() {
|
||||||
|
|||||||
@@ -6,14 +6,28 @@
|
|||||||
|
|
||||||
class QProcess;
|
class QProcess;
|
||||||
|
|
||||||
class MediaController : public QObject {
|
class MediaController : public QObject
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum MediaState { Playing, Paused, Stopped };
|
enum MediaState
|
||||||
|
{
|
||||||
|
Playing,
|
||||||
|
Paused,
|
||||||
|
Stopped
|
||||||
|
};
|
||||||
|
Q_ENUM(MediaState)
|
||||||
|
enum EarDetectionBehavior
|
||||||
|
{
|
||||||
|
PauseWhenOneRemoved,
|
||||||
|
PauseWhenBothRemoved,
|
||||||
|
Disabled
|
||||||
|
};
|
||||||
|
Q_ENUM(EarDetectionBehavior)
|
||||||
|
|
||||||
explicit MediaController(QObject *parent = nullptr);
|
explicit MediaController(QObject *parent = nullptr);
|
||||||
~MediaController();
|
~MediaController();
|
||||||
|
|
||||||
void initializeMprisInterface();
|
void initializeMprisInterface();
|
||||||
void handleEarDetection(const QString &status);
|
void handleEarDetection(const QString &status);
|
||||||
void followMediaChanges();
|
void followMediaChanges();
|
||||||
@@ -23,6 +37,9 @@ public:
|
|||||||
void removeAudioOutputDevice();
|
void removeAudioOutputDevice();
|
||||||
void setConnectedDeviceMacAddress(const QString &macAddress);
|
void setConnectedDeviceMacAddress(const QString &macAddress);
|
||||||
|
|
||||||
|
void setEarDetectionBehavior(EarDetectionBehavior behavior);
|
||||||
|
inline EarDetectionBehavior getEarDetectionBehavior() const { return earDetectionBehavior; }
|
||||||
|
|
||||||
void pause();
|
void pause();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
@@ -36,6 +53,7 @@ private:
|
|||||||
bool wasPausedByApp = false;
|
bool wasPausedByApp = false;
|
||||||
int initialVolume = -1;
|
int initialVolume = -1;
|
||||||
QString connectedDeviceMacAddress;
|
QString connectedDeviceMacAddress;
|
||||||
|
EarDetectionBehavior earDetectionBehavior = PauseWhenOneRemoved;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MEDIACONTROLLER_H
|
#endif // MEDIACONTROLLER_H
|
||||||
Reference in New Issue
Block a user