From efe35bf6c6ca8cce73e97f303963a51862778600 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 30 Mar 2015 01:05:47 +0200 Subject: experiment with new notificationmonitor approach --- saltoqd/notificationmanager.cpp | 3 +- saltoqd/notificationmanager.h | 3 + saltoqd/notificationmonitor.cpp | 287 ++++++++++++++++++++++++++++-- saltoqd/notificationmonitor.h | 35 +++- saltoqd/org.freedesktop.Notifications.xml | 3 + saltoqd/saltoqd.pro | 4 +- saltoqd/toqconnection.cpp | 9 +- saltoqd/toqmanager.cpp | 4 +- saltoqd/toqmanager.h | 2 + 9 files changed, 323 insertions(+), 27 deletions(-) diff --git a/saltoqd/notificationmanager.cpp b/saltoqd/notificationmanager.cpp index 45020cb..a2142fc 100644 --- a/saltoqd/notificationmanager.cpp +++ b/saltoqd/notificationmanager.cpp @@ -1,6 +1,7 @@ #include "notificationmanager.h" +#include "notificationmonitor.h" NotificationManager::NotificationManager(ToqManager *toq) : - QObject(toq), _toq(toq) + QObject(toq), _toq(toq), _monitor(NotificationMonitor::instance()) { } diff --git a/saltoqd/notificationmanager.h b/saltoqd/notificationmanager.h index 1889336..78c32ed 100644 --- a/saltoqd/notificationmanager.h +++ b/saltoqd/notificationmanager.h @@ -3,6 +3,8 @@ #include "toqmanager.h" +class NotificationMonitor; + class NotificationManager : public QObject { Q_OBJECT @@ -11,6 +13,7 @@ public: private: ToqManager *_toq; + NotificationMonitor *_monitor; }; #endif // NOTIFICATIONMANAGER_H diff --git a/saltoqd/notificationmonitor.cpp b/saltoqd/notificationmonitor.cpp index f22ce02..2c6621d 100644 --- a/saltoqd/notificationmonitor.cpp +++ b/saltoqd/notificationmonitor.cpp @@ -1,30 +1,254 @@ #include -#include -#include -#include +#include +#include #include "notificationmonitor.h" #include "notifications_adaptor.h" -static NotificationMonitor *global_monitor = 0; +namespace +{ + +NotificationMonitor *global_monitor = 0; + +DBusConnection *bus_connection; + +QHash pending_confirmation; + +dbus_bool_t bus_watch_add(DBusWatch *watch, void *data) +{ + NotificationMonitor *monitor = static_cast(data); + int socket = dbus_watch_get_socket(watch); + int flags = dbus_watch_get_flags(watch); + QSocketNotifier::Type type; + switch (flags) { + case DBUS_WATCH_READABLE: + type = QSocketNotifier::Read; + break; + case DBUS_WATCH_WRITABLE: + type = QSocketNotifier::Write; + break; + default: + qWarning() << "Can't add this type of watch" << flags; + return FALSE; + } + + QSocketNotifier *notifier = new QSocketNotifier(socket, type, monitor); + dbus_watch_set_data(watch, notifier, NULL); + + notifier->setEnabled(dbus_watch_get_enabled(watch)); + + notifier->connect(notifier, &QSocketNotifier::activated, + [watch]() { + dbus_watch_handle(watch, dbus_watch_get_flags(watch)); + + while (dbus_connection_get_dispatch_status(bus_connection) == DBUS_DISPATCH_DATA_REMAINS) { + dbus_connection_dispatch(bus_connection); + } + }); + + return TRUE; +} + +void bus_watch_remove(DBusWatch *watch, void *data) +{ + QSocketNotifier *notifier = static_cast(dbus_watch_get_data(watch)); + Q_UNUSED(data); + delete notifier; +} + +void bus_watch_toggle(DBusWatch *watch, void *data) +{ + QSocketNotifier *notifier = static_cast(dbus_watch_get_data(watch)); + Q_UNUSED(data); + notifier->setEnabled(dbus_watch_get_enabled(watch)); +} + +QVariantHash parse_notify_call(DBusMessage *msg) +{ + QVariantHash r; + DBusMessageIter iter, sub; + const char *app_name, *app_icon, *summary, *body; + quint32 replaces_id; + qint32 expire_timeout; + + if (strcmp(dbus_message_get_signature(msg), "susssasa{sv}i") != 0) { + qWarning() << "Invalid signature"; + return r; + } + + dbus_message_iter_init(msg, &iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&iter, &app_name); + dbus_message_iter_next(&iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32); + dbus_message_iter_get_basic(&iter, &replaces_id); + dbus_message_iter_next(&iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&iter, &app_icon); + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &summary); + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &body); + dbus_message_iter_next(&iter); + + QStringList actions; + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { + const char *action; + dbus_message_iter_get_basic(&sub, &action); + actions.append(QString::fromUtf8(action)); + dbus_message_iter_next(&sub); + } + r.insert("actions", QVariant::fromValue(actions)); + dbus_message_iter_next(&iter); + + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(&sub, &entry); + dbus_message_iter_get_basic(&entry, &key); + dbus_message_iter_next(&entry); + + dbus_message_iter_recurse(&entry, &value); + if (strcmp(key, "category") == 0 && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { + const char *s; + dbus_message_iter_get_basic(&value, &s); + r.insert("category", QString::fromUtf8(s)); + } + + dbus_message_iter_next(&sub); + } + + dbus_message_iter_next(&iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32); + dbus_message_iter_get_basic(&iter, &expire_timeout); + + r.insert("sender", QString::fromUtf8(app_name)); + r.insert("app_icon", QString::fromUtf8(app_icon)); + r.insert("summary", QString::fromUtf8(summary)); + r.insert("body", QString::fromUtf8(body)); + + return r; +} + +DBusHandlerResult message_filter(DBusConnection *conn, DBusMessage *msg, void *user_data) +{ + NotificationMonitor *monitor = static_cast(user_data); + DBusError error = DBUS_ERROR_INIT; + Q_UNUSED(conn); + switch (dbus_message_get_type(msg)) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + if (dbus_message_is_method_call(msg, "org.freedesktop.Notifications", "Notify")) { + quint32 serial = dbus_message_get_serial(msg); + QVariantHash content = parse_notify_call(msg); + pending_confirmation.insert(serial, content); + } + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + if (pending_confirmation.contains(dbus_message_get_reply_serial(msg))) { + quint32 id; + if (dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID)) { + QVariantHash content = pending_confirmation.take(dbus_message_get_reply_serial(msg)); + monitor->processIncomingNotification(id, content); + } else { + qWarning() << "Could not parse notification method return"; + } + } + break; + case DBUS_MESSAGE_TYPE_SIGNAL: + if (dbus_message_is_signal(msg, "org.freedesktop.Notifications", "NotificationClosed")) { + quint32 id, reason; + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_UINT32, &reason, + DBUS_TYPE_INVALID)) { + monitor->processCloseNotification(id, reason); + } else { + qWarning() << "Failed to parse notification signal arguments"; + } + + } + break; + } + return DBUS_HANDLER_RESULT_HANDLED; +} + +void send_message_with_string(const char *service, const char *path, const char *iface, const char *method, const char *arg) +{ + DBusMessage *msg = dbus_message_new_method_call(service, path, iface, method); + Q_ASSERT(msg); + dbus_message_set_no_reply(msg, TRUE); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + dbus_connection_send(bus_connection, msg, NULL); + dbus_message_unref(msg); +} + +void add_match_rule(const char *rule) +{ + send_message_with_string("org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "AddMatch", rule); +} + +void remove_match_rule(const char *rule) +{ + send_message_with_string("org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "RemoveMatch", rule); +} + +} + +IncomingNotification::IncomingNotification(QObject *parent) : + QObject(parent) +{ +} 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); + Q_ASSERT(!bus_connection); + DBusError error = DBUS_ERROR_INIT; + bus_connection = dbus_bus_get_private(DBUS_BUS_SESSION, &error); + if (!bus_connection) { + qWarning() << "Could not connect to the session bus"; + return; + } + + dbus_connection_set_exit_on_disconnect(bus_connection, FALSE); + + dbus_connection_set_watch_functions(bus_connection, bus_watch_add, + bus_watch_remove, bus_watch_toggle, + this, NULL); + + add_match_rule("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop='true'"); + add_match_rule("type='method_return',sender='org.freedesktop.Notifications',eavesdrop='true'"); + add_match_rule("type='signal',sender='org.freedesktop.Notifications',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications',member='NotificationClosed'"); + + dbus_bool_t result = dbus_connection_add_filter(bus_connection, message_filter, + this, NULL); + if (!result) { + qWarning() << "Could not add filter"; + } + + qDebug() << "Starting notification monitor"; } NotificationMonitor::~NotificationMonitor() { - QDBusConnection bus = QDBusConnection::sessionBus(); - QDBusConnectionInterface *dbus = bus.interface(); - dbus->call("RemoveMatch", - "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + Q_ASSERT(bus_connection); + + remove_match_rule("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop='true'"); + remove_match_rule("type='method_return',sender='org.freedesktop.Notifications',eavesdrop='true'"); + remove_match_rule("type='signal',sender='org.freedesktop.Notifications',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications',member='NotificationClosed'"); + + dbus_connection_remove_filter(bus_connection, message_filter, this); + + dbus_connection_close(bus_connection); + dbus_connection_unref(bus_connection); + bus_connection = NULL; } NotificationMonitor *NotificationMonitor::instance() @@ -35,6 +259,33 @@ NotificationMonitor *NotificationMonitor::instance() return global_monitor; } +void NotificationMonitor::processIncomingNotification(quint32 id, const QVariantHash &content) +{ + qDebug() << "Incoming notification" << id << content; + IncomingNotification *n = _notifs.value(id, 0); + if (n) { + // TODO emit changed signals for individual fields + } else { + n = new IncomingNotification(this); + n->_sender = content["app_name"].toString(); + n->_summary = content["summary"].toString(); + + emit notification(n); + } +} + +void NotificationMonitor::processCloseNotification(quint32 id, quint32 reason) +{ + qDebug() << "Close notification" << id << reason; + IncomingNotification *n = _notifs.value(id, 0); + if (n) { + _notifs.remove(id); + n->deleteLater(); + } +} + +#if 0 + 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; @@ -77,3 +328,11 @@ uint NotificationMonitor::Notify(const QString &app_name, uint replaces_id, cons return 0; } + +void NotificationMonitor::CloseNotification(uint id) +{ + qDebug() << "Close notification" << id; +} + +#endif + diff --git a/saltoqd/notificationmonitor.h b/saltoqd/notificationmonitor.h index c0f7691..8ac49c5 100644 --- a/saltoqd/notificationmonitor.h +++ b/saltoqd/notificationmonitor.h @@ -2,10 +2,32 @@ #define NOTIFICATIONMONITOR_H #include +#include #include -#include -class NotificationMonitor : public QObject, protected QDBusContext +class IncomingNotification : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString sender READ sender) + Q_PROPERTY(QString summary READ summary NOTIFY summaryChanged) + + explicit IncomingNotification(QObject *parent = 0); + +public: + inline QString sender() const { return _sender; } + inline QString summary() const { return _summary; } + +signals: + void summaryChanged(); + +private: + friend class NotificationMonitor; + + QString _sender; + QString _summary; +}; + +class NotificationMonitor : public QObject { Q_OBJECT @@ -14,15 +36,16 @@ public: static NotificationMonitor *instance(); + void processIncomingNotification(quint32 id, const QVariantHash &content); + void processCloseNotification(quint32 id, quint32 reason); + signals: - void incomingNotification(const QString &sender, const QIcon &icon, const QString &summary, int count, const QString &body, const QDateTime &dateTime); + void notification(IncomingNotification *n); private: explicit NotificationMonitor(QObject *parent = 0); + QMap _notifs; -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/saltoqd/org.freedesktop.Notifications.xml b/saltoqd/org.freedesktop.Notifications.xml index d3f8bef..3e645a0 100644 --- a/saltoqd/org.freedesktop.Notifications.xml +++ b/saltoqd/org.freedesktop.Notifications.xml @@ -14,4 +14,7 @@ + + + diff --git a/saltoqd/saltoqd.pro b/saltoqd/saltoqd.pro index 332dcb8..c664b6f 100644 --- a/saltoqd/saltoqd.pro +++ b/saltoqd/saltoqd.pro @@ -5,8 +5,8 @@ QT += dbus bluetooth contacts CONFIG += c++11 link_pkgconfig -PKGCONFIG += zlib mlite5 commhistory-qt5 openobex qtcontacts-sqlite-qt5-extensions -INCLUDEPATH += /usr/include/commhistory-qt5 /usr/include/mlite5 +PKGCONFIG += zlib dbus-1 mlite5 commhistory-qt5 openobex qtcontacts-sqlite-qt5-extensions +INCLUDEPATH += /usr/include/dbus-1.0 /usr/include/mlite5 /usr/include/commhistory-qt5 SOURCES += main.cpp \ toqconnection.cpp \ diff --git a/saltoqd/toqconnection.cpp b/saltoqd/toqconnection.cpp index 4cc1d3e..6c1035e 100644 --- a/saltoqd/toqconnection.cpp +++ b/saltoqd/toqconnection.cpp @@ -6,6 +6,8 @@ #include "toqconnection.h" static const int HEADER_LENGTH = 10; +static const int FIRST_CONNECTION_INTERVAL = 1000; +static const int RETRY_CONNECTION_INTERVAL = 30000; ToqConnection::ToqConnection(QObject *parent) : QObject(parent), @@ -17,7 +19,6 @@ ToqConnection::ToqConnection(QObject *parent) : this, &ToqConnection::tryConnect); _reconnectTimer->setSingleShot(true); - _reconnectTimer->setInterval(1000); } QString ToqConnection::nameOfEndpoint(Endpoint ep) @@ -57,7 +58,7 @@ void ToqConnection::setAddress(const QBluetoothAddress &address) if (isConnected()) { _socket->disconnectFromService(); } else { - _reconnectTimer->start(); + _reconnectTimer->start(FIRST_CONNECTION_INTERVAL); } } } @@ -157,7 +158,9 @@ void ToqConnection::handleSocketDisconnected() qDebug() << "Disconnected"; _socket->deleteLater(); _socket = 0; - _reconnectTimer->start(); + if (!_address.isNull()) { + _reconnectTimer->start(RETRY_CONNECTION_INTERVAL); + } emit disconnected(); emit connectedChanged(); } diff --git a/saltoqd/toqmanager.cpp b/saltoqd/toqmanager.cpp index f03ba54..ea93d2a 100644 --- a/saltoqd/toqmanager.cpp +++ b/saltoqd/toqmanager.cpp @@ -11,6 +11,7 @@ #include "commmanager.h" #include "voicecallmanager.h" #include "weathermanager.h" +#include "notificationmanager.h" static const bool PROTO_DEBUG = true; @@ -27,7 +28,8 @@ ToqManager::ToqManager(MDConfGroup *settings, QObject *parent) : _contactsManager(new ContactsManager(_storageManager, this)), _commManager(new CommManager(_settings, _storageManager, _contactsManager, this)), _voiceCallManager(new VoiceCallManager(this)), - _weatherManager(new WeatherManager(_fmsManager, this)) + _weatherManager(new WeatherManager(_fmsManager, this)), + _notificationManager(new NotificationManager(this)) { connect(_conn, &ToqConnection::messageReceived, this, &ToqManager::handleToqMessage); diff --git a/saltoqd/toqmanager.h b/saltoqd/toqmanager.h index 1b2773c..7e6d19a 100644 --- a/saltoqd/toqmanager.h +++ b/saltoqd/toqmanager.h @@ -16,6 +16,7 @@ class ContactsManager; class CommManager; class VoiceCallManager; class WeatherManager; +class NotificationManager; class ToqManager : public QObject { @@ -63,6 +64,7 @@ private: CommManager *_commManager; VoiceCallManager *_voiceCallManager; WeatherManager *_weatherManager; + NotificationManager *_notificationManager; }; inline bool ToqManager::isConnected() const -- cgit v1.2.3