From 9022823aef8bcca9cda89fdf3a4c0c2433a7568d Mon Sep 17 00:00:00 2001 From: Albert Vaca Date: Sat, 10 Aug 2013 05:21:55 +0200 Subject: [PATCH] Improved MPRIS controls. MPRIS now uses xml dbus interfaces- MPRIS now detects when properties like volume or playbas status change. Link providers now emit connectionLost and connectionReceived, like in Android. Disabled connection notifications. Added some missing const modifiers. --- CMakeLists.txt | 3 + daemon/CMakeLists.txt | 12 ++ daemon/daemon.cpp | 15 +- daemon/device.cpp | 8 +- daemon/device.h | 2 +- daemon/devicelinks/devicelink.h | 4 +- daemon/devicelinks/echodevicelink.h | 4 +- daemon/devicelinks/tcpdevicelink.cpp | 2 +- daemon/devicelinks/tcpdevicelink.h | 2 +- daemon/linkproviders/avahitcplinkprovider.cpp | 8 +- .../broadcasttcplinkprovider.cpp | 16 +- daemon/linkproviders/linkprovider.h | 3 +- daemon/linkproviders/loopbacklinkprovider.cpp | 2 +- daemon/networkpackage.h | 2 +- .../mpriscontrolpackageinterface.cpp | 147 +++++++++++++----- .../mpriscontrolpackageinterface.h | 9 +- .../packageinterfaces/mprisdbusinterface.xml | 45 ++++++ .../packageinterfaces/propertiesInterface.xml | 23 +++ 18 files changed, 231 insertions(+), 76 deletions(-) create mode 100644 daemon/packageinterfaces/mprisdbusinterface.xml create mode 100644 daemon/packageinterfaces/propertiesInterface.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f9bca933..2f61e83b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,10 @@ find_package(KDE4 REQUIRED) include(KDE4Defaults) include_directories(${KDE4_INCLUDES}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(daemon) add_subdirectory(kcm) +#add_subdirectory(kioslave) + add_subdirectory(test) diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 9ae8f2747..40f1e1812 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -23,6 +23,18 @@ set(kded_kdeconnect_SRCS device.cpp ) + +qt4_add_dbus_interface( + kded_kdeconnect_SRCS + packageinterfaces/mprisdbusinterface.xml + mprisdbusinterface +) + +qt4_add_dbus_interface( + kded_kdeconnect_SRCS + packageinterfaces/propertiesInterface.xml + propertiesdbusinterface +) kde4_add_plugin(kded_kdeconnect ${kded_kdeconnect_SRCS}) target_link_libraries(kded_kdeconnect diff --git a/daemon/daemon.cpp b/daemon/daemon.cpp index 0839eded9..375dd8bd2 100644 --- a/daemon/daemon.cpp +++ b/daemon/daemon.cpp @@ -91,14 +91,14 @@ Daemon::Daemon(QObject *parent, const QList&) device,SLOT(sendPackage(const NetworkPackage&))); } } - + QNetworkSession* network = new QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()); //Listen to incomming connections Q_FOREACH (LinkProvider* a, mLinkProviders) { connect(network, SIGNAL(stateChanged(QNetworkSession::State)), a, SLOT(onNetworkChange(QNetworkSession::State))); - connect(a,SIGNAL(onNewDeviceLink(NetworkPackage,DeviceLink*)), + connect(a,SIGNAL(onConnectionReceived(NetworkPackage,DeviceLink*)), this,SLOT(onNewDeviceLink(NetworkPackage,DeviceLink*))); } @@ -130,7 +130,6 @@ QStringList Daemon::devices() return mDevices.keys(); } - void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* dl) { const QString& id = identityPackage.get("deviceId"); @@ -143,16 +142,16 @@ void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* Device* device = mDevices[id]; device->addLink(dl); - if (device->paired()) { + /*if (device->paired()) { KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent notification->setPixmap(KIcon("dialog-ok").pixmap(48, 48)); notification->setComponentData(KComponentData("kdeconnect", "kdeconnect")); notification->setTitle(device->name()); notification->setText("Succesfully connected"); notification->sendEvent(); - } + }*/ - emit deviceStatusChanged(id); + Q_EMIT deviceStatusChanged(id); } else { qDebug() << "It is a new device"; @@ -164,10 +163,8 @@ void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* Q_FOREACH (PackageInterface* pr, mPackageInterfaces) { connect(device,SIGNAL(receivedPackage(const Device&, const NetworkPackage&)), pr,SLOT(receivePackage(const Device&, const NetworkPackage&))); - connect(pr,SIGNAL(sendPackage(const NetworkPackage&)), - device,SLOT(sendPackage(const NetworkPackage&))); } - emit newDeviceAdded(id); + Q_EMIT newDeviceAdded(id); } } diff --git a/daemon/device.cpp b/daemon/device.cpp index 14e27a5aa..f8869693d 100644 --- a/daemon/device.cpp +++ b/daemon/device.cpp @@ -74,6 +74,10 @@ void Device::addLink(DeviceLink* link) connect(link,SIGNAL(destroyed(QObject*)),this,SLOT(linkDestroyed(QObject*))); m_deviceLinks.append(link); + + //TODO: Somehow destroy previous device links from the same provider, + //but if we do it here, the provider will keep a broken ref! + connect(link, SIGNAL(receivedPackage(NetworkPackage)), this, SLOT(privateReceivedPackage(NetworkPackage))); qSort(m_deviceLinks.begin(),m_deviceLinks.end(),lessThan); @@ -100,7 +104,7 @@ void Device::removeLink(DeviceLink* link) } } -bool Device::sendPackage(const NetworkPackage& np) +bool Device::sendPackage(const NetworkPackage& np) const { Q_FOREACH(DeviceLink* dl, m_deviceLinks) { if (dl->sendPackage(np)) return true; @@ -136,5 +140,3 @@ void Device::sendPing() qDebug() << "sendPing:" << success; } - - diff --git a/daemon/device.h b/daemon/device.h index 77b9c5b03..74ec9d373 100644 --- a/daemon/device.h +++ b/daemon/device.h @@ -61,7 +61,7 @@ public: Q_SIGNALS: void receivedPackage(const Device& device, const NetworkPackage& np); public Q_SLOTS: - bool sendPackage(const NetworkPackage& np); + bool sendPackage(const NetworkPackage& np) const; //Public dbus operations public Q_SLOTS: diff --git a/daemon/devicelinks/devicelink.h b/daemon/devicelinks/devicelink.h index f1c0a577c..a8b31258e 100644 --- a/daemon/devicelinks/devicelink.h +++ b/daemon/devicelinks/devicelink.h @@ -39,10 +39,10 @@ public: const QString& deviceId() { return mDeviceId; } LinkProvider* provider() { return mLinkProvider; } - virtual bool sendPackage(const NetworkPackage& np) = 0; + virtual bool sendPackage(const NetworkPackage& np) const = 0; signals: - void receivedPackage(const NetworkPackage& np); + void receivedPackage(const NetworkPackage& np) const; private: QString mDeviceId; diff --git a/daemon/devicelinks/echodevicelink.h b/daemon/devicelinks/echodevicelink.h index 7e83a18f5..8edc2a7d3 100644 --- a/daemon/devicelinks/echodevicelink.h +++ b/daemon/devicelinks/echodevicelink.h @@ -31,8 +31,8 @@ class EchoDeviceLink public: EchoDeviceLink(const QString& d, LoopbackLinkProvider* a); - bool sendPackage(const NetworkPackage& np) { - emit receivedPackage(np); + bool sendPackage(const NetworkPackage& np) const { + Q_EMIT receivedPackage(np); return true; } diff --git a/daemon/devicelinks/tcpdevicelink.cpp b/daemon/devicelinks/tcpdevicelink.cpp index 29d07595d..913f283e3 100644 --- a/daemon/devicelinks/tcpdevicelink.cpp +++ b/daemon/devicelinks/tcpdevicelink.cpp @@ -29,7 +29,7 @@ TcpDeviceLink::TcpDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* sock connect(mSocket, SIGNAL(readyRead()), this, SLOT(dataReceived())); } -bool TcpDeviceLink::sendPackage(const NetworkPackage& np) +bool TcpDeviceLink::sendPackage(const NetworkPackage& np) const { int written = mSocket->write(np.serialize()); return written != -1; diff --git a/daemon/devicelinks/tcpdevicelink.h b/daemon/devicelinks/tcpdevicelink.h index f39b9de2a..2fbc2d26d 100644 --- a/daemon/devicelinks/tcpdevicelink.h +++ b/daemon/devicelinks/tcpdevicelink.h @@ -38,7 +38,7 @@ class TcpDeviceLink public: TcpDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* socket); - bool sendPackage(const NetworkPackage& np); + bool sendPackage(const NetworkPackage& np) const; private Q_SLOTS: void dataReceived(); diff --git a/daemon/linkproviders/avahitcplinkprovider.cpp b/daemon/linkproviders/avahitcplinkprovider.cpp index f17333cc0..0f7f7b67f 100644 --- a/daemon/linkproviders/avahitcplinkprovider.cpp +++ b/daemon/linkproviders/avahitcplinkprovider.cpp @@ -101,7 +101,7 @@ void AvahiTcpLinkProvider::dataReceived() qDebug() << "AvahiTcpLinkProvider creating link to device" << id << "(" << socket->peerAddress() << ")"; - emit onNewDeviceLink(np, dl); + Q_EMIT onConnectionReceived(np, dl); disconnect(socket,SIGNAL(readyRead()),this,SLOT(dataReceived())); @@ -111,9 +111,11 @@ void AvahiTcpLinkProvider::dataReceived() } -void AvahiTcpLinkProvider::deviceLinkDestroyed(QObject* deviceLink) +void AvahiTcpLinkProvider::deviceLinkDestroyed(QObject* uncastedDeviceLink) { - const QString& id = ((DeviceLink*)deviceLink)->deviceId(); + DeviceLink* deviceLink = (DeviceLink*)uncastedDeviceLink; + Q_EMIT onConnectionLost(deviceLink); + const QString& id = deviceLink->deviceId(); if (links.contains(id)) links.remove(id); } diff --git a/daemon/linkproviders/broadcasttcplinkprovider.cpp b/daemon/linkproviders/broadcasttcplinkprovider.cpp index 5d6a6d333..688c4af3d 100644 --- a/daemon/linkproviders/broadcasttcplinkprovider.cpp +++ b/daemon/linkproviders/broadcasttcplinkprovider.cpp @@ -156,7 +156,7 @@ void BroadcastTcpLinkProvider::connected() //TODO: Use reverse connection too to preffer connecting a unstable device (a phone) to a stable device (a computer) if (success) { qDebug() << "Handshaking done (i'm the existing device)"; - emit onNewDeviceLink(*np, dl); + Q_EMIT onConnectionReceived(*np, dl); } else { //I think this will never happen qDebug() << "Fallback (2), try reverse connection"; @@ -218,7 +218,7 @@ void BroadcastTcpLinkProvider::dataReceived() qDebug() << "Handshaking done (i'm the new device)"; - emit onNewDeviceLink(np, dl); + Q_EMIT onConnectionReceived(np, dl); disconnect(socket,SIGNAL(readyRead()),this,SLOT(dataReceived())); @@ -228,14 +228,12 @@ void BroadcastTcpLinkProvider::dataReceived() } -void BroadcastTcpLinkProvider::deviceLinkDestroyed(QObject* deviceLink) +void BroadcastTcpLinkProvider::deviceLinkDestroyed(QObject* uncastedDeviceLink) { - const QString& id = ((DeviceLink*)deviceLink)->deviceId(); - qDebug() << "deviceLinkDestroyed"; - if (links.contains(id)) { - qDebug() << "removing link from link list"; - links.remove(id); - } + DeviceLink* deviceLink = (DeviceLink*)uncastedDeviceLink; + Q_EMIT onConnectionLost(deviceLink); + const QString& id = deviceLink->deviceId(); + if (links.contains(id)) links.remove(id); } BroadcastTcpLinkProvider::~BroadcastTcpLinkProvider() diff --git a/daemon/linkproviders/linkprovider.h b/daemon/linkproviders/linkprovider.h index 54ae50f29..792662bae 100644 --- a/daemon/linkproviders/linkprovider.h +++ b/daemon/linkproviders/linkprovider.h @@ -55,7 +55,8 @@ public Q_SLOTS: Q_SIGNALS: //NOTE: The provider will to destroy the DeviceLink when it's no longer accessible, // and every user should listen to the destroyed signal to remove its references. - void onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink*); + void onConnectionReceived(const NetworkPackage& identityPackage, DeviceLink*); + void onConnectionLost(DeviceLink*); }; diff --git a/daemon/linkproviders/loopbacklinkprovider.cpp b/daemon/linkproviders/loopbacklinkprovider.cpp index bb476f055..34369c83e 100644 --- a/daemon/linkproviders/loopbacklinkprovider.cpp +++ b/daemon/linkproviders/loopbacklinkprovider.cpp @@ -38,6 +38,6 @@ LoopbackLinkProvider::~LoopbackLinkProvider() void LoopbackLinkProvider::setDiscoverable(bool b) { qDebug() << "Echo Device discovery emitted"; - if (b) emit onNewDeviceLink(identityPackage, echoDeviceLink); + if (b) Q_EMIT onConnectionReceived(identityPackage, echoDeviceLink); } diff --git a/daemon/networkpackage.h b/daemon/networkpackage.h index 6b65cd16e..7ed7c8441 100644 --- a/daemon/networkpackage.h +++ b/daemon/networkpackage.h @@ -57,7 +57,7 @@ public: } template void set(const QString& key, const T& value) { mBody[key] = QVariant(value); } - bool has(const QString& key) { return mBody.contains(key); } + bool has(const QString& key) const { return mBody.contains(key); } private: void setId(long id) { mId = id; } diff --git a/daemon/packageinterfaces/mpriscontrolpackageinterface.cpp b/daemon/packageinterfaces/mpriscontrolpackageinterface.cpp index 976416274..22df27d29 100644 --- a/daemon/packageinterfaces/mpriscontrolpackageinterface.cpp +++ b/daemon/packageinterfaces/mpriscontrolpackageinterface.cpp @@ -20,6 +20,8 @@ #include "mpriscontrolpackageinterface.h" +#include "propertiesdbusinterface.h" + #include #include #include @@ -52,7 +54,7 @@ void MprisControlPackageInterface::serviceOwnerChanged(const QString &name, if (name.startsWith("org.mpris.MediaPlayer2")) { - qDebug() << "Something registered in bus" << name << oldOwner << newOwner; + qDebug() << "Something (un)registered in bus" << name << oldOwner << newOwner; if (oldOwner.isEmpty()) { addPlayer(name); @@ -62,14 +64,59 @@ void MprisControlPackageInterface::serviceOwnerChanged(const QString &name, } } -void MprisControlPackageInterface::addPlayer(const QString& ifaceName) +void MprisControlPackageInterface::addPlayer(const QString& service) { - QDBusInterface interface(ifaceName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2"); - //TODO: Make this async - QString identity = interface.property("Identity").toString(); - playerList[identity] = ifaceName; - qDebug() << "addPlayer" << ifaceName << identity; + QDBusInterface mprisInterface(service, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2"); + const QString& identity = mprisInterface.property("Identity").toString(); + playerList[identity] = service; + qDebug() << "addPlayer" << service << identity; sendPlayerList(); + + OrgFreedesktopDBusPropertiesInterface* freedesktopInterface = new OrgFreedesktopDBusPropertiesInterface(service, "/org/mpris/MediaPlayer2", QDBusConnection::sessionBus(), this); + connect(freedesktopInterface, SIGNAL(PropertiesChanged(QString, QVariantMap, QStringList)), this, SLOT(propertiesChanged(QString, QVariantMap))); + +} + +void MprisControlPackageInterface::propertiesChanged(const QString& propertyInterface, const QVariantMap& properties) +{ + + NetworkPackage np(PACKAGE_TYPE_MPRIS); + bool somethingToSend = false; + if (properties.contains("Volume")) { + int volume = (int) (properties["Volume"].toDouble()*100); + if (volume != prevVolume) { + np.set("volume",volume); + prevVolume = volume; + somethingToSend = true; + } + } + if (properties.contains("Metadata")) { + QDBusArgument bullshit = qvariant_cast(properties["Metadata"]); + QVariantMap nowPlayingMap; + bullshit >> nowPlayingMap; + if (nowPlayingMap.contains("xesam:title")) { + QString nowPlaying = nowPlayingMap["xesam:title"].toString(); + if (nowPlayingMap.contains("xesam:artist")) { + nowPlaying = nowPlayingMap["xesam:artist"].toString() + " - " + nowPlaying; + } + np.set("nowPlaying",nowPlaying); + somethingToSend = true; + } + + } + if (properties.contains("PlaybackStatus")) { + bool playing = (properties["PlaybackStatus"].toString() == "Playing"); + np.set("isPlaying", playing); + somethingToSend = true; + } + + if (somethingToSend) { + OrgFreedesktopDBusPropertiesInterface* interface = (OrgFreedesktopDBusPropertiesInterface*)sender(); + const QString& service = interface->service(); + const QString& player = playerList.key(service); + np.set("player", player); + sendPackage(np); + } } void MprisControlPackageInterface::removePlayer(const QString& ifaceName) @@ -78,53 +125,77 @@ void MprisControlPackageInterface::removePlayer(const QString& ifaceName) sendPlayerList(); } -void MprisControlPackageInterface::sendPlayerList() -{ - NetworkPackage np(PACKAGE_TYPE_MPRIS); - np.set("playerList",playerList.keys()); - sendPackage(np); -} - bool MprisControlPackageInterface::receivePackage (const Device& device, const NetworkPackage& np) { Q_UNUSED(device); if (np.type() != PACKAGE_TYPE_MPRIS) return false; - - QString player = np.get("player"); - if (!playerList.contains(player)) { - sendPlayerList(); - return true; + //Send the player list + const QString& player = np.get("player"); + bool valid_player = playerList.contains(player); + if (!valid_player || np.get("requestPlayerList")) { + sendPlayerList(&device); + if (!valid_player) { + return true; + } } - QDBusInterface mprisInterface(playerList[player], "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player"); - - if (np.get("requestPlayerList")) { - sendPlayerList(); - } - - QString action = np.get("action"); - if (!action.isEmpty()) { + //Do something to the mpris interface + OrgMprisMediaPlayer2PlayerInterface mprisInterface(playerList[player], "/org/mpris/MediaPlayer2", QDBusConnection::sessionBus()); + if (np.has("action")) { + const QString& action = np.get("action"); qDebug() << "Calling action" << action << "in" << playerList[player]; + //TODO: Check for valid actions mprisInterface.call(action); - sendNowPlaying(mprisInterface); - } else if (np.get("requestNowPlaying")) { - sendNowPlaying(mprisInterface); + } + if (np.has("setVolume")) { + double volume = np.get("setVolume")/100.f; + qDebug() << "Setting volume" << volume << "to" << playerList[player]; + mprisInterface.setVolume(volume); + } + + //Send something read from the mpris interface + NetworkPackage answer(PACKAGE_TYPE_MPRIS); + bool somethingToSend = false; + if (np.get("requestNowPlaying")) { + + QVariantMap nowPlayingMap = mprisInterface.metadata(); + QString nowPlaying = nowPlayingMap["xesam:title"].toString(); + if (nowPlayingMap.contains("xesam:artist")) { + nowPlaying = nowPlayingMap["xesam:artist"].toString() + " - " + nowPlaying; + } + + answer.set("nowPlaying",nowPlaying); + + + + bool playing = (mprisInterface.playbackStatus() == QLatin1String("Playing")); + answer.set("isPlaying", playing); + + somethingToSend = true; + + + } + if (np.get("requestVolume")) { + int volume = (int)(mprisInterface.volume() * 100); + answer.set("volume",volume); + somethingToSend = true; + + } + if (somethingToSend) { + answer.set("player", player); + device.sendPackage(answer); } return true; } -void MprisControlPackageInterface::sendNowPlaying(const QDBusInterface& mprisInterface) +void MprisControlPackageInterface::sendPlayerList(const Device* device) { - QVariantMap nowPlayingMap = mprisInterface.property("Metadata").toMap(); - QString nowPlaying = nowPlayingMap["xesam:title"].toString(); - if (nowPlayingMap.contains("xesam:artist")) { - nowPlaying = nowPlayingMap["xesam:artist"].toString() + " - " + nowPlaying; - } NetworkPackage np(PACKAGE_TYPE_MPRIS); - np.set("nowPlaying",nowPlaying); - sendPackage(np); + np.set("playerList",playerList.keys()); + if (device == NULL) Q_EMIT sendPackage(np); + else device->sendPackage(np); } diff --git a/daemon/packageinterfaces/mpriscontrolpackageinterface.h b/daemon/packageinterfaces/mpriscontrolpackageinterface.h index 1f1ebe12a..51604b8c5 100644 --- a/daemon/packageinterfaces/mpriscontrolpackageinterface.h +++ b/daemon/packageinterfaces/mpriscontrolpackageinterface.h @@ -22,6 +22,7 @@ #define MPRISCONTROLPACKAGEINTERFACE_H #include "packageinterface.h" +#include "mprisdbusinterface.h" #include #include @@ -37,16 +38,16 @@ public: MprisControlPackageInterface(); virtual bool receivePackage(const Device& device, const NetworkPackage& np); -public Q_SLOTS: +private Q_SLOTS: void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void propertiesChanged(const QString& interface, const QVariantMap& properties); private: QHash playerList; void addPlayer(const QString& ifaceName); void removePlayer(const QString& ifaceName); - void sendPlayerList(); - void sendNowPlaying(const QDBusInterface& interface); - + void sendPlayerList(const Device* device = 0); + int prevVolume; }; diff --git a/daemon/packageinterfaces/mprisdbusinterface.xml b/daemon/packageinterfaces/mprisdbusinterface.xml new file mode 100644 index 000000000..2aaa851f9 --- /dev/null +++ b/daemon/packageinterfaces/mprisdbusinterface.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/daemon/packageinterfaces/propertiesInterface.xml b/daemon/packageinterfaces/propertiesInterface.xml new file mode 100644 index 000000000..d783a73f4 --- /dev/null +++ b/daemon/packageinterfaces/propertiesInterface.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + +