Commit acadeef7 authored by Albert Vaca Cintora's avatar Albert Vaca Cintora

Rewritten old package emitters and receivers using the new package format

Splitted up ping receiver from notification receiver in KDE
Improved pausemusic receiver
Fixed same computer being discovered multiple times
Fixed some other minor bugs and compilation warnings
parent fa3e0145
......@@ -3,6 +3,10 @@ cmake_minimum_required(VERSION 2.6)
find_package(KDE4 REQUIRED)
if(CMAKE_COMPILER_IS_GNUCXX)
add_definitions(-std=gnu++0x)
endif()
include(KDE4Defaults)
include_directories(${KDE4_INCLUDES})
......
......@@ -9,8 +9,9 @@ set(kded_kdeconnect_SRCS
devicelinks/devicelink.cpp
packagereceivers/packagereceiver.cpp
packagereceivers/pausemusicpackagereceiver.cpp
packagereceivers/pingpackagereceiver.cpp
packagereceivers/notificationpackagereceiver.cpp
packagereceivers/pausemusicpackagereceiver.cpp
networkpackage.cpp
daemon.cpp
......
......@@ -49,6 +49,8 @@ public:
virtual void setDiscoverable(bool b) = 0;
signals:
//NOTE: The announcer has to destroy the DeviceLink when it's no longer accessible,
// and every user should listen to the destroy signal to remove its references.
void onNewDeviceLink(const QString& id, const QString& name, DeviceLink*);
signals:
......
......@@ -51,9 +51,8 @@ void AvahiAnnouncer::readPendingNotifications()
QByteArray datagram;
datagram.resize(mUdpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
mUdpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
NetAddress sender;
mUdpSocket->readDatagram(datagram.data(), datagram.size(), &(sender.ip), &(sender.port));
//log.write(datagram);
qDebug() << "AvahiAnnouncer incomming udp datagram: " << datagram;
......@@ -68,8 +67,11 @@ void AvahiAnnouncer::readPendingNotifications()
qDebug() << "AvahiAnnouncer creating link to device" << id;
DeviceLink* dl = new UdpDeviceLink(id, this, sender, 10600);
links.append(dl);
DeviceLink* dl = new UdpDeviceLink(id, this, sender.ip, 10600);
if (links.contains(sender)) delete links[sender]; //Delete old link if we already know it, probably it is down if this happens.
links[sender] = dl;
emit onNewDeviceLink(id, name, dl);
} else {
......
......@@ -28,6 +28,7 @@
#include <KDE/DNSSD/PublicService>
#include "announcer.h"
#include "netaddress.h"
class AvahiAnnouncer
: public Announcer
......@@ -50,10 +51,7 @@ private:
DNSSD::PublicService* service;
QUdpSocket* mUdpSocket;
QVector<DeviceLink*> links;
QHostAddress mIp;
quint16 mPort;
QMap<NetAddress, DeviceLink*> links;
};
......
......@@ -20,6 +20,7 @@
#include "daemon.h"
#include "networkpackage.h"
#include "packagereceivers/pingpackagereceiver.h"
#include "packagereceivers/notificationpackagereceiver.h"
#include "packagereceivers/pausemusicpackagereceiver.h"
#include "announcers/avahiannouncer.h"
......@@ -52,6 +53,7 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
//TODO: Do not hardcode the load of the package receivers
//use: https://techbase.kde.org/Development/Tutorials/Services/Plugins
packageReceivers.push_back(new PingPackageReceiver());
packageReceivers.push_back(new NotificationPackageReceiver());
packageReceivers.push_back(new PauseMusicPackageReceiver());
......@@ -72,8 +74,8 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
Device* device = new Device(id,name);
m_devices[id] = device;
Q_FOREACH (PackageReceiver* pr, packageReceivers) {
connect(device,SIGNAL(receivedPackage(const NetworkPackage&)),
pr,SLOT(receivePackage(const NetworkPackage&)));
connect(device,SIGNAL(receivedPackage(const Device&, const NetworkPackage&)),
pr,SLOT(receivePackage(const Device&, const NetworkPackage&)));
}
}
......@@ -82,7 +84,7 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
connect(a,SIGNAL(onNewDeviceLink(QString,QString,DeviceLink*)),
this,SLOT(onNewDeviceLink(QString,QString,DeviceLink*)));
}
QDBusConnection::sessionBus().registerService("org.kde.kdeconnect");
setDiscoveryEnabled(true);
......@@ -109,6 +111,8 @@ void Daemon::onNewDeviceLink(const QString& id, const QString& name, DeviceLink*
qDebug() << "Device discovered" << dl->deviceId();
if (m_devices.contains(dl->deviceId())) {
qDebug() << "It is a known device";
Device* device = m_devices[dl->deviceId()];
device->addLink(dl);
......@@ -117,42 +121,23 @@ void Daemon::onNewDeviceLink(const QString& id, const QString& name, DeviceLink*
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
notification->setTitle(device->name());
notification->setText("Succesfully connected");
notification->sendEvent();
emit deviceStatusChanged(id);
} else {
qDebug() << "It is a new device";
Device* device = new Device(id,name,dl);
m_devices[dl->deviceId()] = device;
Q_FOREACH (PackageReceiver* pr, packageReceivers) {
connect(device,SIGNAL(receivedPackage(const NetworkPackage&)),
pr,SLOT(receivePackage(const NetworkPackage&)));
connect(device,SIGNAL(receivedPackage(const Device&, const NetworkPackage&)),
pr,SLOT(receivePackage(const Device&, const NetworkPackage&)));
}
emit newDeviceAdded(id);
}
}
void Daemon::onLostDeviceLink(DeviceLink* dl)
{
qDebug() << "Device lost" << dl->deviceId();
if (m_devices.contains(dl->deviceId())) {
Device* device = m_devices[dl->deviceId()];
device->removeLink(dl);
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("Disconnected");
} else {
qDebug() << "Lost unknown device, this should not happen (?)";
}
}
Daemon::~Daemon()
{
qDebug() << "SAYONARA BABY";
......
......@@ -69,9 +69,7 @@ Q_SIGNALS:
private Q_SLOTS:
void onNewDeviceLink(const QString& id, const QString& name, DeviceLink* dl);
void onLostDeviceLink(DeviceLink* dl);
private:
......
......@@ -63,19 +63,25 @@ static bool lessThan(DeviceLink* p1, DeviceLink* p2)
void Device::addLink(DeviceLink* link)
{
Q_FOREACH(DeviceLink* existing, m_deviceLinks) {
//Do not add duplicate links
if (existing->announcer() == link->announcer()) return;
}
qDebug() << "AddLink";
connect(link,SIGNAL(destroyed(QObject*)),this,SLOT(linkDestroyed(QObject*)));
m_deviceLinks.append(link);
connect(link, SIGNAL(receivedPackage(NetworkPackage)), this, SLOT(privateReceivedPackage(NetworkPackage)));
qSort(m_deviceLinks.begin(),m_deviceLinks.end(),lessThan);
}
void Device::linkDestroyed(QObject* o)
{
removeLink(static_cast<DeviceLink*>(o));
}
void Device::removeLink(DeviceLink* link)
{
qDebug() << "RemoveLink";
disconnect(link, SIGNAL(receivedPackage(NetworkPackage)), this, SLOT(privateReceivedPackage(NetworkPackage)));
m_deviceLinks.remove(m_deviceLinks.indexOf(link));
m_deviceLinks.removeOne(link);
}
bool Device::sendPackage(const NetworkPackage& np)
......@@ -90,7 +96,7 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
m_deviceName = np.get<QString>("deviceName");
} else if (m_paired) {
qDebug() << "package received from paired device";
emit receivedPackage(np);
emit receivedPackage(*this, np);
} else {
qDebug() << "not paired, ignoring package";
}
......
......@@ -46,15 +46,17 @@ public:
//(not supported yet, do we need it or we can rely on the device presenging itself?)
//Device(const QString& id, DeviceLink* dl);
//Add and remove links
void addLink(DeviceLink*);
void removeLink(DeviceLink*);
//Send and receive
bool sendPackage(const NetworkPackage& np);
Q_SIGNALS:
void receivedPackage(const NetworkPackage& np);
void receivedPackage(const Device& device, const NetworkPackage& np);
public Q_SLOTS:
//Public dbus interface
Q_SCRIPTABLE QString id() const{ return m_deviceId; }
Q_SCRIPTABLE QString name() const { return m_deviceName; }
Q_SCRIPTABLE bool paired() const { return m_paired; }
......@@ -63,13 +65,14 @@ public Q_SLOTS:
Q_SCRIPTABLE void sendPing();
private Q_SLOTS:
void linkDestroyed(QObject* o = 0);
void privateReceivedPackage(const NetworkPackage& np);
private:
bool m_paired;
QString m_deviceId;
QString m_deviceName;
QVector<DeviceLink*> m_deviceLinks;
QList<DeviceLink*> m_deviceLinks;
bool m_knownIdentiy;
......
/**
* Copyright 2013 Albert Vaca <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NETADDRESS_H
#define NETADDRESS_H
#include <QHostAddress>
struct NetAddress {
NetAddress() { }
NetAddress(QHostAddress _ip, quint16 _port) : ip(_ip), port(_port) { }
QHostAddress ip;
quint16 port;
};
inline bool operator< (const NetAddress& a, const NetAddress& b){
return (a.ip.toString()+a.port) < (b.ip.toString()+b.port);
}
#endif // NETADDRESS_H
......@@ -71,7 +71,6 @@ void NetworkPackage::unserialize(QByteArray a, NetworkPackage* np)
qDebug() << "Warning: package version " << np->version() << " greater than supported version " << CURRENT_PACKAGE_VERSION;
}
//QVariant -> Object
//NetworkPackage np;
//QJSon json(a);
......
......@@ -21,6 +21,7 @@
#ifndef NETWORKPACKAGE_H
#define NETWORKPACKAGE_H
#include "networkpackagetypes.h"
#include <QObject>
#include <QString>
#include <QVariant>
......
/**
* Copyright 2013 Albert Vaca <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NETWORKPACKAGETYPES_H
#define NETWORKPACKAGETYPES_H
#define PACKAGE_TYPE_IDENTITY QString("kdeconnect.identity")
#define PACKAGE_TYPE_PING QString("kdeconnect.ping")
#define PACKAGE_TYPE_NOTIFICATION QString("kdeconnect.notification")
#define PACKAGE_TYPE_CALL QString("kdeconnect.call")
#endif // NETWORKPACKAGETYPES_H
......@@ -20,70 +20,74 @@
#include "notificationpackagereceiver.h"
#include <QDebug>
#include <kicon.h>
KNotification* NotificationPackageReceiver::createNotification(const NetworkPackage& np) {
KNotification* NotificationPackageReceiver::createNotification(const QString& deviceName, const NetworkPackage& np) {
QString title, type, icon;
QString npType = np.get<QString>("notificationType");
if (np.type() == "RINGING") {
title = "Incoming call";
QString title, content, type, icon;
title = deviceName;
if (npType == "ringing") {
type = "callReceived";
icon = "call-start";
} else if (np.type() == "MISSED") {
title = "Missed call";
type = "callMissed";
content = "Incoming call from " + np.get<QString>("phoneNumber");
} else if (npType == "missedCall") {
type = "missedCall";
icon = "call-start";
} else if (np.type() == "SMS") {
title = "SMS Received";
content = "Missed call from " + np.get<QString>("phoneNumber");
} else if (npType == "sms") {
type = "smsReceived";
icon = "mail-receive";
} else if (np.type() == "BATTERY") {
title = "Battery status";
content = "SMS received from " + np.get<QString>("phoneNumber");
} else if (npType == "battery") {
type = "battery100";
icon = "battery-100"; // Here we need to take all different cases into account. All
// possible steps on battery charge level and state (discharging
// or charging)
} else if (np.type() == "NOTIFY") {
title = "Notification";
type = "pingReceived";
icon = "dialog-ok";
} else if (np.type() == "PING") {
title = "Ping!";
icon = "battery-100";
content = "Battery at " + np.get<QString>("batteryLevel") + "%";
} else if (npType == "notification") {
type = "pingReceived";
icon = "dialog-ok";
content = np.get<QString>("notificationContent");
} else {
//TODO: return if !debug
title = "Unknown";
//TODO: return NULL if !debug
type = "unknownEvent";
icon = "pda";
content = "Unknown notification type: " + npType;
}
qDebug() << "Creating notification with type:" << type;
KNotification* notification = new KNotification(type); //KNotification::Persistent
notification->setPixmap(KIcon(icon).pixmap(48, 48));
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
notification->setTitle(title);
notification->setText(np.get<QString>(QString("content")));
notification->setText(content);
return notification;
}
bool NotificationPackageReceiver::receivePackage(const NetworkPackage& np) {
bool NotificationPackageReceiver::receivePackage(const Device& device, const NetworkPackage& np) {
//if (np.get("isCancel")) {
qDebug() << "LOLOLO" << np.serialize();
if (np.type() != PACKAGE_TYPE_NOTIFICATION) return false;
if (np.get<bool>("isCancel")) {
//It would be awesome to remove the old notification from the system tray here, but there is no way to do it :(
//Now I realize why at the end of the day I have hundreds of notifications from facebook messages that I HAVE ALREADY READ,
//...it's just because the telepathy client has no way to remove them! even when it knows that I have read those messages lol
//...it's just because the telepathy client has no way to remove them! even when it knows that I have read those messages!
//} else {
} else {
KNotification* n = createNotification(np);
n->sendEvent();
KNotification* n = createNotification(device.name(), np);
if (n != NULL) n->sendEvent();
//}
}
return true;
......
......@@ -30,10 +30,10 @@ class NotificationPackageReceiver
{
public:
virtual bool receivePackage(const NetworkPackage& np);
virtual bool receivePackage(const Device&, const NetworkPackage& np);
private:
static KNotification* createNotification(const NetworkPackage& np);
static KNotification* createNotification(const QString& deviceName,const NetworkPackage& np);
};
......
......@@ -24,6 +24,7 @@
#include <QObject>
#include "networkpackage.h"
#include "device.h"
class PackageReceiver
: public QObject
......@@ -36,7 +37,7 @@ public:
public Q_SLOTS:
//Returns true if it has handled the package in some way
virtual bool receivePackage(const NetworkPackage& np) = 0;
virtual bool receivePackage(const Device& device, const NetworkPackage& np) = 0;
};
#endif // PACKAGERECEIVER_H
......@@ -20,25 +20,49 @@
#include "pausemusicpackagereceiver.h"
#include <QDebug>
PauseMusicPackageReceiver::PauseMusicPackageReceiver()
{
pauseOnlyAfterAnswering = false;
//TODO: Be able to change this from settings
pauseWhen = PauseWhenTalking;
paused = false;
}
bool PauseMusicPackageReceiver::receivePackage ( const NetworkPackage& np )
bool PauseMusicPackageReceiver::receivePackage (const Device& device, const NetworkPackage& np)
{
Q_UNUSED(device);
bool pauseConditionFulfilled = false;
if (np.get<QString>("eventType","") != "ring") return false; //TODO: Consider pauseOnlyAfterAnswering
//TODO: I have manually tested it and it works for both cases, but I should somehow write a test for this logic
if (pauseWhen == PauseWhenRinging) {
if (np.type() == PACKAGE_TYPE_NOTIFICATION) {
if (np.get<QString>("notificationType") != "ringing") return false;
pauseConditionFulfilled = !np.get<bool>("isCancel");
} else if (np.type() == PACKAGE_TYPE_CALL) {
pauseConditionFulfilled = !np.get<bool>("isCancel");
} else {
return false;
}
} else if (pauseWhen == PauseWhenTalking){
if (np.type() != PACKAGE_TYPE_CALL) return false;
pauseConditionFulfilled = !np.get<bool>("isCancel");
}
qDebug() << "PauseMusicPackageReceiver - PauseCondition:" << pauseConditionFulfilled;
//TODO: Use KDE DBUS API
if (np.get<QString>("eventDetails") == "hang") {
system("qdbus org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Play");
} else {
if (pauseConditionFulfilled && !paused) {
//TODO: Use KDE DBUS API
system("qdbus org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Pause");
} if (!pauseConditionFulfilled && paused) {
//FIXME: Play does not work, using PlayPause
system("qdbus org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlayPause");
}
paused = pauseConditionFulfilled;
return true;
}
......@@ -28,10 +28,12 @@ class PauseMusicPackageReceiver
{
public:
PauseMusicPackageReceiver();
virtual bool receivePackage(const NetworkPackage& np);
virtual bool receivePackage(const Device& device, const NetworkPackage& np);
private:
bool pauseOnlyAfterAnswering; //if set to false it will pause when ringing too
enum PauseCondtions { PauseWhenTalking, PauseWhenRinging, NeverPause };
PauseCondtions pauseWhen;
bool paused;
};
......
/**
* Copyright 2013 Albert Vaca <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pingpackagereceiver.h"
#include <QDebug>
#include <kicon.h>
bool PingPackageReceiver::receivePackage(const Device& device, const NetworkPackage& np) {
qDebug() << np.type();
if (np.type() != PACKAGE_TYPE_PING) return false;
KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent
notification->setPixmap(KIcon("dialog-ok").pixmap(48, 48));
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
notification->setTitle("Ping!");
notification->setText(device.name());
notification->sendEvent();
return true;
}
/**
* Copyright 2013 Albert Vaca <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or