From 49f1261bf9d635d5e3d881e87a93ed4e76abfe90 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 30 Nov 2014 17:27:08 +0100 Subject: allow receiving responses to commands in watchconnector * the skeleton is in place for watchconnector to allow query->response messages. I've used call/cc style because it is impossible to make QBluetoothSocket synchronous (waitForReadyRead() is a no-op) * remove watchcommands, instead create musicmanager to listen for the music endpoint. The other (simpler) endpoints are now listened in watchconnector itself. hangupAll() slot is moved to voicecallmanager. * instead of emitting signals for each received message, listeners can now register for receiving messages targeted towards a given endpoint * when reading from bluetoothsocket, properly handle short reads * remove useless 'watch' namespace * create appmanager, which mantains a database of installed apps (installed on the phone, that is; watch installed apps will come later) * all the *Managers are now instantiated by the main Manager itself * introduce Unpacker helper class for decoding watch messages * implement getAppbankStatus and getAppbankUuids messages and response parsers * remove file logging for now (20MB is bad for eMMC!) * use dbus object path /org/pebbled instead of / --- daemon/musicmanager.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 daemon/musicmanager.cpp (limited to 'daemon/musicmanager.cpp') diff --git a/daemon/musicmanager.cpp b/daemon/musicmanager.cpp new file mode 100644 index 0000000..abea715 --- /dev/null +++ b/daemon/musicmanager.cpp @@ -0,0 +1,97 @@ +#include +#include "musicmanager.h" + +MusicManager::MusicManager(WatchConnector *watch, QObject *parent) + : QObject(parent), watch(watch) +{ + watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) { + musicControl(WatchConnector::MusicControl(data.at(0))); + return true; + }); +} + +void MusicManager::onMprisMetadataChanged(QVariantMap metadata) +{ + QString track = metadata.value("xesam:title").toString(); + QString album = metadata.value("xesam:album").toString(); + QString artist = metadata.value("xesam:artist").toString(); + logger()->debug() << __FUNCTION__ << track << album << artist; + watch->sendMusicNowPlaying(track, album, artist); +} + +void MusicManager::musicControl(WatchConnector::MusicControl operation) +{ + logger()->debug() << "Operation:" << operation; + + QString mpris = parent()->property("mpris").toString(); + if (mpris.isEmpty()) { + logger()->debug() << "No mpris interface active"; + return; + } + + QString method; + + switch(operation) { + case WatchConnector::musicPLAY_PAUSE: + method = "PlayPause"; + break; + case WatchConnector::musicPAUSE: + method = "Pause"; + break; + case WatchConnector::musicPLAY: + method = "Play"; + break; + case WatchConnector::musicNEXT: + method = "Next"; + break; + case WatchConnector::musicPREVIOUS: + method = "Previous"; + break; + case WatchConnector::musicVOLUME_UP: + case WatchConnector::musicVOLUME_DOWN: { + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusReply VolumeReply = bus.call( + QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") + << "org.mpris.MediaPlayer2.Player" << "Volume"); + if (VolumeReply.isValid()) { + double volume = VolumeReply.value().variant().toDouble(); + if (operation == WatchConnector::musicVOLUME_UP) { + volume += 0.1; + } + else { + volume -= 0.1; + } + logger()->debug() << "Setting volume" << volume; + QDBusError err = QDBusConnection::sessionBus().call( + QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set") + << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume))); + if (err.isValid()) { + logger()->error() << err.message(); + } + } else { + logger()->error() << VolumeReply.error().message(); + } + } + return; + case WatchConnector::musicGET_NOW_PLAYING: + onMprisMetadataChanged(parent()->property("mprisMetadata").toMap()); + return; + + case WatchConnector::musicSEND_NOW_PLAYING: + logger()->warn() << "Operation" << operation << "not supported"; + return; + } + + if (method.isEmpty()) { + logger()->error() << "Requested unsupported operation" << operation; + return; + } + + logger()->debug() << operation << "->" << method; + + QDBusError err = QDBusConnection::sessionBus().call( + QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method)); + if (err.isValid()) { + logger()->error() << err.message(); + } +} -- cgit v1.2.3 From 617c632f245c44151f0e17917f9e158403c444c6 Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 12 Dec 2014 02:33:03 +0100 Subject: move the mpris tracking into musicmanager --- daemon/dbusconnector.cpp | 24 ------- daemon/dbusconnector.h | 11 +--- daemon/manager.cpp | 69 -------------------- daemon/manager.h | 15 ----- daemon/musicmanager.cpp | 159 +++++++++++++++++++++++++++++++++++++++-------- daemon/musicmanager.h | 14 ++++- 6 files changed, 148 insertions(+), 144 deletions(-) (limited to 'daemon/musicmanager.cpp') diff --git a/daemon/dbusconnector.cpp b/daemon/dbusconnector.cpp index 8bde322..1f3ffc2 100644 --- a/daemon/dbusconnector.cpp +++ b/daemon/dbusconnector.cpp @@ -6,7 +6,6 @@ #include #include #include -#include //dbus-send --system --dest=org.bluez --print-reply / org.bluez.Manager.ListAdapters //dbus-send --system --dest=org.bluez --print-reply $path org.bluez.Adapter.GetProperties @@ -16,17 +15,6 @@ DBusConnector::DBusConnector(QObject *parent) : QObject(parent) { - QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); - - QDBusReply serviceNames = interface->registeredServiceNames(); - if (serviceNames.isValid()) { - dbusServices = serviceNames.value(); - } - else { - logger()->error() << serviceNames.error().message(); - } - connect(interface, SIGNAL(serviceRegistered(const QString &)), SLOT(onServiceRegistered(const QString &))); - connect(interface, SIGNAL(serviceUnregistered(const QString &)), SLOT(onServiceUnregistered(const QString &))); } bool DBusConnector::findPebble() @@ -82,15 +70,3 @@ bool DBusConnector::findPebble() return false; } - -void DBusConnector::onServiceRegistered(const QString &name) -{ - logger()->debug() << "DBus service online:" << name; - if (!dbusServices.contains(name)) dbusServices.append(name); -} - -void DBusConnector::onServiceUnregistered(const QString &name) -{ - logger()->debug() << "DBus service offline:" << name; - if (dbusServices.contains(name)) dbusServices.removeAll(name); -} diff --git a/daemon/dbusconnector.h b/daemon/dbusconnector.h index c24bb9b..7ed3d56 100644 --- a/daemon/dbusconnector.h +++ b/daemon/dbusconnector.h @@ -6,33 +6,26 @@ #include #include +// TODO Remove this. + class DBusConnector : public QObject { Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER Q_PROPERTY(QVariantMap pebble READ pebble NOTIFY pebbleChanged) - Q_PROPERTY(QStringList services READ services NOTIFY servicesChanged) - QVariantMap pebbleProps; - QStringList dbusServices; public: explicit DBusConnector(QObject *parent = 0); QVariantMap pebble() const { return pebbleProps; } - QStringList services() const { return dbusServices; } signals: void pebbleChanged(); - void servicesChanged(); public slots: bool findPebble(); - -protected slots: - void onServiceRegistered(const QString &); - void onServiceUnregistered(const QString &); }; #endif // DBUSCONNECTOR_H diff --git a/daemon/manager.cpp b/daemon/manager.cpp index dcf9c16..73b80e5 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -76,13 +76,6 @@ Manager::Manager(Settings *settings, QObject *parent) : defaultProfile = currentProfile.isEmpty() ? "ambience" : currentProfile; connect(watch, SIGNAL(connectedChanged()), SLOT(applyProfile())); - // Music Control interface - session.connect("", "/org/mpris/MediaPlayer2", - "org.freedesktop.DBus.Properties", "PropertiesChanged", - this, SLOT(onMprisPropertiesChanged(QString,QMap,QStringList))); - - connect(this, SIGNAL(mprisMetadataChanged(QVariantMap)), music, SLOT(onMprisMetadataChanged(QVariantMap))); - // Set BT icon for notification notification.setImage("icon-system-bluetooth-device"); @@ -131,22 +124,6 @@ void Manager::onConnectedChanged() if (!notification.publish()) { logger()->debug() << "Failed publishing notification"; } - - if (watch->isConnected()) { - QString mpris = this->mpris(); - if (not mpris.isEmpty()) { - QDBusReply Metadata = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") - << "org.mpris.MediaPlayer2.Player" << "Metadata"); - if (Metadata.isValid()) { - setMprisMetadata(Metadata.value().variant().value()); - } - else { - logger()->error() << Metadata.error().message(); - setMprisMetadata(QVariantMap()); - } - } - } } void Manager::onActiveVoiceCallChanged() @@ -279,52 +256,6 @@ void Manager::onEmailNotify(const QString &sender, const QString &data,const QSt watch->sendEmailNotification(sender, data, subject); } -void Manager::onMprisPropertiesChanged(QString interface, QMap changed, QStringList invalidated) -{ - logger()->debug() << interface << changed << invalidated; - - if (changed.contains("Metadata")) { - setMprisMetadata(changed.value("Metadata").value()); - } - - if (changed.contains("PlaybackStatus")) { - QString PlaybackStatus = changed.value("PlaybackStatus").toString(); - if (PlaybackStatus == "Stopped") { - setMprisMetadata(QVariantMap()); - } - } - - lastSeenMpris = message().service(); - logger()->debug() << "lastSeenMpris:" << lastSeenMpris; -} - -QString Manager::mpris() const -{ - const QStringList &services = dbus->services(); - if (not lastSeenMpris.isEmpty() && services.contains(lastSeenMpris)) - return lastSeenMpris; - - foreach (QString service, services) - if (service.startsWith("org.mpris.MediaPlayer2.")) - return service; - - return QString(); -} - -void Manager::setMprisMetadata(QDBusArgument metadata) -{ - if (metadata.currentType() == QDBusArgument::MapType) { - metadata >> mprisMetadata; - emit mprisMetadataChanged(mprisMetadata); - } -} - -void Manager::setMprisMetadata(QVariantMap metadata) -{ - mprisMetadata = metadata; - emit mprisMetadataChanged(mprisMetadata); -} - QString Manager::getCurrentProfile() const { QDBusReply profile = QDBusConnection::sessionBus().call( diff --git a/daemon/manager.h b/daemon/manager.h index ad0428b..c191b0c 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -35,9 +35,6 @@ class Manager : public QObject, protected QDBusContext friend class PebbledProxy; - Q_PROPERTY(QString mpris READ mpris) - Q_PROPERTY(QVariantMap mprisMetadata READ getMprisMetadata WRITE setMprisMetadata NOTIFY mprisMetadataChanged) - QBluetoothLocalDevice btDevice; Settings *settings; @@ -63,9 +60,6 @@ class Manager : public QObject, protected QDBusContext QString defaultProfile; - QString lastSeenMpris; - QVariantMap mprisMetadata; - QUuid currentAppUuid; QScopedPointer transliterator; @@ -76,16 +70,10 @@ public: QString findPersonByNumber(QString number); QString getCurrentProfile() const; - QString mpris() const; - - inline QVariantMap getMprisMetadata() const { return mprisMetadata; } protected: void transliterateMessage(const QString &text); -signals: - void mprisMetadataChanged(const QVariantMap &metadata); - public slots: void applyProfile(); @@ -102,9 +90,6 @@ private slots: void onTwitterNotify(const QString &sender, const QString &data); void onFacebookNotify(const QString &sender, const QString &data); void onEmailNotify(const QString &sender, const QString &data,const QString &subject); - void onMprisPropertiesChanged(QString,QMap,QStringList); - void setMprisMetadata(QDBusArgument metadata); - void setMprisMetadata(QVariantMap metadata); void onAppNotification(const QUuid &uuid, const QString &title, const QString &body); void onAppOpened(const QUuid &uuid); diff --git a/daemon/musicmanager.cpp b/daemon/musicmanager.cpp index abea715..e018e4c 100644 --- a/daemon/musicmanager.cpp +++ b/daemon/musicmanager.cpp @@ -4,28 +4,45 @@ MusicManager::MusicManager(WatchConnector *watch, QObject *parent) : QObject(parent), watch(watch) { + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusConnectionInterface *bus_iface = bus.interface(); + + // Listen for MPRIS signals from every player + bus.connect("", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged", + this, SLOT(handleMprisPropertiesChanged(QString,QMap,QStringList))); + + // Listen for D-Bus name registered signals to see if a MPRIS service comes up + connect(bus_iface, &QDBusConnectionInterface::serviceRegistered, + this, &MusicManager::handleServiceRegistered); + connect(bus_iface, &QDBusConnectionInterface::serviceUnregistered, + this, &MusicManager::handleServiceUnregistered); + connect(bus_iface, &QDBusConnectionInterface::serviceOwnerChanged, + this, &MusicManager::handleServiceOwnerChanged); + + // But also try to find an already active MPRIS service + const QStringList &services = bus_iface->registeredServiceNames(); + foreach (QString service, services) { + if (service.startsWith("org.mpris.MediaPlayer2.")) { + switchToService(service); + break; + } + } + + // Set up watch endpoint handler watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) { musicControl(WatchConnector::MusicControl(data.at(0))); return true; }); -} - -void MusicManager::onMprisMetadataChanged(QVariantMap metadata) -{ - QString track = metadata.value("xesam:title").toString(); - QString album = metadata.value("xesam:album").toString(); - QString artist = metadata.value("xesam:artist").toString(); - logger()->debug() << __FUNCTION__ << track << album << artist; - watch->sendMusicNowPlaying(track, album, artist); + connect(watch, &WatchConnector::connectedChanged, + this, &MusicManager::handleWatchConnected); } void MusicManager::musicControl(WatchConnector::MusicControl operation) { - logger()->debug() << "Operation:" << operation; + logger()->debug() << "operation from watch:" << operation; - QString mpris = parent()->property("mpris").toString(); - if (mpris.isEmpty()) { - logger()->debug() << "No mpris interface active"; + if (_curService.isEmpty()) { + logger()->info() << "No mpris interface active"; return; } @@ -50,11 +67,11 @@ void MusicManager::musicControl(WatchConnector::MusicControl operation) case WatchConnector::musicVOLUME_UP: case WatchConnector::musicVOLUME_DOWN: { QDBusConnection bus = QDBusConnection::sessionBus(); - QDBusReply VolumeReply = bus.call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") - << "org.mpris.MediaPlayer2.Player" << "Volume"); - if (VolumeReply.isValid()) { - double volume = VolumeReply.value().variant().toDouble(); + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + call << "org.mpris.MediaPlayer2.Player" << "Volume"; + QDBusReply volumeReply = bus.call(call); + if (volumeReply.isValid()) { + double volume = volumeReply.value().variant().toDouble(); if (operation == WatchConnector::musicVOLUME_UP) { volume += 0.1; } @@ -62,22 +79,25 @@ void MusicManager::musicControl(WatchConnector::MusicControl operation) volume -= 0.1; } logger()->debug() << "Setting volume" << volume; - QDBusError err = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set") - << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume))); + + call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set"); + call << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume)); + + QDBusError err = QDBusConnection::sessionBus().call(call); if (err.isValid()) { logger()->error() << err.message(); } } else { - logger()->error() << VolumeReply.error().message(); + logger()->error() << volumeReply.error().message(); } } return; case WatchConnector::musicGET_NOW_PLAYING: - onMprisMetadataChanged(parent()->property("mprisMetadata").toMap()); + setMprisMetadata(_curMetadata); return; case WatchConnector::musicSEND_NOW_PLAYING: + default: logger()->warn() << "Operation" << operation << "not supported"; return; } @@ -89,9 +109,98 @@ void MusicManager::musicControl(WatchConnector::MusicControl operation) logger()->debug() << operation << "->" << method; - QDBusError err = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method)); + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method); + QDBusError err = QDBusConnection::sessionBus().call(call); if (err.isValid()) { logger()->error() << err.message(); } } + +void MusicManager::switchToService(const QString &service) +{ + if (_curService != service) { + logger()->debug() << "switching to mpris service" << service; + _curService = service; + } +} + +void MusicManager::setMprisMetadata(const QVariantMap &metadata) +{ + _curMetadata = metadata; + QString track = metadata.value("xesam:title").toString(); + QString album = metadata.value("xesam:album").toString(); + QString artist = metadata.value("xesam:artist").toString(); + logger()->debug() << "new mpris metadata:" << track << album << artist; + + if (watch->isConnected()) { + watch->sendMusicNowPlaying(track, album, artist); + } +} + +void MusicManager::handleServiceRegistered(const QString &service) +{ + if (service.startsWith("org.mpris.MediaPlayer2.")) { + if (_curService.isEmpty()) { + switchToService(service); + } + } +} + +void MusicManager::handleServiceUnregistered(const QString &service) +{ + if (service == _curService) { + // Oops! + setMprisMetadata(QVariantMap()); + switchToService(QString()); + } +} + +void MusicManager::handleServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) +{ + Q_UNUSED(oldOwner); + if (newOwner.isEmpty()) { + handleServiceUnregistered(name); + } else { + handleServiceRegistered(name); + } +} + +void MusicManager::handleMprisPropertiesChanged(const QString &interface, const QMap &changed, const QStringList &invalidated) +{ + Q_ASSERT(calledFromDBus()); + Q_UNUSED(interface); + Q_UNUSED(invalidated); + + if (changed.contains("Metadata")) { + QVariantMap metadata = qdbus_cast(changed.value("Metadata").value()); + logger()->debug() << "received new metadata" << metadata; + setMprisMetadata(metadata); + } + + if (changed.contains("PlaybackStatus")) { + QString status = changed.value("PlaybackStatus").toString(); + if (status == "Stopped") { + setMprisMetadata(QVariantMap()); + } + } + + switchToService(message().service()); +} + +void MusicManager::handleWatchConnected() +{ + if (watch->isConnected()) { + if (!_curService.isEmpty()) { + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + call << "org.mpris.MediaPlayer2.Player" << "Metadata"; + QDBusReply metadata = QDBusConnection::sessionBus().call(call); + if (metadata.isValid()) { + setMprisMetadata(qdbus_cast(metadata.value().variant().value())); + // + } else { + logger()->error() << metadata.error().message(); + setMprisMetadata(QVariantMap()); + } + } + } +} diff --git a/daemon/musicmanager.h b/daemon/musicmanager.h index ca86ce3..88c46c3 100644 --- a/daemon/musicmanager.h +++ b/daemon/musicmanager.h @@ -2,9 +2,10 @@ #define MUSICMANAGER_H #include +#include #include "watchconnector.h" -class MusicManager : public QObject +class MusicManager : public QObject, protected QDBusContext { Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER @@ -14,12 +15,21 @@ public: private: void musicControl(WatchConnector::MusicControl operation); + void switchToService(const QString &service); + void setMprisMetadata(const QVariantMap &data); private slots: - void onMprisMetadataChanged(QVariantMap metadata); + void handleServiceRegistered(const QString &service); + void handleServiceUnregistered(const QString &service); + void handleServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void handleMprisPropertiesChanged(const QString &interface, const QMap &changed, const QStringList &invalidated); + void handleWatchConnected(); private: WatchConnector *watch; + + QVariantMap _curMetadata; + QString _curService; }; #endif // MUSICMANAGER_H -- cgit v1.2.3 From 75352f8cf5a60cfd291a26fe2c93d06281055f31 Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 12 Dec 2014 22:46:56 +0100 Subject: minor cleanup --- daemon/musicmanager.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'daemon/musicmanager.cpp') diff --git a/daemon/musicmanager.cpp b/daemon/musicmanager.cpp index e018e4c..05e3727 100644 --- a/daemon/musicmanager.cpp +++ b/daemon/musicmanager.cpp @@ -12,10 +12,6 @@ MusicManager::MusicManager(WatchConnector *watch, QObject *parent) this, SLOT(handleMprisPropertiesChanged(QString,QMap,QStringList))); // Listen for D-Bus name registered signals to see if a MPRIS service comes up - connect(bus_iface, &QDBusConnectionInterface::serviceRegistered, - this, &MusicManager::handleServiceRegistered); - connect(bus_iface, &QDBusConnectionInterface::serviceUnregistered, - this, &MusicManager::handleServiceUnregistered); connect(bus_iface, &QDBusConnectionInterface::serviceOwnerChanged, this, &MusicManager::handleServiceOwnerChanged); @@ -28,7 +24,7 @@ MusicManager::MusicManager(WatchConnector *watch, QObject *parent) } } - // Set up watch endpoint handler + // Set up watch endpoint handler for music control watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) { musicControl(WatchConnector::MusicControl(data.at(0))); return true; @@ -130,6 +126,7 @@ void MusicManager::setMprisMetadata(const QVariantMap &metadata) QString track = metadata.value("xesam:title").toString(); QString album = metadata.value("xesam:album").toString(); QString artist = metadata.value("xesam:artist").toString(); + logger()->debug() << "new mpris metadata:" << track << album << artist; if (watch->isConnected()) { @@ -149,7 +146,8 @@ void MusicManager::handleServiceRegistered(const QString &service) void MusicManager::handleServiceUnregistered(const QString &service) { if (service == _curService) { - // Oops! + // Oops! Losing the current MPRIS service + // We must assume it's been closed and thus remove current metadata setMprisMetadata(QVariantMap()); switchToService(QString()); } @@ -196,7 +194,6 @@ void MusicManager::handleWatchConnected() QDBusReply metadata = QDBusConnection::sessionBus().call(call); if (metadata.isValid()) { setMprisMetadata(qdbus_cast(metadata.value().variant().value())); - // } else { logger()->error() << metadata.error().message(); setMprisMetadata(QVariantMap()); -- cgit v1.2.3 From d0b0090e951668f9160632c5c30b9f1e0d0aa5a0 Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 13 Dec 2014 19:27:07 +0100 Subject: do not listen for every d-bus owner change rather, just update the current mpris service when a signal comes in; seems much more efficient. --- daemon/musicmanager.cpp | 217 ++++++++++++++++++++++++++---------------------- daemon/musicmanager.h | 15 ++-- daemon/watchconnector.h | 3 +- 3 files changed, 126 insertions(+), 109 deletions(-) (limited to 'daemon/musicmanager.cpp') diff --git a/daemon/musicmanager.cpp b/daemon/musicmanager.cpp index 05e3727..d34ae5c 100644 --- a/daemon/musicmanager.cpp +++ b/daemon/musicmanager.cpp @@ -1,69 +1,143 @@ -#include +#include +#include #include "musicmanager.h" MusicManager::MusicManager(WatchConnector *watch, QObject *parent) - : QObject(parent), watch(watch) + : QObject(parent), watch(watch), _watcher(new QDBusServiceWatcher(this)) { QDBusConnection bus = QDBusConnection::sessionBus(); QDBusConnectionInterface *bus_iface = bus.interface(); - // Listen for MPRIS signals from every player - bus.connect("", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged", - this, SLOT(handleMprisPropertiesChanged(QString,QMap,QStringList))); - - // Listen for D-Bus name registered signals to see if a MPRIS service comes up - connect(bus_iface, &QDBusConnectionInterface::serviceOwnerChanged, - this, &MusicManager::handleServiceOwnerChanged); + // This watcher will be used to find when the current MPRIS service dies + // (and thus we must clear the metadata) + _watcher->setConnection(bus); + connect(_watcher, &QDBusServiceWatcher::serviceOwnerChanged, + this, &MusicManager::handleMprisServiceOwnerChanged); - // But also try to find an already active MPRIS service + // Try to find an active MPRIS service to initially connect to const QStringList &services = bus_iface->registeredServiceNames(); foreach (QString service, services) { if (service.startsWith("org.mpris.MediaPlayer2.")) { switchToService(service); + fetchMetadataFromService(); + // The watch is not connected by this point, + // so we don't send the current metadata. break; } } - // Set up watch endpoint handler for music control + // Even if we didn't find any service, we still listen for metadataChanged signals + // from every MPRIS-compatible player + // If such a signal comes in, we will connect to the source service for that signal + bus.connect("", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged", + this, SLOT(handleMprisPropertiesChanged(QString,QMap,QStringList))); + + // Now set up the Pebble endpoint handler for music control commands watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) { - musicControl(WatchConnector::MusicControl(data.at(0))); + handleMusicControl(WatchConnector::MusicControl(data.at(0))); return true; }); + + // If the watch disconnects, we will send the current metadata when it comes back. connect(watch, &WatchConnector::connectedChanged, this, &MusicManager::handleWatchConnected); } -void MusicManager::musicControl(WatchConnector::MusicControl operation) +void MusicManager::switchToService(const QString &service) +{ + if (_curService != service) { + logger()->debug() << "switching to mpris service" << service; + _curService = service; + + if (_curService.isEmpty()) { + _watcher->setWatchedServices(QStringList()); + } else { + _watcher->setWatchedServices(QStringList(_curService)); + } + } +} + +void MusicManager::fetchMetadataFromService() +{ + _curMetadata.clear(); + + if (!_curService.isEmpty()) { + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + call << "org.mpris.MediaPlayer2.Player" << "Metadata"; + QDBusReply reply = QDBusConnection::sessionBus().call(call); + if (reply.isValid()) { + logger()->debug() << "got mpris metadata from service" << _curService; + _curMetadata = qdbus_cast(reply.value().variant().value()); + } else { + logger()->error() << reply.error().message(); + } + } +} + +void MusicManager::sendCurrentMprisMetadata() +{ + Q_ASSERT(watch->isConnected()); + + QString track = _curMetadata.value("xesam:title").toString().left(30); + QString album = _curMetadata.value("xesam:album").toString().left(30); + QString artist = _curMetadata.value("xesam:artist").toString().left(30); + + logger()->debug() << "sending mpris metadata:" << track << album << artist; + + watch->sendMusicNowPlaying(track, album, artist); +} + +void MusicManager::callMprisMethod(const QString &method) +{ + Q_ASSERT(!method.isEmpty()); + Q_ASSERT(!_curService.isEmpty()); + + logger()->debug() << _curService << "->" << method; + + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusMessage call = QDBusMessage::createMethodCall(_curService, + "/org/mpris/MediaPlayer2", + "org.mpris.MediaPlayer2.Player", + method); + + QDBusError err = bus.call(call); + + if (err.isValid()) { + logger()->error() << "while calling mpris method on" << _curService << ":" << err.message(); + } +} + +void MusicManager::handleMusicControl(WatchConnector::MusicControl operation) { logger()->debug() << "operation from watch:" << operation; if (_curService.isEmpty()) { - logger()->info() << "No mpris interface active"; + logger()->info() << "can't do any music operation, no mpris interface active"; return; } - QString method; - - switch(operation) { + switch (operation) { case WatchConnector::musicPLAY_PAUSE: - method = "PlayPause"; + callMprisMethod("PlayPause"); break; case WatchConnector::musicPAUSE: - method = "Pause"; + callMprisMethod("Pause"); break; case WatchConnector::musicPLAY: - method = "Play"; + callMprisMethod("Play"); break; case WatchConnector::musicNEXT: - method = "Next"; + callMprisMethod("Next"); break; case WatchConnector::musicPREVIOUS: - method = "Previous"; + callMprisMethod("Previous"); break; + case WatchConnector::musicVOLUME_UP: case WatchConnector::musicVOLUME_DOWN: { QDBusConnection bus = QDBusConnection::sessionBus(); - QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", + "org.freedesktop.DBus.Properties", "Get"); call << "org.mpris.MediaPlayer2.Player" << "Volume"; QDBusReply volumeReply = bus.call(call); if (volumeReply.isValid()) { @@ -87,79 +161,28 @@ void MusicManager::musicControl(WatchConnector::MusicControl operation) logger()->error() << volumeReply.error().message(); } } - return; + break; + case WatchConnector::musicGET_NOW_PLAYING: - setMprisMetadata(_curMetadata); - return; + sendCurrentMprisMetadata(); + break; - case WatchConnector::musicSEND_NOW_PLAYING: default: logger()->warn() << "Operation" << operation << "not supported"; - return; - } - - if (method.isEmpty()) { - logger()->error() << "Requested unsupported operation" << operation; - return; - } - - logger()->debug() << operation << "->" << method; - - QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method); - QDBusError err = QDBusConnection::sessionBus().call(call); - if (err.isValid()) { - logger()->error() << err.message(); - } -} - -void MusicManager::switchToService(const QString &service) -{ - if (_curService != service) { - logger()->debug() << "switching to mpris service" << service; - _curService = service; - } -} - -void MusicManager::setMprisMetadata(const QVariantMap &metadata) -{ - _curMetadata = metadata; - QString track = metadata.value("xesam:title").toString(); - QString album = metadata.value("xesam:album").toString(); - QString artist = metadata.value("xesam:artist").toString(); - - logger()->debug() << "new mpris metadata:" << track << album << artist; - - if (watch->isConnected()) { - watch->sendMusicNowPlaying(track, album, artist); - } -} - -void MusicManager::handleServiceRegistered(const QString &service) -{ - if (service.startsWith("org.mpris.MediaPlayer2.")) { - if (_curService.isEmpty()) { - switchToService(service); - } - } -} - -void MusicManager::handleServiceUnregistered(const QString &service) -{ - if (service == _curService) { - // Oops! Losing the current MPRIS service - // We must assume it's been closed and thus remove current metadata - setMprisMetadata(QVariantMap()); - switchToService(QString()); + break; } } -void MusicManager::handleServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) +void MusicManager::handleMprisServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner); - if (newOwner.isEmpty()) { - handleServiceUnregistered(name); - } else { - handleServiceRegistered(name); + if (name == _curService && newOwner.isEmpty()) { + // Oops, current service is going away + switchToService(QString()); + _curMetadata.clear(); + if (watch->isConnected()) { + sendCurrentMprisMetadata(); + } } } @@ -172,32 +195,26 @@ void MusicManager::handleMprisPropertiesChanged(const QString &interface, const if (changed.contains("Metadata")) { QVariantMap metadata = qdbus_cast(changed.value("Metadata").value()); logger()->debug() << "received new metadata" << metadata; - setMprisMetadata(metadata); + _curMetadata = metadata; } if (changed.contains("PlaybackStatus")) { QString status = changed.value("PlaybackStatus").toString(); if (status == "Stopped") { - setMprisMetadata(QVariantMap()); + _curMetadata.clear(); } } + if (watch->isConnected()) { + sendCurrentMprisMetadata(); + } + switchToService(message().service()); } void MusicManager::handleWatchConnected() { if (watch->isConnected()) { - if (!_curService.isEmpty()) { - QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); - call << "org.mpris.MediaPlayer2.Player" << "Metadata"; - QDBusReply metadata = QDBusConnection::sessionBus().call(call); - if (metadata.isValid()) { - setMprisMetadata(qdbus_cast(metadata.value().variant().value())); - } else { - logger()->error() << metadata.error().message(); - setMprisMetadata(QVariantMap()); - } - } + sendCurrentMprisMetadata(); } } diff --git a/daemon/musicmanager.h b/daemon/musicmanager.h index 88c46c3..89e5fd7 100644 --- a/daemon/musicmanager.h +++ b/daemon/musicmanager.h @@ -3,6 +3,7 @@ #include #include +#include #include "watchconnector.h" class MusicManager : public QObject, protected QDBusContext @@ -14,22 +15,22 @@ public: explicit MusicManager(WatchConnector *watch, QObject *parent = 0); private: - void musicControl(WatchConnector::MusicControl operation); void switchToService(const QString &service); - void setMprisMetadata(const QVariantMap &data); + void fetchMetadataFromService(); + void sendCurrentMprisMetadata(); + void callMprisMethod(const QString &method); private slots: - void handleServiceRegistered(const QString &service); - void handleServiceUnregistered(const QString &service); - void handleServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void handleMusicControl(WatchConnector::MusicControl operation); + void handleMprisServiceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner); void handleMprisPropertiesChanged(const QString &interface, const QMap &changed, const QStringList &invalidated); void handleWatchConnected(); private: WatchConnector *watch; - - QVariantMap _curMetadata; + QDBusServiceWatcher *_watcher; QString _curService; + QVariantMap _curMetadata; }; #endif // MUSICMANAGER_H diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index 4194e9a..6546302 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -94,8 +94,7 @@ public: musicPREVIOUS = 5, musicVOLUME_UP = 6, musicVOLUME_DOWN = 7, - musicGET_NOW_PLAYING = 8, - musicSEND_NOW_PLAYING = 9 + musicGET_NOW_PLAYING = 8 }; enum SystemMessage { systemFIRMWARE_AVAILABLE = 0, -- cgit v1.2.3