Expose pairing state for devices

Will allow to have information about whether we're pairing, mostly for
better GUI.

Pair-programmed with Albert Vaca
This commit is contained in:
Aleix Pol 2017-01-25 00:22:22 +01:00
parent 339ce7beea
commit ea41d3786e
9 changed files with 99 additions and 25 deletions

View file

@ -60,9 +60,11 @@ public:
virtual bool linkShouldBeKeptAlive() { return false; } virtual bool linkShouldBeKeptAlive() { return false; }
Q_SIGNALS: Q_SIGNALS:
void receivedPackage(const NetworkPackage& np); void pairingRequest(PairingHandler* handler);
void pairingRequestExpired(PairingHandler* handler);
void pairStatusChanged(DeviceLink::PairStatus status); void pairStatusChanged(DeviceLink::PairStatus status);
void pairingError(const QString &error); void pairingError(const QString &error);
void receivedPackage(const NetworkPackage& np);
protected: protected:
QCA::PrivateKey mPrivateKey; QCA::PrivateKey mPrivateKey;

View file

@ -38,8 +38,6 @@ LanPairingHandler::LanPairingHandler(DeviceLink* deviceLink)
void LanPairingHandler::packageReceived(const NetworkPackage& np) void LanPairingHandler::packageReceived(const NetworkPackage& np)
{ {
m_pairingTimeout.stop();
bool wantsPair = np.get<bool>(QStringLiteral("pair")); bool wantsPair = np.get<bool>(QStringLiteral("pair"));
if (wantsPair) { if (wantsPair) {
@ -57,7 +55,6 @@ void LanPairingHandler::packageReceived(const NetworkPackage& np)
return; return;
} }
Daemon::instance()->askPairingConfirmation(this);
setInternalPairStatus(RequestedByPeer); setInternalPairStatus(RequestedByPeer);
} }
@ -93,14 +90,12 @@ bool LanPairingHandler::requestPairing()
const bool success = deviceLink()->sendPackage(np); const bool success = deviceLink()->sendPackage(np);
if (success) { if (success) {
setInternalPairStatus(Requested); setInternalPairStatus(Requested);
m_pairingTimeout.start();
} }
return success; return success;
} }
bool LanPairingHandler::acceptPairing() bool LanPairingHandler::acceptPairing()
{ {
m_pairingTimeout.stop(); // Just in case it is started
NetworkPackage np(PACKAGE_TYPE_PAIR, {{"pair", true}}); NetworkPackage np(PACKAGE_TYPE_PAIR, {{"pair", true}});
bool success = deviceLink()->sendPackage(np); bool success = deviceLink()->sendPackage(np);
if (success) { if (success) {
@ -132,6 +127,18 @@ void LanPairingHandler::pairingTimeout()
void LanPairingHandler::setInternalPairStatus(LanPairingHandler::InternalPairStatus status) void LanPairingHandler::setInternalPairStatus(LanPairingHandler::InternalPairStatus status)
{ {
if (status == Requested || status == RequestedByPeer) {
m_pairingTimeout.start();
} else {
m_pairingTimeout.stop();
}
if (m_status == RequestedByPeer && (status == NotPaired || status == Paired)) {
Q_EMIT deviceLink()->pairingRequestExpired(this);
} else if (status == RequestedByPeer) {
Q_EMIT deviceLink()->pairingRequest(this);
}
m_status = status; m_status = status;
if (status == Paired) { if (status == Paired) {
deviceLink()->setPairStatus(DeviceLink::Paired); deviceLink()->setPairStatus(DeviceLink::Paired);

View file

@ -49,7 +49,7 @@ public:
virtual void packageReceived(const NetworkPackage& np) = 0; virtual void packageReceived(const NetworkPackage& np) = 0;
virtual void unpair() = 0; virtual void unpair() = 0;
virtual int pairingTimeoutMsec() const { return 30 * 1000; } // 30 seconds of timeout (default), subclasses that use different values should override static int pairingTimeoutMsec() { return 30 * 1000; } // 30 seconds of timeout (default), subclasses that use different values should override
public Q_SLOTS: public Q_SLOTS:
virtual bool requestPairing() = 0; virtual bool requestPairing() = 0;

View file

@ -70,11 +70,7 @@ Daemon::Daemon(QObject *parent, bool testMode)
//Read remebered paired devices //Read remebered paired devices
const QStringList& list = KdeConnectConfig::instance()->trustedDevices(); const QStringList& list = KdeConnectConfig::instance()->trustedDevices();
Q_FOREACH (const QString& id, list) { Q_FOREACH (const QString& id, list) {
Device* device = new Device(this, id); addDevice(new Device(this, id));
connect(device, &Device::reachableChanged, this, &Daemon::onDeviceStatusChanged);
connect(device, &Device::trustedChanged, this, &Daemon::onDeviceStatusChanged);
d->mDevices[id] = device;
Q_EMIT deviceAdded(id);
} }
//Listen to new devices //Listen to new devices
@ -185,11 +181,7 @@ void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink*
if (!isDiscoveringDevices() && !device->isTrusted() && !dl->linkShouldBeKeptAlive()) { if (!isDiscoveringDevices() && !device->isTrusted() && !dl->linkShouldBeKeptAlive()) {
device->deleteLater(); device->deleteLater();
} else { } else {
connect(device, &Device::reachableChanged, this, &Daemon::onDeviceStatusChanged); addDevice(device);
connect(device, &Device::trustedChanged, this, &Daemon::onDeviceStatusChanged);
d->mDevices[id] = device;
Q_EMIT deviceAdded(id);
} }
} }
} }
@ -250,6 +242,28 @@ QString Daemon::deviceIdByName(const QString &name) const
return {}; return {};
} }
void Daemon::addDevice(Device* device)
{
const QString id = device->id();
connect(device, &Device::reachableChanged, this, &Daemon::onDeviceStatusChanged);
connect(device, &Device::trustedChanged, this, &Daemon::onDeviceStatusChanged);
connect(device, &Device::pairingRequestsChanged, this, &Daemon::pairingRequestsChanged);
connect(device, &Device::pairingRequestsChanged, this, [this, device]() { askPairingConfirmation(device); } );
d->mDevices[id] = device;
Q_EMIT deviceAdded(id);
}
QStringList Daemon::pairingRequests() const
{
QStringList ret;
for(Device* dev: d->mDevices) {
if (dev->hasPairingRequests())
ret += dev->id();
}
return ret;
}
Daemon::~Daemon() Daemon::~Daemon()
{ {

View file

@ -39,6 +39,7 @@ class KDECONNECTCORE_EXPORT Daemon
Q_OBJECT Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.daemon") Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.daemon")
Q_PROPERTY(bool isDiscoveringDevices READ isDiscoveringDevices) Q_PROPERTY(bool isDiscoveringDevices READ isDiscoveringDevices)
Q_PROPERTY(QStringList pairingRequests READ pairingRequests NOTIFY pairingRequestsChanged)
public: public:
explicit Daemon(QObject *parent, bool testMode = false); explicit Daemon(QObject *parent, bool testMode = false);
@ -48,12 +49,14 @@ public:
QList<Device*> devicesList() const; QList<Device*> devicesList() const;
virtual void askPairingConfirmation(PairingHandler *d) = 0; virtual void askPairingConfirmation(Device *device) = 0;
virtual void reportError(const QString &title, const QString &description) = 0; virtual void reportError(const QString &title, const QString &description) = 0;
virtual QNetworkAccessManager* networkAccessManager(); virtual QNetworkAccessManager* networkAccessManager();
Device* getDevice(const QString& deviceId); Device* getDevice(const QString& deviceId);
QStringList pairingRequests() const;
public Q_SLOTS: public Q_SLOTS:
Q_SCRIPTABLE void acquireDiscoveryMode(const QString &id); Q_SCRIPTABLE void acquireDiscoveryMode(const QString &id);
Q_SCRIPTABLE void releaseDiscoveryMode(const QString &id); Q_SCRIPTABLE void releaseDiscoveryMode(const QString &id);
@ -74,12 +77,14 @@ Q_SIGNALS:
Q_SCRIPTABLE void deviceRemoved(const QString& id); //Note that paired devices will never be removed Q_SCRIPTABLE void deviceRemoved(const QString& id); //Note that paired devices will never be removed
Q_SCRIPTABLE void deviceVisibilityChanged(const QString& id, bool isVisible); Q_SCRIPTABLE void deviceVisibilityChanged(const QString& id, bool isVisible);
Q_SCRIPTABLE void announcedNameChanged(const QString &announcedName); Q_SCRIPTABLE void announcedNameChanged(const QString &announcedName);
Q_SCRIPTABLE void pairingRequestsChanged();
private Q_SLOTS: private Q_SLOTS:
void onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* dl); void onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* dl);
void onDeviceStatusChanged(); void onDeviceStatusChanged();
private: private:
void addDevice(Device* device);
bool isDiscoveringDevices() const; bool isDiscoveringDevices() const;
void removeDevice(Device* d); void removeDevice(Device* d);
void cleanDevices(); void cleanDevices();

View file

@ -251,9 +251,45 @@ void Device::addLink(const NetworkPackage& identityPackage, DeviceLink* link)
} }
connect(link, &DeviceLink::pairStatusChanged, this, &Device::pairStatusChanged); connect(link, &DeviceLink::pairStatusChanged, this, &Device::pairStatusChanged);
connect(link, &DeviceLink::pairingRequest, this, &Device::addPairingRequest);
connect(link, &DeviceLink::pairingRequestExpired, this, &Device::removePairingRequest);
connect(link, &DeviceLink::pairingError, this, &Device::pairingError); connect(link, &DeviceLink::pairingError, this, &Device::pairingError);
} }
void Device::addPairingRequest(PairingHandler* handler)
{
m_pairRequests.insert(handler);
Q_EMIT pairingRequestsChanged();
}
void Device::removePairingRequest(PairingHandler* handler)
{
m_pairRequests.remove(handler);
Q_EMIT pairingRequestsChanged();
}
void Device::acceptPairing()
{
if (m_pairRequests.isEmpty())
qWarning() << "no pair requests to accept!";
//copying because the pairing handler will be removed upon accept
const auto prCopy = m_pairRequests;
for (auto ph: prCopy)
ph->acceptPairing();
}
void Device::rejectPairing()
{
if (m_pairRequests.isEmpty())
qWarning() << "no pair requests to reject!";
//copying because the pairing handler will be removed upon reject
const auto prCopy = m_pairRequests;
for (auto ph: prCopy)
ph->rejectPairing();
}
void Device::linkDestroyed(QObject* o) void Device::linkDestroyed(QObject* o)
{ {
removeLink(static_cast<DeviceLink*>(o)); removeLink(static_cast<DeviceLink*>(o));

View file

@ -44,6 +44,7 @@ class KDECONNECTCORE_EXPORT Device
Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChanged) Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChanged)
Q_PROPERTY(bool isTrusted READ isTrusted NOTIFY trustedChanged) Q_PROPERTY(bool isTrusted READ isTrusted NOTIFY trustedChanged)
Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged) Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged)
Q_PROPERTY(bool hasPairingRequests READ hasPairingRequests NOTIFY pairingRequestsChanged)
public: public:
@ -102,6 +103,8 @@ public:
int protocolVersion() { return m_protocolVersion; } int protocolVersion() { return m_protocolVersion; }
QStringList supportedPlugins() const { return m_supportedPlugins.toList(); } QStringList supportedPlugins() const { return m_supportedPlugins.toList(); }
bool hasPairingRequests() const { return !m_pairRequests.isEmpty(); }
public Q_SLOTS: public Q_SLOTS:
///sends a @p np package to the device ///sends a @p np package to the device
///virtual for testing purposes. ///virtual for testing purposes.
@ -113,10 +116,15 @@ public Q_SLOTS:
Q_SCRIPTABLE void unpair(); //from all links Q_SCRIPTABLE void unpair(); //from all links
Q_SCRIPTABLE void reloadPlugins(); //from kconf Q_SCRIPTABLE void reloadPlugins(); //from kconf
Q_SCRIPTABLE void acceptPairing();
Q_SCRIPTABLE void rejectPairing();
private Q_SLOTS: private Q_SLOTS:
void privateReceivedPackage(const NetworkPackage& np); void privateReceivedPackage(const NetworkPackage& np);
void linkDestroyed(QObject* o); void linkDestroyed(QObject* o);
void pairStatusChanged(DeviceLink::PairStatus current); void pairStatusChanged(DeviceLink::PairStatus current);
void addPairingRequest(PairingHandler* handler);
void removePairingRequest(PairingHandler* handler);
Q_SIGNALS: Q_SIGNALS:
Q_SCRIPTABLE void pluginsChanged(); Q_SCRIPTABLE void pluginsChanged();
@ -125,6 +133,8 @@ Q_SIGNALS:
Q_SCRIPTABLE void pairingError(const QString& error); Q_SCRIPTABLE void pairingError(const QString& error);
Q_SCRIPTABLE void nameChanged(const QString& name); Q_SCRIPTABLE void nameChanged(const QString& name);
void pairingRequestsChanged();
private: //Methods private: //Methods
static DeviceType str2type(const QString &deviceType); static DeviceType str2type(const QString &deviceType);
static QString type2str(DeviceType deviceType); static QString type2str(DeviceType deviceType);
@ -144,6 +154,7 @@ private: //Fields (TODO: dPointer!)
//Capabilities stuff //Capabilities stuff
QMultiMap<QString, KdeConnectPlugin*> m_pluginsByIncomingCapability; QMultiMap<QString, KdeConnectPlugin*> m_pluginsByIncomingCapability;
QSet<QString> m_supportedPlugins; QSet<QString> m_supportedPlugins;
QSet<PairingHandler*> m_pairRequests;
}; };
Q_DECLARE_METATYPE(Device*) Q_DECLARE_METATYPE(Device*)

