From 9949c02b0f814ad94a27864a5c39689f090299b6 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 23 Nov 2014 19:53:30 +0100 Subject: port to sailfish with qt 5.2 --- endianhelpers.h | 2 +- main.cc | 23 +++++++-- notificationconn.cc | 61 ++++++++++++++++++----- notificationconn.h | 8 +-- rpm/sapd.yaml | 27 ++++++++++ sailfish/notificationmonitor.cpp | 79 ++++++++++++++++++++++++++++++ sailfish/notificationmonitor.h | 28 +++++++++++ sailfish/org.freedesktop.Notifications.xml | 17 +++++++ sapbtlistener.cc | 22 +++++++-- sapbtlistener.h | 9 +++- sapbtpeer.h | 4 +- sapd.pro | 31 +++++++++--- saprotocol.h | 4 +- 13 files changed, 284 insertions(+), 31 deletions(-) create mode 100644 rpm/sapd.yaml create mode 100644 sailfish/notificationmonitor.cpp create mode 100644 sailfish/notificationmonitor.h create mode 100644 sailfish/org.freedesktop.Notifications.xml diff --git a/endianhelpers.h b/endianhelpers.h index f11c669..3060382 100644 --- a/endianhelpers.h +++ b/endianhelpers.h @@ -10,7 +10,7 @@ template inline T read(const QByteArray &data, int &offset) { T unswapped; - qMemCopy(&unswapped, &data.constData()[offset], sizeof(T)); // Unaligned access warning! + memcpy(&unswapped, &data.constData()[offset], sizeof(T)); // Unaligned access warning! offset += sizeof(T); return qFromBigEndian(unswapped); } diff --git a/main.cc b/main.cc index b30e9b5..feb0acb 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include "sapmanager.h" #include "sapbtlistener.h" @@ -18,8 +19,24 @@ int main(int argc, char *argv[]) app.setOrganizationDomain("com.javispedro"); app.setApplicationName("sapd"); - if (app.arguments().size() != 2) { - cerr << "Usage:: sapd " << endl; + QBluetoothAddress address; + QSettings settings; + QString address_from_settings = settings.value("device/address").toString(); + if (!address_from_settings.isEmpty()) { + address = QBluetoothAddress(address_from_settings); + } + + if (app.arguments().size() >= 2) { + // Get the address from the command line. + address = QBluetoothAddress(app.arguments().at(1)); + } + + if (address.isNull()) { + cerr << "Usage: sapd " << endl; + cerr << "If you don't want to specify the BT address every time, put it in " + << qPrintable(settings.fileName()) + << endl; + settings.setValue("device/address", QString()); return EXIT_FAILURE; } @@ -31,8 +48,6 @@ int main(int argc, char *argv[]) QScopedPointer sap_listener(new SAPBTListener); - QBluetoothAddress address = QBluetoothAddress(app.arguments().at(1)); - sap_listener->start(); sap_listener->nudge(address); diff --git a/notificationconn.cc b/notificationconn.cc index fde3303..231427e 100644 --- a/notificationconn.cc +++ b/notificationconn.cc @@ -6,6 +6,10 @@ #include "endianhelpers.h" #include "notificationconn.h" +#if SAILFISH +#include "sailfish/notificationmonitor.h" +#endif + NotificationConn::Notification::Notification() : type(NotificationPopup), sequenceNumber(0), urgent(false), applicationId(0), category(0), @@ -44,7 +48,7 @@ QByteArray NotificationConn::packNotification(const Notification &n) if (!n.body.isEmpty()) attributeCount++; if (!n.thumbnail.isEmpty()) attributeCount++; if (!n.applicationName.isEmpty()) attributeCount++; - //TODO if (n.openInHost) attributeCount++; + // TODO if (n.openInHost) attributeCount++; //if (n.openInWatch) attributeCount++; if (n.notificationId != -1) attributeCount++; @@ -94,12 +98,27 @@ QByteArray NotificationConn::packNotification(const Notification &n) return data; } +void NotificationConn::sendNotification(const Notification &n) +{ + QByteArray packet = packNotification(n); + packet.prepend('\0'); + + qDebug() << packet.toHex(); + + _socket->send(packet); +} + void NotificationConn::handleConnected() { - qDebug() << "Manager socket now connected!"; + qDebug() << "NotificationManager socket now connected!"; +#if SAILFISH + NotificationMonitor *monitor = NotificationMonitor::instance(); + connect(monitor, SIGNAL(incomingNotification(QString,QIcon,QString,int,QString,QDateTime)), + this, SLOT(handleIncomingNotification(QString,QIcon,QString,int,QString,QDateTime))); +#else QTimer::singleShot(2000, this, SLOT(performTest())); - +#endif } void NotificationConn::handleMessageReceived() @@ -112,12 +131,37 @@ void NotificationConn::handleMessageReceived() // TODO Seems that we receive the notification ID that we should act upon. } +void NotificationConn::handleIncomingNotification(const QString &sender, const QIcon &icon, const QString &summary, int count, const QString &body, const QDateTime &dateTime) +{ + short applicationId = _knownSenders.value(sender, 0); + if (!applicationId) { + applicationId = _knownSenders.size() + 1; + _knownSenders.insert(sender, applicationId); + } + + Notification n; + n.sequenceNumber = ++_lastSeqNumber; + n.type = NotificationPopup; + n.time = dateTime; + n.title = summary; + n.packageName = sender; + n.applicationName = sender; + n.body = body; + n.sender = 0; + n.count = 0; // TODO figure this out + n.category = 0; + n.applicationId = applicationId; + n.notificationId = ++_lastNotifId; + + sendNotification(n); +} + void NotificationConn::performTest() { qDebug() << "Performing notif test"; Notification n; - n.sequenceNumber = 1; + n.sequenceNumber = ++_lastSeqNumber; n.type = NotificationPopup; n.time = QDateTime::currentDateTime(); n.title = "A title"; @@ -128,12 +172,7 @@ void NotificationConn::performTest() n.count = 13; n.category = 0; n.applicationId = 0xc2d7; - n.notificationId = 1; + n.notificationId = ++_lastNotifId; - QByteArray packet = packNotification(n); - packet.prepend('\0'); - - qDebug() << packet.toHex(); - - _socket->send(packet); + sendNotification(n); } diff --git a/notificationconn.h b/notificationconn.h index 9be3e20..da3e3c3 100644 --- a/notificationconn.h +++ b/notificationconn.h @@ -54,18 +54,20 @@ protected: }; QByteArray packNotification(const Notification &n); - -private: - + void sendNotification(const Notification &n); private slots: void handleConnected(); void handleMessageReceived(); + void handleIncomingNotification(const QString &sender, const QIcon &icon, const QString &summary, int count, const QString &body, const QDateTime &dateTime); void performTest(); private: SAPConnection *_conn; SAPSocket *_socket; + QHash _knownSenders; + int _lastSeqNumber; + int _lastNotifId; }; #endif // NOTIFICATIONCONN_H diff --git a/rpm/sapd.yaml b/rpm/sapd.yaml new file mode 100644 index 0000000..212f735 --- /dev/null +++ b/rpm/sapd.yaml @@ -0,0 +1,27 @@ +Name: sapd +Summary: Accesory protocol daemon +Version: 0.1.0 +Release: 1 +Group: Communications/Bluetooth +URL: https://gitorious.org/javispedro-jolla-misc/sapd/ +License: GPLv3 +Sources: +- '%{name}-%{version}.tar.bz2' +Description: | + A daemon that speaks accessory protocol. +Configure: none +Builder: qtc5 + +PkgConfigBR: + - sailfishapp >= 1.0.2 + - Qt5Core + - Qt5DBus + - Qt5Bluetooth + - openssl + +PkgBR: +# Workaround current sailfish qt5connectivity packaging bug + - qt5-qtconnectivity-qtbluetooth-devel + +Files: + - '%{_bindir}' diff --git a/sailfish/notificationmonitor.cpp b/sailfish/notificationmonitor.cpp new file mode 100644 index 0000000..f22ce02 --- /dev/null +++ b/sailfish/notificationmonitor.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#include "notificationmonitor.h" +#include "notifications_adaptor.h" + +static NotificationMonitor *global_monitor = 0; + +NotificationMonitor::NotificationMonitor(QObject *parent) : + QObject(parent) +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusConnectionInterface *dbus = bus.interface(); + dbus->call("AddMatch", + "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + new NotificationsAdaptor(this); + bus.registerObject("/org/freedesktop/Notifications", this); +} + +NotificationMonitor::~NotificationMonitor() +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusConnectionInterface *dbus = bus.interface(); + dbus->call("RemoveMatch", + "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); +} + +NotificationMonitor *NotificationMonitor::instance() +{ + if (!global_monitor) { + global_monitor = new NotificationMonitor; + } + return global_monitor; +} + +uint NotificationMonitor::Notify(const QString &app_name, uint replaces_id, const QString &app_icon, const QString &summary, const QString &body, const QStringList &actions, const QVariantHash &hints, int expire_timeout) +{ + QIcon icon; + + qDebug() << "Got notification" << app_name << app_icon << summary << body; + + // Avoid sending a reply for this method call, since we've received it + // because we're eavesdropping. + // The actual target of the method call will send the proper reply, not us. + Q_ASSERT(calledFromDBus()); + setDelayedReply(true); + + // If the notification mentions a specific icon, then use it. + // But otherwise let's prefer our builtin icons. + if (app_icon.startsWith("/")) { + icon = QIcon(app_icon); + } else if (app_icon.startsWith("file:")) { + QUrl url(app_icon); + icon = QIcon(url.toLocalFile()); + } else { + QString category = hints.value("category").toString(); + // Let's hardcode a few categories for now.. + if (!category.isEmpty()) { + qDebug() << "TODO: Category icons"; + } + } + + int count = hints.value("x-nemo-item-count").toInt(); + QDateTime dateTime = hints.value("x-nemo-timestamp").toDateTime(); + if (!dateTime.isValid()) { + dateTime = QDateTime::currentDateTime(); + } + + if (summary.isEmpty() && body.isEmpty()) { + // Avoid sending empty notifications to watch. + return 0; + } + + emit incomingNotification(app_name, icon, summary, count, body, dateTime); + + return 0; +} diff --git a/sailfish/notificationmonitor.h b/sailfish/notificationmonitor.h new file mode 100644 index 0000000..c0f7691 --- /dev/null +++ b/sailfish/notificationmonitor.h @@ -0,0 +1,28 @@ +#ifndef NOTIFICATIONMONITOR_H +#define NOTIFICATIONMONITOR_H + +#include +#include +#include + +class NotificationMonitor : public QObject, protected QDBusContext +{ + Q_OBJECT + +public: + ~NotificationMonitor(); + + static NotificationMonitor *instance(); + +signals: + void incomingNotification(const QString &sender, const QIcon &icon, const QString &summary, int count, const QString &body, const QDateTime &dateTime); + +private: + explicit NotificationMonitor(QObject *parent = 0); + +private slots: + friend class NotificationsAdaptor; + uint Notify(const QString &app_name, uint replaces_id, const QString &app_icon, const QString &summary, const QString &body, const QStringList &actions, const QVariantHash &hints, int expire_timeout); +}; + +#endif // NOTIFICATIONMONITOR_H diff --git a/sailfish/org.freedesktop.Notifications.xml b/sailfish/org.freedesktop.Notifications.xml new file mode 100644 index 0000000..d3f8bef --- /dev/null +++ b/sailfish/org.freedesktop.Notifications.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/sapbtlistener.cc b/sapbtlistener.cc index 1b4550a..76ecb94 100644 --- a/sapbtlistener.cc +++ b/sapbtlistener.cc @@ -1,6 +1,9 @@ #include + +#ifdef MANUAL_SDP #include #include +#endif #include "saprotocol.h" #include "sapbtlistener.h" @@ -12,6 +15,10 @@ namespace { + +#ifdef MANUAL_SDP +// Basically, QBluetoothServiceInfo is not yet compatible with Bluez5, +// so we hack around it by doing our own SDP connection. void add_sdp_record(sdp_session_t *session, const QBluetoothUuid &btuuid, quint16 port) { sdp_list_t *svclass_id, *apseq, *root; @@ -63,6 +70,7 @@ void add_sdp_records(quint16 port) add_sdp_record(sess, SAProtocol::dataServiceUuid, port); } +#endif } @@ -82,7 +90,11 @@ void SAPBTListener::start() return; } +#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) + _server = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); +#else _server = new QRfcommServer(this); +#endif connect(_server, SIGNAL(newConnection()), this, SLOT(acceptConnection())); if (!_server->listen(QBluetoothAddress(), 0)) { qWarning() << "Failed to start Bluetooth listener socket"; @@ -92,7 +104,7 @@ void SAPBTListener::start() quint8 serverPort = _server->serverPort(); -#if 1 +#if MANUAL_SDP // Basically, QBluetoothServiceInfo is not yet compatible with Bluez5, // so we hack around it by doing our own SDP connection. add_sdp_records(serverPort); @@ -123,7 +135,7 @@ void SAPBTListener::start() _service.setServiceProvider("gearbtteest"); QBluetoothServiceInfo::Sequence classIds; - classIds.append(QVariant::fromValue(SAPProtocol::dataServiceUuid)); + classIds.append(QVariant::fromValue(SAProtocol::dataServiceUuid)); _service.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classIds); QBluetoothServiceInfo::Sequence browseGroupList; @@ -167,7 +179,11 @@ void SAPBTListener::stop() void SAPBTListener::nudge(const QBluetoothAddress &address) { +#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) + QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); +#else QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket, this); +#endif connect(socket, SIGNAL(connected()), socket, SLOT(deleteLater())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), @@ -177,7 +193,7 @@ void SAPBTListener::nudge(const QBluetoothAddress &address) socket->connectToService(address, 2); //SAPProtocol::nudgeServiceUuid); -#if 1 +#if DESKTOP // At the same time set up and HFP connection to the watch. new HfpAg(address, this); #endif diff --git a/sapbtlistener.h b/sapbtlistener.h index a59a5e1..3fe821d 100644 --- a/sapbtlistener.h +++ b/sapbtlistener.h @@ -3,7 +3,10 @@ #include -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) +#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) +#include +#include +#elif QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include #include QT_USE_NAMESPACE_BLUETOOTH @@ -36,7 +39,11 @@ private slots: private: +#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) + QBluetoothServer *_server; +#else QRfcommServer *_server; +#endif QBluetoothServiceInfo _service; }; diff --git a/sapbtpeer.h b/sapbtpeer.h index cc060f8..da77f22 100644 --- a/sapbtpeer.h +++ b/sapbtpeer.h @@ -3,7 +3,9 @@ #include -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) +#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) +#include +#elif QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include QT_USE_NAMESPACE_BLUETOOTH #else diff --git a/sapd.pro b/sapd.pro index ee30305..f2157d4 100644 --- a/sapd.pro +++ b/sapd.pro @@ -1,21 +1,36 @@ TARGET = sapd TEMPLATE = app -QT += core dbus -QT -= gui +QT += core gui dbus CONFIG += console -greaterThan(QT_MAJOR_VERSION, 4) { +CONFIG += link_pkgconfig +PKGCONFIG += openssl + +exists(/usr/lib/libsailfishapp.so) { + # Building for Jolla Sailfish, Qt5, Bluez5 + QT += bluetooth + DEFINES += SAILFISH + DBUS_ADAPTORS += sailfish/org.freedesktop.Notifications.xml + SOURCES += sailfish/notificationmonitor.cpp + HEADERS += sailfish/notificationmonitor.h +} else:greaterThan(QT_MAJOR_VERSION, 4) { + # Building for desktop, Qt5, Bluez5 QT += bluetooth + DEFINES += DESKTOP + SOURCES += hfpag.cc + HEADERS += hfpag.h } else { + # Building for desktop, Qt4, Bluez5 CONFIG += mobility MOBILITY += connectivity - DEFINES += DESKTOP + DEFINES += DESKTOP MANUAL_SDP SOURCES += hfpag.cc HEADERS += hfpag.h + PKGCONFIG += bluez } -CONFIG += link_pkgconfig -PKGCONFIG += bluez openssl +target.path = /usr/bin +INSTALLS += target SOURCES += main.cc \ sapbtlistener.cc \ @@ -76,3 +91,7 @@ HEADERS += \ notificationagent.h \ notificationconn.h \ endianhelpers.h + +OTHER_FILES += \ + rpm/sapd.yaml \ + sailfish/org.freedesktop.Notifications.xml diff --git a/saprotocol.h b/saprotocol.h index 469a293..3f38e05 100644 --- a/saprotocol.h +++ b/saprotocol.h @@ -3,7 +3,9 @@ #include -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) +#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) +#include +#elif QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include QT_USE_NAMESPACE_BLUETOOTH #else -- cgit v1.2.3