summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libsowatch/declarativewatchlet.cpp6
-rw-r--r--libsowatch/declarativewatchwrapper.cpp13
-rw-r--r--libsowatch/declarativewatchwrapper.h3
-rw-r--r--libsowatch/notificationsmodel.cpp94
-rw-r--r--libsowatch/notificationsmodel.h19
-rw-r--r--libsowatch/watchserver.cpp196
-rw-r--r--libsowatch/watchserver.h10
-rw-r--r--metawatch/metawatchdigitalsimulator.cpp1
-rw-r--r--metawatch/qml/com/javispedro/sowatch/metawatch/MWListView.qml11
-rw-r--r--notificationswatchlet/metawatch-digital.qml24
-rw-r--r--testnotification/testnotification.cpp5
-rw-r--r--testnotification/testnotificationprovider.cpp4
12 files changed, 244 insertions, 142 deletions
diff --git a/libsowatch/declarativewatchlet.cpp b/libsowatch/declarativewatchlet.cpp
index 510a68f..1851e38 100644
--- a/libsowatch/declarativewatchlet.cpp
+++ b/libsowatch/declarativewatchlet.cpp
@@ -23,7 +23,9 @@ DeclarativeWatchlet::DeclarativeWatchlet(WatchServer* server, const QString& id)
if (!_registered) {
qmlRegisterUncreatableType<DeclarativeWatchWrapper>("com.javispedro.sowatch", 1, 0,
- "Watch", "Watch is only available via the 'watch' object");
+ "Watch", "Watch is only available via the 'watch' context property");
+ qmlRegisterUncreatableType<NotificationsModel>("com.javispedro.sowatch", 1, 0,
+ "NotificationsModel", "NotificationsModel is only available via the 'notifications' context property");
qmlRegisterType<ConfigKey>();
qmlRegisterType<GConfKey>("com.javispedro.sowatch", 1, 0, "GConfKey");
_registered = true;
@@ -40,6 +42,8 @@ DeclarativeWatchlet::DeclarativeWatchlet(WatchServer* server, const QString& id)
_wrapper = new DeclarativeWatchWrapper(server, server->watch(), this);
_engine->rootContext()->setContextProperty("watch", _wrapper);
+ _engine->rootContext()->setContextProperty("notifications",
+ const_cast<NotificationsModel*>(server->notifications()));
}
DeclarativeWatchlet::~DeclarativeWatchlet()
diff --git a/libsowatch/declarativewatchwrapper.cpp b/libsowatch/declarativewatchwrapper.cpp
index ebf92b5..968c3e2 100644
--- a/libsowatch/declarativewatchwrapper.cpp
+++ b/libsowatch/declarativewatchwrapper.cpp
@@ -23,19 +23,6 @@ bool DeclarativeWatchWrapper::active() const
return _active;
}
-QList<QObject*> DeclarativeWatchWrapper::notifications() const
-{
- // TODO: Figure out a better way for this; QAbstractListModel, etc.
- QList<Notification*> nl = _server->liveNotifications();
- QList<QObject*> ol;
- foreach (Notification* n, nl) {
- QObject * o = n;
- ol.append(o);
- }
- qDebug() << "notifications to declarative: " << ol;
- return ol;
-}
-
void DeclarativeWatchWrapper::vibrate(int msecs)
{
if (_active) {
diff --git a/libsowatch/declarativewatchwrapper.h b/libsowatch/declarativewatchwrapper.h
index 8d4fd7d..74f569c 100644
--- a/libsowatch/declarativewatchwrapper.h
+++ b/libsowatch/declarativewatchwrapper.h
@@ -17,7 +17,6 @@ class SOWATCH_EXPORT DeclarativeWatchWrapper : public QObject
Q_OBJECT
Q_PROPERTY(QString model READ model CONSTANT)
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
- Q_PROPERTY(QList<QObject*> notifications READ notifications NOTIFY notificationsChanged)
public:
explicit DeclarativeWatchWrapper(WatchServer *server, Watch *watch, QObject *parent = 0);
@@ -25,8 +24,6 @@ public:
QString model() const;
bool active() const;
- QList<QObject*> notifications() const;
-
public slots:
void vibrate(int msecs);
diff --git a/libsowatch/notificationsmodel.cpp b/libsowatch/notificationsmodel.cpp
index 2eba9a0..73d4ab6 100644
--- a/libsowatch/notificationsmodel.cpp
+++ b/libsowatch/notificationsmodel.cpp
@@ -12,6 +12,12 @@ using namespace sowatch;
NotificationsModel::NotificationsModel(QObject *parent) :
QAbstractListModel(parent)
{
+ QHash<int, QByteArray> roles = roleNames();
+ roles[Qt::DisplayRole] = QByteArray("title");
+ roles[ObjectRole] = QByteArray("object");
+ roles[BodyRole] = QByteArray("body");
+ roles[CountRole] = QByteArray("count");
+ setRoleNames(roles);
}
int NotificationsModel::rowCount(const QModelIndex &parent) const
@@ -26,15 +32,51 @@ int NotificationsModel::rowCount(const QModelIndex &parent) const
QVariant NotificationsModel::data(const QModelIndex &index, int role) const
{
+ const Notification *n = getNotificationByIndex(index.row());
+ if (!n) return QVariant();
+ switch (role) {
+ case Qt::DisplayRole:
+ return QVariant::fromValue(n->title());
+ case ObjectRole:
+ return QVariant::fromValue(const_cast<sowatch::Notification*>(n));
+ case BodyRole:
+ return QVariant::fromValue(n->body());
+ case CountRole:
+ return QVariant::fromValue(n->count());
+ }
+ return QVariant();
}
void NotificationsModel::add(Notification *n)
{
+ const Notification::Type type = n->type();
+ const int offset = getAppendOffsetForType(type);
+
+ beginInsertRows(QModelIndex(), offset, offset);
+ _list[type].append(n);
+ endInsertRows();
+
+ connect(n, SIGNAL(changed()), SLOT(handleNotificationChanged()));
}
void NotificationsModel::remove(Notification *n)
{
+ const Notification::Type type = n->type();
+ remove(type, n);
+}
+
+void NotificationsModel::remove(Notification::Type type, Notification *n)
+{
+ const int subindex = _list[type].indexOf(n);
+ const int index = getOffsetForType(type) + subindex;
+
+ Q_ASSERT(index >= 0);
+
+ disconnect(n, 0, this, 0);
+ beginRemoveRows(QModelIndex(), index, index);
+ _list[type].removeAt(subindex);
+ endRemoveRows();
}
int NotificationsModel::fullCount() const
@@ -55,16 +97,62 @@ int NotificationsModel::fullCountByType(Notification::Type type) const
return count;
}
-bool NotificationsModel::removeDeletedNotification(Notification *n)
+Notification::Type NotificationsModel::getTypeOfDeletedNotification(Notification *n) const
{
// Can't call any methods of 'n'
+ FOREACH_TYPE(type) {
+ if (_list[type].contains(n)) {
+ return type;
+ }
+ }
+ return Notification::OtherNotification;
}
-int NotificationsModel::getOffsetForType(Notification::Type type)
+int NotificationsModel::getOffsetForType(Notification::Type type) const
{
int count = 0;
FOREACH_TYPE_UNTIL(t, type) {
- count += _list[type].count();
+ count += _list[t].count();
}
return count;
}
+
+int NotificationsModel::getAppendOffsetForType(Notification::Type type) const
+{
+ return getOffsetForType(type) + _list[type].count();
+}
+
+int NotificationsModel::getIndexForNotification(Notification *n) const
+{
+ Notification::Type type = n->type();
+ const int subindex = _list[type].indexOf(n);
+
+ Q_ASSERT(subindex >= 0);
+
+ return getOffsetForType(type) + subindex;
+}
+
+const Notification * NotificationsModel::getNotificationByIndex(int index) const
+{
+ FOREACH_TYPE(type) {
+ const int size = _list[type].size();
+ if (index < size) {
+ return _list[type].at(index);
+ } else {
+ index -= size;
+ }
+ }
+ qWarning() << "Notification with index" << index << "not found";
+ return 0;
+}
+
+void NotificationsModel::handleNotificationChanged()
+{
+ QObject *obj = sender();
+ if (obj) {
+ Notification* n = static_cast<Notification*>(obj);
+ const int index = getIndexForNotification(n);
+
+ emit dataChanged(createIndex(index, 0), createIndex(index, 0));
+ }
+}
diff --git a/libsowatch/notificationsmodel.h b/libsowatch/notificationsmodel.h
index 5e7e029..6aabf71 100644
--- a/libsowatch/notificationsmodel.h
+++ b/libsowatch/notificationsmodel.h
@@ -14,19 +14,32 @@ class NotificationsModel : public QAbstractListModel
public:
explicit NotificationsModel(QObject *parent = 0);
+ enum DataRoles {
+ ObjectRole = Qt::UserRole,
+ BodyRole,
+ CountRole
+ };
+
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
void add(Notification *n);
void remove(Notification *n);
+ void remove(Notification::Type type, Notification *n);
int fullCount() const;
int fullCountByType(Notification::Type type) const;
- bool removeDeletedNotification(Notification *n);
+ Notification::Type getTypeOfDeletedNotification(Notification *n) const;
private:
- int getOffsetForType(Notification::Type type);
+ int getOffsetForType(Notification::Type type) const;
+ int getAppendOffsetForType(Notification::Type type) const;
+ int getIndexForNotification(Notification *n) const;
+ const Notification* getNotificationByIndex(int index) const;
+
+private slots:
+ void handleNotificationChanged();
private:
QList<Notification*> _list[Notification::TypeCount];
@@ -34,4 +47,6 @@ private:
}
+QML_DECLARE_TYPE(sowatch::NotificationsModel)
+
#endif // SOWATCH_NOTIFICATIONSMODEL_H
diff --git a/libsowatch/watchserver.cpp b/libsowatch/watchserver.cpp
index a6b5886..01af063 100644
--- a/libsowatch/watchserver.cpp
+++ b/libsowatch/watchserver.cpp
@@ -11,6 +11,7 @@ WatchServer::WatchServer(Watch* watch, QObject* parent) :
QObject(parent), _watch(watch),
_nextWatchletButton(-1),
_oldNotificationThreshold(300),
+ _notifications(new NotificationsModel(this)),
_currentWatchlet(0), _currentWatchletActive(false), _currentWatchletIndex(-1),
_syncTimeTimer(new QTimer(this))
{
@@ -97,15 +98,66 @@ void WatchServer::removeProvider(const NotificationProvider *provider)
this, SLOT(postNotification(Notification*)));
}
-QList<Notification*> WatchServer::liveNotifications()
+const NotificationsModel * WatchServer::notifications() const
{
- QList<Notification*> notifications;
+ return _notifications;
+}
+
+void WatchServer::postNotification(Notification *notification)
+{
+ const Notification::Type type = notification->type();
+
+ // Add notification to model
+ _notifications->add(notification);
+ _notificationCounts[notification] = notification->count();
+
+ connect(notification, SIGNAL(changed()), SLOT(handleNotificationChanged()));
+ connect(notification, SIGNAL(dismissed()), SLOT(handleNotificationDismissed()));
+ connect(notification, SIGNAL(destroyed()), SLOT(handleNotificationDestroyed()));
- for (int i = 0; i < Notification::TypeCount; i++) {
- notifications.append(_notifications[i]);
+ qDebug() << "notification received" << notification->title() << "(" << notification->count() << ")";
+
+ _watch->updateNotificationCount(type, getNotificationCount(type));
+
+ if (type == Notification::WeatherNotification) {
+ // Weather notifications, we handle differently.
+ WeatherNotification* weather = static_cast<WeatherNotification*>(notification);
+ _weather = weather;
+ _watch->updateWeather(weather);
+ return; // And do not display it the usual way
}
- return notifications;
+ QDateTime oldThreshold = QDateTime::currentDateTime().addSecs(-_oldNotificationThreshold);
+ if (notification->dateTime() < oldThreshold) {
+ return; // Do not care about notifications that old...
+ }
+
+ if (_pendingNotifications.isEmpty()) {
+ _pendingNotifications.enqueue(notification);
+ nextNotification();
+ } else if (type == Notification::CallNotification) {
+ // Oops, priority!!!!
+ _pendingNotifications.prepend(notification);
+ nextNotification();
+ } else {
+ _pendingNotifications.enqueue(notification);
+ }
+}
+
+void WatchServer::nextNotification()
+{
+ if (!_watch->isConnected()) return;
+ if (!_pendingNotifications.empty()) {
+ Notification *n = _pendingNotifications.head();
+ if (_currentWatchlet && _currentWatchletActive) {
+ deactivateCurrentWatchlet();
+ }
+ _watch->displayNotification(n);
+ } else if (_currentWatchlet) {
+ reactivateCurrentWatchlet();
+ } else {
+ goToIdle();
+ }
}
void WatchServer::runWatchlet(Watchlet *watchlet)
@@ -182,11 +234,33 @@ void WatchServer::syncTime()
uint WatchServer::getNotificationCount(Notification::Type type)
{
- uint count = 0;
- foreach (Notification* n, _notifications[type]) {
- count += n->count();
+ return _notifications->fullCountByType(type);
+}
+
+void WatchServer::removeNotification(Notification::Type type, Notification *n)
+{
+ // Warning: This function might be called with n being deleted.
+ _notifications->remove(type, n);
+ _notificationCounts.remove(n);
+
+ _watch->updateNotificationCount(type, getNotificationCount(type));
+
+ if (!_pendingNotifications.isEmpty() && _pendingNotifications.head() == n) {
+ qDebug() << "removing top notification";
+ _pendingNotifications.removeAll(n);
+ nextNotification();
+ } else {
+ _pendingNotifications.removeAll(n);
}
- return count;
+ if (type == Notification::WeatherNotification) {
+ WeatherNotification* w = static_cast<WeatherNotification*>(n);
+ if (_weather == w) {
+ _weather = 0;
+ }
+ }
+
+ // No longer interested in this notification
+ disconnect(n, 0, this, 0);
}
void WatchServer::goToIdle()
@@ -242,61 +316,6 @@ void WatchServer::handleWatchButtonPress(int button)
}
}
-void WatchServer::postNotification(Notification *notification)
-{
- const Notification::Type type = notification->type();
- _notifications[type].append(notification);
- _notificationCounts[notification] = notification->count();
-
- connect(notification, SIGNAL(changed()), SLOT(handleNotificationChanged()));
- connect(notification, SIGNAL(dismissed()), SLOT(handleNotificationDismissed()));
- connect(notification, SIGNAL(destroyed()), SLOT(handleNotificationDestroyed()));
-
- qDebug() << "notification received" << notification->title() << "(" << notification->count() << ")";
-
- _watch->updateNotificationCount(type, getNotificationCount(type));
-
- if (type == Notification::WeatherNotification) {
- // Weather notifications, we handle differently.
- WeatherNotification* weather = static_cast<WeatherNotification*>(notification);
- _weather = weather;
- _watch->updateWeather(weather);
- return; // And do not display it the usual way
- }
-
- QDateTime oldThreshold = QDateTime::currentDateTime().addSecs(-_oldNotificationThreshold);
- if (notification->dateTime() < oldThreshold) {
- return; // Do not care about notifications that old...
- }
-
- if (_pendingNotifications.isEmpty()) {
- _pendingNotifications.enqueue(notification);
- nextNotification();
- } else if (type == Notification::CallNotification) {
- // Oops, priority!!!!
- _pendingNotifications.prepend(notification);
- nextNotification();
- } else {
- _pendingNotifications.enqueue(notification);
- }
-}
-
-void WatchServer::nextNotification()
-{
- if (!_watch->isConnected()) return;
- if (!_pendingNotifications.empty()) {
- Notification *n = _pendingNotifications.head();
- if (_currentWatchlet && _currentWatchletActive) {
- deactivateCurrentWatchlet();
- }
- _watch->displayNotification(n);
- } else if (_currentWatchlet) {
- reactivateCurrentWatchlet();
- } else {
- goToIdle();
- }
-}
-
void WatchServer::handleNotificationChanged()
{
QObject *obj = sender();
@@ -346,29 +365,8 @@ void WatchServer::handleNotificationDismissed()
if (obj) {
Notification* n = static_cast<Notification*>(obj);
const Notification::Type type = n->type();
- _notifications[type].removeOne(n);
- _notificationCounts.remove(n);
-
qDebug() << "notification dismissed" << n->title() << "(" << n->count() << ")";
-
- _watch->updateNotificationCount(type, getNotificationCount(type));
-
- if (!_pendingNotifications.isEmpty() && _pendingNotifications.head() == n) {
- qDebug() << "removing top notification";
- _pendingNotifications.removeAll(n);
- nextNotification();
- } else {
- _pendingNotifications.removeAll(n);
- }
- if (type == Notification::WeatherNotification) {
- WeatherNotification* w = static_cast<WeatherNotification*>(n);
- if (_weather == w) {
- _weather = 0;
- }
- }
-
- // No longer interested in this notification
- disconnect(n, 0, this, 0);
+ removeNotification(type, n);
}
}
@@ -380,30 +378,8 @@ void WatchServer::handleNotificationDestroyed()
// Cannot call any methods of n; it is a dangling pointer now.
if (_notificationCounts.contains(n)) {
qWarning() << "Notification destroyed without being dismissed!";
- _notificationCounts.remove(n);
-
- for (int i = 0; i < Notification::TypeCount; i++) {
- Notification::Type type = static_cast<Notification::Type>(i);
- if (_notifications[type].contains(n)) {
- _notifications[type].removeAll(n);
- _watch->updateNotificationCount(type, getNotificationCount(type));
-
- if (type == Notification::WeatherNotification) {
- WeatherNotification* w = static_cast<WeatherNotification*>(n);
- if (_weather == w) {
- _weather = 0;
- }
- }
- }
- }
-
- if (!_pendingNotifications.isEmpty() && _pendingNotifications.head() == n) {
- qDebug() << "removing top notification";
- _pendingNotifications.removeAll(n);
- nextNotification();
- } else {
- _pendingNotifications.removeAll(n);
- }
+ Notification::Type type = _notifications->getTypeOfDeletedNotification(n);
+ removeNotification(type, n);
}
}
}
diff --git a/libsowatch/watchserver.h b/libsowatch/watchserver.h
index af2a8de..91f9b4e 100644
--- a/libsowatch/watchserver.h
+++ b/libsowatch/watchserver.h
@@ -8,7 +8,7 @@
#include <QtCore/QTimer>
#include "sowatch_global.h"
-#include "notification.h"
+#include "notificationsmodel.h"
namespace sowatch
{
@@ -41,7 +41,7 @@ public:
void removeProvider(const NotificationProvider *provider);
/** Get a list of all current live notifications. */
- QList<Notification*> liveNotifications();
+ const NotificationsModel * notifications() const;
public slots:
void postNotification(Notification *notification);
@@ -71,8 +71,8 @@ private:
/** Stores all the watchlets with a given watchled id. */
QMap<QString, Watchlet*> _watchletIds;
- /** Stores current live notifications, classified by type. */
- QList<Notification*> _notifications[Notification::TypeCount];
+ /** Stores current live notifications. */
+ NotificationsModel *_notifications;
/** A list of notifications that are yet to be shown to the user. */
QQueue<Notification*> _pendingNotifications;
/** Stores the count of notifications hidden between each notification object. */
@@ -92,6 +92,8 @@ private:
/** Counts all notifications from a given type. */
uint getNotificationCount(Notification::Type type);
+ /** Remove a notification of a certain type. */
+ void removeNotification(Notification::Type type, Notification* n);
void deactivateCurrentWatchlet();
void reactivateCurrentWatchlet();
diff --git a/metawatch/metawatchdigitalsimulator.cpp b/metawatch/metawatchdigitalsimulator.cpp
index f12e987..5c399bd 100644
--- a/metawatch/metawatchdigitalsimulator.cpp
+++ b/metawatch/metawatchdigitalsimulator.cpp
@@ -116,4 +116,5 @@ void MetaWatchDigitalSimulator::retryConnect()
void MetaWatchDigitalSimulator::send(const Message &msg)
{
// Do not send messages
+ Q_UNUSED(msg);
}
diff --git a/metawatch/qml/com/javispedro/sowatch/metawatch/MWListView.qml b/metawatch/qml/com/javispedro/sowatch/metawatch/MWListView.qml
index 6af7b18..e16869f 100644
--- a/metawatch/qml/com/javispedro/sowatch/metawatch/MWListView.qml
+++ b/metawatch/qml/com/javispedro/sowatch/metawatch/MWListView.qml
@@ -67,6 +67,17 @@ ListView {
}
}
+ function scrollTop() {
+ if (count == 0) {
+ return;
+ }
+ if (selectable) {
+ currentIndex = 0;
+ }
+ positionViewAtIndex(0, ListView.Beginning);
+
+ }
+
Rectangle {
id: indicatorCont
visible: list.indicator && (list.contentHeight > list.height)
diff --git a/notificationswatchlet/metawatch-digital.qml b/notificationswatchlet/metawatch-digital.qml
index 57a6176..fcd1081 100644
--- a/notificationswatchlet/metawatch-digital.qml
+++ b/notificationswatchlet/metawatch-digital.qml
@@ -18,7 +18,7 @@ MWPage {
anchors.right: parent.right
anchors.bottom: parent.bottom
clip: true
- model: watch.notifications
+ model: notifications
delegate: Rectangle {
id: notifDelegate
@@ -30,14 +30,14 @@ MWPage {
width: parent.width
MWLabel {
width: parent.width
- text: model.modelData.title
+ text: title
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: notifDelegate.selected ? "white" : "black"
font.pointSize: 12
}
MWLabel {
width: parent.width
- text: model.modelData.body
+ text: body
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: notifDelegate.selected ? "white" : "black"
}
@@ -56,7 +56,7 @@ MWPage {
Connections {
target: watch
- onButtonPressed : {
+ onButtonPressed: {
switch (button) {
case 1:
notifs.scrollUp();
@@ -67,4 +67,20 @@ MWPage {
}
}
}
+
+ Connections {
+ target: notifications
+ onRowsInserted: {
+ if (!watch.active) {
+ // Always position into the topmost notification if
+ // user is not looking at this list
+ notifs.scrollTop();
+ }
+ }
+ onRowsRemoved: {
+ if (!watch.active) {
+ notifs.scrollTop();
+ }
+ }
+ }
}
diff --git a/testnotification/testnotification.cpp b/testnotification/testnotification.cpp
index fa904b9..d541287 100644
--- a/testnotification/testnotification.cpp
+++ b/testnotification/testnotification.cpp
@@ -8,6 +8,9 @@ TestNotification::TestNotification(Type type, const QString &title, const QStrin
_time(QDateTime::currentDateTime()),
_title(title), _body(body)
{
+ const int high = 30000, low = 10000;
+ int rand = qrand() % ((high + 1) - low) + low;
+ QTimer::singleShot(rand, this, SIGNAL(dismissed()));
}
Notification::Type TestNotification::type() const
@@ -44,5 +47,3 @@ void TestNotification::dismiss()
{
deleteLater(); // We do not want to keep those around.
}
-
-
diff --git a/testnotification/testnotificationprovider.cpp b/testnotification/testnotificationprovider.cpp
index f66ae89..230237f 100644
--- a/testnotification/testnotificationprovider.cpp
+++ b/testnotification/testnotificationprovider.cpp
@@ -13,6 +13,10 @@ TestNotificationProvider::TestNotificationProvider(QObject *parent) :
QTimer::singleShot(1200, this, SLOT(generateNotification()));
QTimer::singleShot(1400, this, SLOT(generateNotification()));
QTimer::singleShot(1600, this, SLOT(generateNotification()));
+ QTimer::singleShot(1800, this, SLOT(generateNotification()));
+ QTimer::singleShot(2000, this, SLOT(generateNotification()));
+ QTimer::singleShot(2200, this, SLOT(generateNotification()));
+ QTimer::singleShot(2400, this, SLOT(generateInitialNotification()));
connect(_timer, SIGNAL(timeout()), SLOT(generateNotification()));
_timer->setInterval(60000);
//_timer->start();