From 459da42728aa88dfcd0128319984de567384d65e Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 31 Mar 2015 01:27:32 +0200 Subject: notifications partially working --- saltoqd/cardmanager.cpp | 331 ++++++++++++++++++++++++++++++++++++++++ saltoqd/cardmanager.h | 150 ++++++++++++++++++ saltoqd/fmsmanager.cpp | 2 +- saltoqd/notificationmanager.cpp | 36 ++++- saltoqd/notificationmanager.h | 13 +- saltoqd/notificationmonitor.cpp | 71 ++------- saltoqd/notificationmonitor.h | 21 ++- saltoqd/saltoqd.pro | 6 +- saltoqd/storagemanager.cpp | 2 +- saltoqd/toqmanager.cpp | 3 +- saltoqd/toqmanager.h | 2 + 11 files changed, 566 insertions(+), 71 deletions(-) create mode 100644 saltoqd/cardmanager.cpp create mode 100644 saltoqd/cardmanager.h diff --git a/saltoqd/cardmanager.cpp b/saltoqd/cardmanager.cpp new file mode 100644 index 0000000..5418104 --- /dev/null +++ b/saltoqd/cardmanager.cpp @@ -0,0 +1,331 @@ +#include "cardmanager.h" + +Card::Card(const QString &id, QObject *parent) : + QObject(parent), _id(id), + _vibrate(true), _open(false), _visible(false) +{ +} + +QString Card::id() const +{ + return _id; +} + +QString Card::header() const +{ + return _header; +} + +void Card::setHeader(const QString &header) +{ + if (_header != header) { + _header = header; + emit headerChanged(); + } +} + +QString Card::title() const +{ + return _title; +} + +void Card::setTitle(const QString &title) +{ + if (_title != title) { + _title = title; + emit titleChanged(); + } +} + +QString Card::info() const +{ + return _info; +} + +void Card::setInfo(const QString &info) +{ + if (_info != info) { + _info = info; + emit infoChanged(); + } +} + +QDateTime Card::dateTime() const +{ + return _dateTime; +} + +void Card::setDateTime(const QDateTime &dt) +{ + if (_dateTime != dt) { + _dateTime = dt; + emit dateTimeChanged(); + } +} + +bool Card::isVibrate() const +{ + return _vibrate; +} + +void Card::setVibrate(bool vibrate) +{ + if (_vibrate != vibrate) { + _vibrate = vibrate; + emit vibrateChanged(); + } +} + +QString Card::text() const +{ + return _text; +} + +void Card::setText(const QString &text) +{ + if (_text != text) { + _text = text; + emit textChanged(); + } +} + +QStringList Card::menuOptions() const +{ + return _options; +} + +void Card::setMenuOptions(const QStringList &options) +{ + if (_options != options) { + _options = options; + emit menuOptionsChanged(); + } +} + +bool Card::isOpen() const +{ + return _open; +} + +void Card::setOpen(bool open) +{ + _open = open; +} + +bool Card::isVisible() const +{ + return _visible; +} + +void Card::setVisible(bool visible) +{ + _visible = visible; +} + +CardDeck::CardDeck(const QString &package, const QString &application, QObject *parent) : + QObject(parent), + _package(package), _application(application), + _refreshTimer(new QTimer(this)) +{ + _refreshTimer->setSingleShot(true); + _refreshTimer->setInterval(100); + connect(_refreshTimer, &QTimer::timeout, + this, &CardDeck::needsRefresh); +} + +QString CardDeck::package() const +{ + return _package; +} + +QString CardDeck::application() const +{ + return _application; +} + +QList CardDeck::cards() const +{ + return _deck; +} + +Card* CardDeck::cardAt(int position) const +{ + return _deck.at(position); +} + +void CardDeck::appendCard(Card *card) +{ + insertCard(_deck.size(), card); +} + +void CardDeck::insertCard(int position, Card *card) +{ + if (_ids.contains(card->id())) { + qWarning() << "Card" << card->id() << "already present"; + return; + } + + _deck.insert(position, card); + _ids.insert(card->id(), card); + + connect(card, &Card::headerChanged, this, &CardDeck::scheduleRefresh); + connect(card, &Card::titleChanged, this, &CardDeck::scheduleRefresh); + connect(card, &Card::infoChanged, this, &CardDeck::scheduleRefresh); + connect(card, &Card::vibrateChanged, this, &CardDeck::scheduleRefresh); + connect(card, &Card::textChanged, this, &CardDeck::scheduleRefresh); + connect(card, &Card::menuOptionsChanged, this, &CardDeck::scheduleRefresh); + + emit cardAdded(card); + scheduleRefresh(); +} + +void CardDeck::removeCard(int position) +{ + Card * card = _deck.takeAt(position); + _ids.remove(card->id()); + + disconnect(card, 0, this, 0); + + emit cardRemoved(card); + scheduleRefresh(); +} + +void CardDeck::removeCard(Card *card) +{ + int position = _deck.indexOf(card); + if (position >= 0) { + removeCard(position); + } else { + qWarning() << "Card not found"; + } +} + +void CardDeck::scheduleRefresh() +{ + if (!_refreshTimer->isActive()) { + _refreshTimer->start(); + } +} + +CardManager::CardManager(FmsManager *fms, ToqManager *toq) : + QObject(toq), _toq(toq), _fms(fms) +{ + _toq->setEndpointListener(ToqConnection::AppMessagingEndpoint, this); +} + +void CardManager::handleMessage(const ToqConnection::Message &msg) +{ + +} + +void CardManager::installDeck(CardDeck *deck) +{ + if (_decks.contains(deck->package())) { + qWarning() << "Deck" << deck->package() << "is already installed"; + return; + } + + _decks.insert(deck->package(), deck); + + connect(deck, &CardDeck::needsRefresh, this, &CardManager::handleDeckNeedsRefresh); + connect(deck, &CardDeck::cardAdded, this, &CardManager::handleCardAdded); + connect(deck, &CardDeck::cardRemoved, this, &CardManager::handleCardRemoved); +} + +void CardManager::uninstallDeck(CardDeck *deck) +{ + if (!_decks.contains(deck->package())) { + qWarning() << "Deck" << deck->package() << "is not installed"; + } + _decks.remove(deck->package()); + + disconnect(deck, 0, this, 0); +} + +QString CardManager::generateCardDescription(const QString &verb, Card *card) const +{ + QString desc = verb + " { "; + desc += QString("id = \"%1\", ").arg(card->id()); + desc += QString("version = 2, "); + if (!card->header().isEmpty()) { + desc += QString("header = \"%1\", ").arg(card->header()); + } + if (!card->title().isEmpty()) { + desc += QString("title = \"%1\", ").arg(card->title()); + } + if (card->dateTime().isValid()) { + desc += QString("time = \"%1\", ").arg(card->dateTime().toMSecsSinceEpoch()); + } + if (!card->info().isEmpty()) { + desc += QString("title = \"%1\", ").arg(card->info()); + } + if (!card->isVibrate()) { + desc += QString("suppressvibe = \"true\", "); + } + if (!card->text().isEmpty()) { + desc += QString("detail = { \"%1\" }").arg(card->text()); + } + desc += QString("}\n"); + return desc; +} + +void CardManager::refreshDeck(CardDeck *deck) +{ + Q_ASSERT(deck); + Q_ASSERT(_toq->isConnected()); + + qDebug() << "Refreshing deck" << deck->package(); + + QList cards = deck->cards(); + QString data; + for (Card * card : cards) { + data += generateCardDescription("NotifyCard", card); + } + +#if 0 + qDebug() << data; + data = QLatin1String( + "NotifyCard { id = \"jackpal.androidterm\", version = 2, app = \"Terminal\", title = \"Terminal\", time = \"1427073495379\", cardevents = \"true\", divider = \"true\", icon = \"fms:/jackpal.androidterm.img\", detail = { \"Ses de terminal en ejecucin\" } }\n" + "NotifyCard { id = \"com.android.vending\", version = 2, app = \"Google Play Store\", title = \"Simulador Chat\", time = \"1427073139463\", divider = \"true\", icon = \"fms:/com.android.vending.img\", detail = { \"Instalada correctamente\" } }\n"); +#endif + + qDebug() << data; + + QString cardsFile = QString("/packages/%1/cards.dat").arg(deck->package()); + _fms->updateFile(cardsFile, data.toUtf8()); +} + +void CardManager::handleToqConnected() +{ + for (const QString &package : _pending) { + refreshDeck(_decks.value(package)); + } + _pending.clear(); +} + +void CardManager::handleDeckNeedsRefresh() +{ + CardDeck *deck = static_cast(sender()); + qDebug() << deck->package() << "needs refresh"; + if (_toq->isConnected()) { + refreshDeck(deck); + } else { + _pending.insert(deck->package()); + } +} + +void CardManager::handleCardAdded(Card *card) +{ + CardDeck *deck = static_cast(sender()); + if (_toq->isConnected()) { + + } +} + +void CardManager::handleCardRemoved(Card *card) +{ + CardDeck *deck = static_cast(sender()); + if (_toq->isConnected()) { + + } +} diff --git a/saltoqd/cardmanager.h b/saltoqd/cardmanager.h new file mode 100644 index 0000000..863c4a6 --- /dev/null +++ b/saltoqd/cardmanager.h @@ -0,0 +1,150 @@ +#ifndef CARDMANAGER_H +#define CARDMANAGER_H + +#include "fmsmanager.h" + +class CardManager; +class CardDeck; +class Card; + +class Card : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString id READ id CONSTANT) + Q_PROPERTY(QString header READ header WRITE setHeader NOTIFY headerChanged) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged) + Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged) + // TODO card events, divider, icon, picture + Q_PROPERTY(bool vibrate READ isVibrate WRITE setVibrate NOTIFY vibrateChanged) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QStringList menuOptions READ menuOptions WRITE setMenuOptions NOTIFY menuOptionsChanged) + Q_PROPERTY(bool open READ isOpen NOTIFY openChanged) + Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged) + +public: + explicit Card(const QString &id, QObject *parent = 0); + + QString id() const; + + QString header() const; + void setHeader(const QString &header); + + QString title() const; + void setTitle(const QString &title); + + QString info() const; + void setInfo(const QString &info); + + QDateTime dateTime() const; + void setDateTime(const QDateTime &dt); + + bool isVibrate() const; + void setVibrate(bool vibrate); + + QString text() const; + void setText(const QString &text); + + QStringList menuOptions() const; + void setMenuOptions(const QStringList &options); + + bool isOpen() const; + void setOpen(bool open); + + bool isVisible() const; + void setVisible(bool visible); + +signals: + void headerChanged(); + void titleChanged(); + void infoChanged(); + void dateTimeChanged(); + void vibrateChanged(); + void textChanged(); + void menuOptionsChanged(); + void openChanged(); + void visibleChanged(); + + void optionSelected(const QString &option); + +private: + QString _id; + QString _header; + QString _title; + QString _info; + QDateTime _dateTime; + bool _vibrate; + QString _text; + QStringList _options; + bool _open; + bool _visible; +}; + +class CardDeck : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString package READ package CONSTANT) + Q_PROPERTY(QString application READ application CONSTANT) + Q_PROPERTY(QList cards READ cards) + +public: + explicit CardDeck(const QString &package, const QString &application, QObject *parent = 0); + + QString package() const; + QString application() const; + + QList cards() const; + Card * cardAt(int position) const; + + void appendCard(Card * card); + void insertCard(int position, Card * card); + void removeCard(int position); + void removeCard(Card * card); + +signals: + void messageReceived(const QString &message); + void needsRefresh(); + void cardAdded(Card * card); + void cardRemoved(Card * card); + +private slots: + void scheduleRefresh(); + +private: + QString _package; + QString _application; + QList _deck; + QHash _ids; + QTimer *_refreshTimer; +}; + +class CardManager : public QObject, public ToqManager::EndpointHandler +{ + Q_OBJECT +public: + explicit CardManager(FmsManager *fms, ToqManager *toq); + + void handleMessage(const ToqConnection::Message &msg) Q_DECL_OVERRIDE; + +public slots: + void installDeck(CardDeck *deck); + void uninstallDeck(CardDeck *deck); + +private: + QString generateCardDescription(const QString &verb, Card * card) const; + +private slots: + void refreshDeck(CardDeck *deck); + void handleToqConnected(); + void handleDeckNeedsRefresh(); + void handleCardAdded(Card * card); + void handleCardRemoved(Card * card); + +private: + ToqManager *_toq; + FmsManager *_fms; + QHash _decks; + QSet _pending; +}; + +#endif // CARDMANAGER_H diff --git a/saltoqd/fmsmanager.cpp b/saltoqd/fmsmanager.cpp index 26e147b..6abf0b6 100644 --- a/saltoqd/fmsmanager.cpp +++ b/saltoqd/fmsmanager.cpp @@ -8,7 +8,7 @@ static QString generate_send_name(const QString &path, int transactionId, int ch int dot = path.lastIndexOf('.'); int start = slash >= 0 ? slash + 1 : 0; - int len = dot > start ? (path.length() - 1) - (dot + 1) : -1; + int len = dot > start ? dot - start : -1; QString base = path.mid(start, len); QString ext; diff --git a/saltoqd/notificationmanager.cpp b/saltoqd/notificationmanager.cpp index a2142fc..c78fffb 100644 --- a/saltoqd/notificationmanager.cpp +++ b/saltoqd/notificationmanager.cpp @@ -1,7 +1,39 @@ #include "notificationmanager.h" #include "notificationmonitor.h" -NotificationManager::NotificationManager(ToqManager *toq) : - QObject(toq), _toq(toq), _monitor(NotificationMonitor::instance()) +NotificationManager::NotificationManager(CardManager *card, ToqManager *toq) : + QObject(toq), _toq(toq), _monitor(NotificationMonitor::instance()), + _card(card), + _deck(new CardDeck("com.qualcomm.qce.androidnotifications", "Notifications", this)) { + _card->installDeck(_deck); + + connect(_monitor, &NotificationMonitor::notification, + this, &NotificationManager::handleNotification); +} + +void NotificationManager::handleNotification(MonitoredNotification *n) +{ + Card *card = new Card(QString::number(qint64(n->id()))); + + card->setHeader(n->sender()); + card->setTitle(n->summary()); + card->setText(n->body()); + card->setDateTime(n->timestamp()); + card->setVibrate(true); + + connect(n, &MonitoredNotification::closed, + this, &NotificationManager::handleClosedNotification); + + _deck->appendCard(card); +} + +void NotificationManager::handleClosedNotification() +{ + MonitoredNotification *n = static_cast(sender()); + Card *card = _cards.take(n); + if (card) { + _deck->removeCard(card); + card->deleteLater(); + } } diff --git a/saltoqd/notificationmanager.h b/saltoqd/notificationmanager.h index 78c32ed..ab6ccad 100644 --- a/saltoqd/notificationmanager.h +++ b/saltoqd/notificationmanager.h @@ -1,19 +1,28 @@ #ifndef NOTIFICATIONMANAGER_H #define NOTIFICATIONMANAGER_H -#include "toqmanager.h" +#include "cardmanager.h" +class MonitoredNotification; class NotificationMonitor; class NotificationManager : public QObject { Q_OBJECT + public: - explicit NotificationManager(ToqManager *toq); + explicit NotificationManager(CardManager *card, ToqManager *toq); + +private slots: + void handleNotification(MonitoredNotification *n); + void handleClosedNotification(); private: ToqManager *_toq; NotificationMonitor *_monitor; + CardManager *_card; + CardDeck *_deck; + QMap _cards; }; #endif // NOTIFICATIONMANAGER_H diff --git a/saltoqd/notificationmonitor.cpp b/saltoqd/notificationmonitor.cpp index 2c6621d..5cff5f6 100644 --- a/saltoqd/notificationmonitor.cpp +++ b/saltoqd/notificationmonitor.cpp @@ -116,6 +116,10 @@ QVariantHash parse_notify_call(DBusMessage *msg) const char *s; dbus_message_iter_get_basic(&value, &s); r.insert("category", QString::fromUtf8(s)); + } else if (strcmp(key, "x-nemo-timestamp") == 0 && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { + const char *s; + dbus_message_iter_get_basic(&value, &s); + r.insert("timestamp", QDateTime::fromString(QString::fromUtf8(s), Qt::ISODate)); } dbus_message_iter_next(&sub); @@ -201,7 +205,7 @@ void remove_match_rule(const char *rule) } -IncomingNotification::IncomingNotification(QObject *parent) : +MonitoredNotification::MonitoredNotification(QObject *parent) : QObject(parent) { } @@ -262,13 +266,16 @@ NotificationMonitor *NotificationMonitor::instance() void NotificationMonitor::processIncomingNotification(quint32 id, const QVariantHash &content) { qDebug() << "Incoming notification" << id << content; - IncomingNotification *n = _notifs.value(id, 0); + MonitoredNotification *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 = new MonitoredNotification(this); + n->_id = id; + n->_sender = content["sender"].toString(); n->_summary = content["summary"].toString(); + n->_body = content["body"].toString(); + n->_timestamp = content["timestamp"].toDateTime(); emit notification(n); } @@ -277,62 +284,10 @@ void NotificationMonitor::processIncomingNotification(quint32 id, const QVariant void NotificationMonitor::processCloseNotification(quint32 id, quint32 reason) { qDebug() << "Close notification" << id << reason; - IncomingNotification *n = _notifs.value(id, 0); + MonitoredNotification *n = _notifs.value(id, 0); if (n) { _notifs.remove(id); + emit n->closed(reason); 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; - - 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; -} - -void NotificationMonitor::CloseNotification(uint id) -{ - qDebug() << "Close notification" << id; -} - -#endif - diff --git a/saltoqd/notificationmonitor.h b/saltoqd/notificationmonitor.h index 8ac49c5..f617817 100644 --- a/saltoqd/notificationmonitor.h +++ b/saltoqd/notificationmonitor.h @@ -5,26 +5,39 @@ #include #include -class IncomingNotification : public QObject +class MonitoredNotification : public QObject { Q_OBJECT + Q_PROPERTY(uint id READ id CONSTANT) Q_PROPERTY(QString sender READ sender) Q_PROPERTY(QString summary READ summary NOTIFY summaryChanged) + Q_PROPERTY(QString body READ body NOTIFY bodyChanged) + Q_PROPERTY(QDateTime timestamp READ timestamp NOTIFY timestampChanged) - explicit IncomingNotification(QObject *parent = 0); + explicit MonitoredNotification(QObject *parent = 0); public: + inline uint id() const { return _id; } inline QString sender() const { return _sender; } inline QString summary() const { return _summary; } + inline QString body() const { return _body; } + inline QDateTime timestamp() const { return _timestamp; } signals: void summaryChanged(); + void bodyChanged(); + void timestampChanged(); + + void closed(int reason); private: friend class NotificationMonitor; + uint _id; QString _sender; QString _summary; + QString _body; + QDateTime _timestamp; }; class NotificationMonitor : public QObject @@ -40,11 +53,11 @@ public: void processCloseNotification(quint32 id, quint32 reason); signals: - void notification(IncomingNotification *n); + void notification(MonitoredNotification *n); private: explicit NotificationMonitor(QObject *parent = 0); - QMap _notifs; + QMap _notifs; }; diff --git a/saltoqd/saltoqd.pro b/saltoqd/saltoqd.pro index c664b6f..a454938 100644 --- a/saltoqd/saltoqd.pro +++ b/saltoqd/saltoqd.pro @@ -21,7 +21,8 @@ SOURCES += main.cpp \ fmsmanager.cpp \ weathermanager.cpp \ obexconnection.cpp \ - contactsmanager.cpp + contactsmanager.cpp \ + cardmanager.cpp HEADERS += \ toqconnection.h \ @@ -36,7 +37,8 @@ HEADERS += \ fmsmanager.h \ weathermanager.h \ obexconnection.h \ - contactsmanager.h + contactsmanager.h \ + cardmanager.h DBUS_ADAPTORS += org.freedesktop.Notifications.xml diff --git a/saltoqd/storagemanager.cpp b/saltoqd/storagemanager.cpp index 257faf1..eea309b 100644 --- a/saltoqd/storagemanager.cpp +++ b/saltoqd/storagemanager.cpp @@ -4,7 +4,7 @@ static QString generate_send_name(const QString &store, int checksum) { - return QString("%1_%2.jsn").arg(store).arg(checksum); + return QString("%1_%2.jsn").arg(store).arg(qint64(checksum)); } StorageManager::StorageManager(ObexConnection *obex, ToqManager *toq) : diff --git a/saltoqd/toqmanager.cpp b/saltoqd/toqmanager.cpp index ea93d2a..c8ba0e2 100644 --- a/saltoqd/toqmanager.cpp +++ b/saltoqd/toqmanager.cpp @@ -29,7 +29,8 @@ ToqManager::ToqManager(MDConfGroup *settings, QObject *parent) : _commManager(new CommManager(_settings, _storageManager, _contactsManager, this)), _voiceCallManager(new VoiceCallManager(this)), _weatherManager(new WeatherManager(_fmsManager, this)), - _notificationManager(new NotificationManager(this)) + _cardManager(new CardManager(_fmsManager, this)), + _notificationManager(new NotificationManager(_cardManager, this)) { connect(_conn, &ToqConnection::messageReceived, this, &ToqManager::handleToqMessage); diff --git a/saltoqd/toqmanager.h b/saltoqd/toqmanager.h index 7e6d19a..e3601f4 100644 --- a/saltoqd/toqmanager.h +++ b/saltoqd/toqmanager.h @@ -16,6 +16,7 @@ class ContactsManager; class CommManager; class VoiceCallManager; class WeatherManager; +class CardManager; class NotificationManager; class ToqManager : public QObject @@ -64,6 +65,7 @@ private: CommManager *_commManager; VoiceCallManager *_voiceCallManager; WeatherManager *_weatherManager; + CardManager *_cardManager; NotificationManager *_notificationManager; }; -- cgit v1.2.3