View file

@ -42,18 +42,17 @@ public:
, m_nam(Q_NULLPTR) , m_nam(Q_NULLPTR)
{} {}
void askPairingConfirmation(PairingHandler* d) override void askPairingConfirmation(Device* device) override
{ {
KNotification* notification = new KNotification(QStringLiteral("pairingRequest"), KNotification* notification = new KNotification(QStringLiteral("pairingRequest"),
KNotification::Persistent); KNotification::Persistent);
notification->setIconName(QStringLiteral("dialog-information")); notification->setIconName(QStringLiteral("dialog-information"));
notification->setComponentName(QStringLiteral("kdeconnect")); notification->setComponentName(QStringLiteral("kdeconnect"));
notification->setText(i18n("Pairing request from %1", getDevice(d->deviceLink()->deviceId())->name())); notification->setText(i18n("Pairing request from %1", device->name()));
notification->setActions(QStringList() << i18n("Accept") << i18n("Reject")); notification->setActions(QStringList() << i18n("Accept") << i18n("Reject"));
connect(notification, &KNotification::ignored, d, &PairingHandler::rejectPairing); // notification->setTimeout(PairingHandler::pairingTimeoutMsec());
connect(notification, &KNotification::action1Activated, d, &PairingHandler::acceptPairing); connect(notification, &KNotification::action1Activated, device, &Device::acceptPairing);
connect(notification, &KNotification::action2Activated, d, &PairingHandler::rejectPairing); connect(notification, &KNotification::action2Activated, device, &Device::rejectPairing);
QTimer::singleShot(d->pairingTimeoutMsec(), notification, &KNotification::close); // close after pairing timeout, assuming that the peer uses the same timeout value
notification->sendEvent(); notification->sendEvent();
} }

View file

@ -38,7 +38,7 @@ public:
qWarning() << "error:" << title << description; qWarning() << "error:" << title << description;
} }
void askPairingConfirmation(PairingHandler * d) override { void askPairingConfirmation(Device * d) override {
d->acceptPairing(); d->acceptPairing();
} }