Commit 9022823a authored by Albert Vaca Cintora's avatar Albert Vaca Cintora

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.
parent af02ca7c
......@@ -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)
......@@ -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
......
......@@ -91,14 +91,14 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
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<QString>("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);
}
}
......
......@@ -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;
}
......@@ -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:
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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();
......
......@@ -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);
}
......
......@@ -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()
......
......@@ -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*);
};
......
......@@ -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);
}
......@@ -57,7 +57,7 @@ public:
}
template<typename T> 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; }
......
......@@ -20,6 +20,8 @@
#include "mpriscontrolpackageinterface.h"
#include "propertiesdbusinterface.h"
#include <QDebug>
#include <QDBusConnection>
#include <QDBusInterface>
......@@ -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,27 +64,65 @@ 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::removePlayer(const QString& ifaceName)
void MprisControlPackageInterface::propertiesChanged(const QString& propertyInterface, const QVariantMap& properties)
{
playerList.remove(playerList.key(ifaceName));
sendPlayerList();
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<QDBusArgument>(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::sendPlayerList()
void MprisControlPackageInterface::removePlayer(const QString& ifaceName)
{
NetworkPackage np(PACKAGE_TYPE_MPRIS);
np.set("playerList",playerList.keys());
sendPackage(np);
playerList.remove(playerList.key(ifaceName));
sendPlayerList();
}
bool MprisControlPackageInterface::receivePackage (const Device& device, const NetworkPackage& np)
......@@ -91,40 +131,71 @@ bool MprisControlPackageInterface::receivePackage (const Device& device, const N
if (np.type() != PACKAGE_TYPE_MPRIS) return false;
//Send the player list
const QString& player = np.get<QString>("player");
bool valid_player = playerList.contains(player);
if (!valid_player || np.get<bool>("requestPlayerList")) {
sendPlayerList(&device);
if (!valid_player) {
return true;
}
}
QString player = np.get<QString>("player");
if (!playerList.contains(player)) {
sendPlayerList();
return true;
//Do something to the mpris interface
OrgMprisMediaPlayer2PlayerInterface mprisInterface(playerList[player], "/org/mpris/MediaPlayer2", QDBusConnection::sessionBus());
if (np.has("action")) {
const QString& action = np.get<QString>("action");
qDebug() << "Calling action" << action << "in" << playerList[player];
//TODO: Check for valid actions
mprisInterface.call(action);
}
if (np.has("setVolume")) {
double volume = np.get<int>("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<bool>("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;
QDBusInterface mprisInterface(playerList[player], "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player");
if (np.get<bool>("requestPlayerList")) {
sendPlayerList();
}
if (np.get<bool>("requestVolume")) {
int volume = (int)(mprisInterface.volume() * 100);
answer.set("volume",volume);
somethingToSend = true;
QString action = np.get<QString>("action");
if (!action.isEmpty()) {
qDebug() << "Calling action" << action << "in" << playerList[player];
mprisInterface.call(action);
sendNowPlaying(mprisInterface);
} else if (np.get<bool>("requestNowPlaying")) {
sendNowPlaying(mprisInterface);
}
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);
}
......@@ -22,6 +22,7 @@
#define MPRISCONTROLPACKAGEINTERFACE_H
#include "packageinterface.h"
#include "mprisdbusinterface.h"
#include <QSet>
#include <QString>
......@@ -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<QString, QString> 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;
};
......
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.mpris.MediaPlayer2.Player">
<method name="Next"/>
<method name="Previous"/>
<method name="Pause"/>
<method name="PlayPause"/>
<method name="Stop"/>
<method name="Play"/>
<method name="Seek">
<arg direction="in" type="x" name="Offset"/>
</method>
<method name="SetPosition">
<arg direction="in" type="o" name="TrackId"/>
<arg direction="in" type="x" name="Position"/>
</method>
<method name="OpenUri">
<arg direction="in" type="s"/>
</method>
<!-- Signals -->
<signal name="Seeked">
<arg type="x" name="Position"/>
</signal>
<!-- Properties -->
<property access="read" type="s" name="PlaybackStatus"/>
<property access="readwrite" type="s" name="LoopStatus"/>
<property access="readwrite" type="d" name="Rate"/>
<property access="readwrite" type="b" name="Shuffle"/>
<property access="read" type="a{sv}" name="Metadata">
<annotation value="QVariantMap" name="com.trolltech.QtDBus.QtTypeName"/>
</property>
<property access="readwrite" type="d" name="Volume"/>
<property access="read" type="x" name="Position"/>
<property access="read" type="d" name="MinimumRate"/>
<property access="read" type="d" name="MaximumRate"/>
<property access="read" type="b" name="CanGoNext"/>
<property access="read" type="b" name="CanGoPrevious"/>
<property access="read" type="b" name="CanPlay"/>
<property access="read" type="b" name="CanPause"/>
<property access="read" type="b" name="CanSeek"/>
<property access="read" type="b" name="CanControl"/>
</interface>
</node>
<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.32.4 -->
<node>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="out"/>
</method>
<method name="Set">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="in"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface_name"/>
<arg type="a{sv}" name="changed_properties"/>
<arg type="as" name="invalidated_properties"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap" />
</signal>
</interface>
</node>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment