summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-09-04 01:55:14 +0200
committerJavier <dev.git@javispedro.com>2014-09-04 01:55:14 +0200
commit1adf7f1bcde493ccaacedb0d9778911ad69ff335 (patch)
tree2a1c4cffb09a4406a59df25853a726a7aa86fb28 /src
downloadsalmeta-1adf7f1bcde493ccaacedb0d9778911ad69ff335.tar.gz
salmeta-1adf7f1bcde493ccaacedb0d9778911ad69ff335.zip
Initial import
Diffstat (limited to 'src')
-rw-r--r--src/controller.cpp84
-rw-r--r--src/controller.h53
-rw-r--r--src/metawatch.cpp183
-rw-r--r--src/metawatch.h101
-rw-r--r--src/metawatchbletransport.cpp141
-rw-r--r--src/metawatchbletransport.h46
-rw-r--r--src/metawatchtransport.cpp6
-rw-r--r--src/metawatchtransport.h25
-rw-r--r--src/notificationmonitor.cpp6
-rw-r--r--src/notificationmonitor.h18
-rw-r--r--src/reconnecttimer.cpp64
-rw-r--r--src/reconnecttimer.h35
-rw-r--r--src/salmeta.cpp43
-rw-r--r--src/widgetinfo.cpp77
-rw-r--r--src/widgetinfo.h72
-rw-r--r--src/widgetview.cpp50
-rw-r--r--src/widgetview.h30
17 files changed, 1034 insertions, 0 deletions
diff --git a/src/controller.cpp b/src/controller.cpp
new file mode 100644
index 0000000..02c8252
--- /dev/null
+++ b/src/controller.cpp
@@ -0,0 +1,84 @@
+#include <QtCore/QDebug>
+#include <QtCore/QDateTime>
+
+#include "controller.h"
+
+static const QLatin1String setting_address("address");
+static const QLatin1String setting_num_pages("num-pages");
+static const QLatin1String setting_page("page%1");
+static const QLatin1String setting_page_widget("widget%1");
+
+Controller::Controller(const QString &settingsPrefix, QQuickView *view, QObject *parent) :
+ QObject(parent),
+ _settings(new MDConfGroup(this)),
+ _metawatch(0),
+ _reconnect(new ReconnectTimer(this)),
+ _view(new WidgetView(settingsPrefix, view, this)),
+ _batteryCharge(0), _batteryCharging(false)
+{
+ _settings->setPath(settingsPrefix);
+ _metawatch = new MetaWatch(_settings->value(setting_address).toString(), this);
+
+ connect(_settings, &MDConfGroup::valueChanged, this, &Controller::handleSettingChanged);
+ connect(_metawatch, &MetaWatch::connected, _reconnect, &ReconnectTimer::stop);
+ connect(_metawatch, &MetaWatch::connected, this, &Controller::handleMetaWatchConnected);
+ connect(_metawatch, &MetaWatch::disconnected, _reconnect, &ReconnectTimer::start);
+ connect(_metawatch, &MetaWatch::batteryStatus, this, &Controller::handleMetaWatchBatteryStatus);
+ connect(_reconnect, &ReconnectTimer::tick, _metawatch, &MetaWatch::connectDevice);
+ _reconnect->start();
+
+ reloadPages();
+}
+
+int Controller::batteryCharge() const
+{
+ return _batteryCharge;
+}
+
+bool Controller::batteryCharging() const
+{
+ return _batteryCharging;
+}
+
+void Controller::reloadPages()
+{
+
+}
+
+void Controller::switchToPage(int page)
+{
+
+}
+
+void Controller::handleSettingChanged(const QString &key)
+{
+ qDebug() << "Setting" << key << "changed";
+ if (key == setting_num_pages) {
+ _settings->value(setting_num_pages, 0);
+ switchToPage(0);
+ }
+}
+
+void Controller::handleMetaWatchConnected()
+{
+ qDebug() << "MetaWatch connected, synchronizing date time";
+
+ _metawatch->updateDeviceType();
+ _metawatch->updateBatteryStatus();
+ _metawatch->setDateTime(QDateTime::currentDateTime());
+ _metawatch->updateLcdDisplay();
+
+ _metawatch->updateWidgetList(_view->widgets());
+}
+
+void Controller::handleMetaWatchBatteryStatus(bool charging, int charge)
+{
+ if (_batteryCharging != charging) {
+ _batteryCharging = charging;
+ emit batteryChargingChanged();
+ }
+ if (_batteryCharge != charge) {
+ _batteryCharge = charge;
+ emit batteryChargeChanged();
+ }
+}
diff --git a/src/controller.h b/src/controller.h
new file mode 100644
index 0000000..d7f2349
--- /dev/null
+++ b/src/controller.h
@@ -0,0 +1,53 @@
+#ifndef DAEMON_H
+#define DAEMON_H
+
+#include <QObject>
+#include <QQuickView>
+#include <MDConfGroup>
+
+#include "metawatch.h"
+#include "widgetview.h"
+#include "reconnecttimer.h"
+
+class Controller : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int batteryCharge READ batteryCharge NOTIFY batteryChargeChanged)
+ Q_PROPERTY(bool batteryCharging READ batteryCharging NOTIFY batteryChargingChanged)
+
+public:
+ Controller(const QString &settingsPrefix, QQuickView *view = 0, QObject *parent = 0);
+
+ int batteryCharge() const;
+ bool batteryCharging() const;
+
+signals:
+ void batteryChargeChanged();
+ void batteryChargingChanged();
+
+public slots:
+ void reloadPages();
+ void switchToPage(int page);
+
+private:
+ static int parseClockUrl(const QUrl &url);
+
+private slots:
+ void handleSettingChanged(const QString &key);
+ void handleMetaWatchConnected();
+ void handleMetaWatchBatteryStatus(bool charging, int charge);
+
+private:
+ MDConfGroup *_settings;
+ MetaWatch *_metawatch;
+ ReconnectTimer *_reconnect;
+
+ WidgetView *_view;
+
+ // Watch status
+ int _batteryCharge;
+ bool _batteryCharging;
+};
+
+#endif // DAEMON_H
diff --git a/src/metawatch.cpp b/src/metawatch.cpp
new file mode 100644
index 0000000..a27d0ec
--- /dev/null
+++ b/src/metawatch.cpp
@@ -0,0 +1,183 @@
+#include <QtCore/QDataStream>
+#include <QtCore/QDateTime>
+#include <QtCore/QDebug>
+
+#include "metawatch.h"
+#include "metawatchbletransport.h"
+
+MetaWatch::MetaWatch(const QString &btAddr, QObject *parent) :
+ QObject(parent)
+{
+ GatoPeripheral *peripheral = new GatoPeripheral(GatoAddress(btAddr), this);
+ _transport = new MetaWatchBLETransport(peripheral, this);
+
+ connect(_transport, &MetaWatchTransport::connected, this, &MetaWatch::connected);
+ connect(_transport, &MetaWatchTransport::disconnected, this, &MetaWatch::disconnected);
+ connect(_transport, &MetaWatchTransport::messageReceived, this, &MetaWatch::handleTransportMessage);
+}
+
+QList<QUrl> MetaWatch::availableClocks()
+{
+ QList<QUrl> clocks;
+
+ for (int i = 0; i < 6; i++) {
+ clocks << QUrl("clock://" + QString::number(i));
+ }
+
+ return clocks;
+}
+
+void MetaWatch::setDateTime(const QDateTime &dt)
+{
+ const QDate &date = dt.date();
+ const QTime &time = dt.time();
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::BigEndian);
+ s << quint16(date.year()) << quint8(date.month()) << quint8(date.day());
+ s << quint8(date.dayOfWeek() % 7);
+ s << quint8(time.hour()) << quint8(time.minute()) << quint8(time.second());
+
+ _transport->sendMessage(MessageSetRealTimeClock, 0, data);
+}
+
+void MetaWatch::configure(WatchProperties props)
+{
+ _transport->sendMessage(MessageWatchPropertyOperation, props | WatchPropertyOperationWrite, QByteArray());
+}
+
+void MetaWatch::updateDeviceType()
+{
+ // Also disables HFP/MAP connection.
+ _transport->sendMessage(MessageGetDeviceType, 0xC0, QByteArray());
+}
+
+void MetaWatch::updateBatteryStatus()
+{
+ _transport->sendMessage(MessageGetBatteryStatus, 0, QByteArray());
+}
+
+void MetaWatch::updateLcdDisplay()
+{
+ // Switches to UiGen2 mode and to page 0.
+ _transport->sendMessage(MessageUpdateLcdDisplay, 0x80, QByteArray());
+}
+
+void MetaWatch::updateWidgetList(const QList<WidgetInfo*> &widgets)
+{
+ int num_widgets = 0;
+
+ // Count valid widgets
+ for (int w = 0; w < widgets.size(); w++) {
+ const WidgetInfo *info = widgets[w];
+ if (info->valid()) num_widgets++;
+ }
+
+ const int max_widgets_in_one_msg = 7;
+ const int num_messages = (num_widgets + (max_widgets_in_one_msg - 1)) / max_widgets_in_one_msg;
+ int num_message = 0;
+ QByteArray msg;
+
+ Q_ASSERT(num_messages < 4);
+
+ msg.reserve(max_widgets_in_one_msg * 2);
+
+ if (num_widgets == 0) {
+ msg.append(static_cast<char>(0xFF));
+ msg.append(static_cast<char>(0x00));
+
+ // Clear all widgets
+ _transport->sendMessage(MessageSetWidgetList, 0x4, msg);
+
+ return;
+ }
+
+ qDebug() << "Msgs" << num_messages << num_message;
+
+ for (int w = 0; w < num_widgets; w++) {
+ WidgetInfo *info = widgets[w];
+ if (!info->valid()) continue; // Skip disabled/empty widget
+
+ quint8 id = w;
+ quint8 options = ((info->page() << 4) & 0x30)
+ | ((info->size() << 2) & 0xC) | ((info->position() << 2) & 0x3);
+
+ if (info->url().scheme() == "clock") {
+ id |= (clockUrlToClockId(info->url()) << 4) & 0xF0;
+ options |= 0x80;
+ }
+
+ if (info->invert()) {
+ options |= 0x40;
+ }
+
+ qDebug() << QString::number(id, 16) << QString::number(options, 16);
+
+ msg.append(id);
+ msg.append(options);
+
+ if (msg.size() >= max_widgets_in_one_msg * 2) {
+ Q_ASSERT(num_message < num_messages);
+ _transport->sendMessage(MessageSetWidgetList,
+ ((num_messages << 2) & 0xC) | (num_message & 0x3),
+ msg);
+
+ msg.clear();
+ msg.reserve(max_widgets_in_one_msg * 2);
+ num_message++;
+ }
+ }
+
+ if (msg.size() > 0) {
+ Q_ASSERT(num_message < num_messages);
+ _transport->sendMessage(MessageSetWidgetList,
+ ((num_messages << 2) & 0xC) | (num_message & 0x3),
+ msg);
+ }
+}
+
+void MetaWatch::connectDevice()
+{
+ _transport->connectDevice();
+}
+
+void MetaWatch::disconnectDevice()
+{
+ _transport->disconnectDevice();
+}
+
+int MetaWatch::clockUrlToClockId(const QUrl &url)
+{
+ if (url.scheme() != "clock")
+ return -1;
+
+ QString host = url.host();
+ if (!host.startsWith("clock"))
+ return -1;
+
+ return host.mid(5).toInt();
+}
+
+void MetaWatch::handleTransportMessage(quint8 type, quint8 options, const QByteArray &payload)
+{
+ switch (type) {
+ case MessageGetDeviceTypeResponse:
+ emit deviceType(static_cast<DeviceType>(options & 0xF));
+ break;
+ case MessageModeChangeIndication:
+ qDebug() << "Got mode change indication";
+ break;
+ case MessageReadBatteryStatusResponse:
+ if (payload.size() < 6) {
+ qWarning() << "Invalid battery status response size";
+ }
+ emit batteryStatus(payload[1], payload[2]);
+ break;
+ case MessageConnectionChange:
+ case MessageIntervalChanged:
+ // No idea what to do with these.
+ break;
+ default:
+ qWarning() << "Unknown message type received:" << QString::number(type, 16);
+ }
+}
diff --git a/src/metawatch.h b/src/metawatch.h
new file mode 100644
index 0000000..dd02bf4
--- /dev/null
+++ b/src/metawatch.h
@@ -0,0 +1,101 @@
+#ifndef METAWATCH_H
+#define METAWATCH_H
+
+#include <QObject>
+#include <QBluetoothAddress>
+
+QT_USE_NAMESPACE_BLUETOOTH
+
+#include "metawatchtransport.h"
+#include "widgetinfo.h"
+
+class MetaWatch : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(MessageTypes WatchProperty UiStyle)
+ Q_FLAGS(WatchProperties)
+
+public:
+ explicit MetaWatch(const QString &btAddr, QObject *parent = 0);
+
+ enum MessageTypes {
+ MessageGetDeviceType = 0x01,
+ MessageGetDeviceTypeResponse = 0x02,
+
+ MessageSetRealTimeClock = 0x26,
+
+ MessageWatchPropertyOperation = 0x30,
+
+ MessageModeChangeIndication = 0x33,
+
+ MessageUpdateLcdDisplay = 0x43,
+
+ MessageGetBatteryStatus = 0x56,
+ MessageReadBatteryStatusResponse = 0x57,
+
+ MessageSetWidgetList = 0xA1,
+
+ MessageSetupAccelerometer = 0xE1,
+ MessageAccelerometerDataResponse = 0xE0,
+
+ // Messages from the propietary BT stack
+ MessageConnectionChange = 0xB9,
+ MessageIntervalChanged = 0xBB
+ };
+
+ enum DeviceType {
+ DeviceMetaWatchDigitalGen1 = 2,
+ DeviceMetaWatchDigitalGen2 = 5
+ };
+
+ enum WatchProperty {
+ WatchPropertyHourFormat12h = 0,
+ WatchPropertyHourFormat24h = 1,
+ WatchPropertyDateFormatMMDD = 0 << 1,
+ WatchPropertyDateFormatDDMM = 1 << 1,
+ WatchPropertyShowSeconds = 1 << 2,
+ WatchPropertyShowSeparationLines = 1 << 3,
+ WatchPropertyAutobacklight = 1 << 4,
+
+ WatchPropertyOperationRead = 0 << 7,
+ WatchPropertyOperationWrite = 1 << 7
+ };
+ Q_DECLARE_FLAGS(WatchProperties, WatchProperty)
+
+ enum UiStyle {
+ UiGen1 = 0,
+ UiGen2 = 1
+ };
+
+ static QList<QUrl> availableClocks();
+
+ void setDateTime(const QDateTime &dt);
+ void configure(WatchProperties props);
+
+ void updateDeviceType();
+ void updateBatteryStatus();
+ void updateLcdDisplay(); // TODO: More overloads
+ void updateWidgetList(const QList<WidgetInfo*>& widgets);
+
+signals:
+ void connected();
+ void disconnected();
+
+ void deviceType(DeviceType type);
+ void batteryStatus(bool charging, int charge);
+
+public slots:
+ void connectDevice();
+ void disconnectDevice();
+
+private:
+ static int clockUrlToClockId(const QUrl &url);
+
+private slots:
+ void handleTransportMessage(quint8 type, quint8 options, const QByteArray &payload);
+
+private:
+ MetaWatchTransport *_transport;
+};
+
+#endif // METAWATCH_H
diff --git a/src/metawatchbletransport.cpp b/src/metawatchbletransport.cpp
new file mode 100644
index 0000000..56039d3
--- /dev/null
+++ b/src/metawatchbletransport.cpp
@@ -0,0 +1,141 @@
+#include "metawatchbletransport.h"
+
+const GatoUUID MetaWatchBLETransport::ServiceUuid(quint16(0x8880));
+const GatoUUID MetaWatchBLETransport::InputCharacteristicUuid(quint16(0x8882));
+const GatoUUID MetaWatchBLETransport::OutputCharacteristicUuid(quint16(0x8881));
+
+MetaWatchBLETransport::MetaWatchBLETransport(GatoPeripheral *peripheral, QObject *parent) :
+ MetaWatchTransport(parent), _dev(peripheral)
+{
+ connect(_dev, SIGNAL(connected()), SLOT(handleDeviceConnected()));
+ connect(_dev, SIGNAL(disconnected()), SLOT(handleDeviceDisconnected()));
+ connect(_dev, SIGNAL(servicesDiscovered()), SLOT(handleDeviceServices()));
+ connect(_dev, SIGNAL(characteristicsDiscovered(GatoService)),
+ SLOT(handleDeviceCharacteristics(GatoService)));
+ connect(_dev, SIGNAL(valueUpdated(GatoCharacteristic,QByteArray)),
+ SLOT(handleDeviceUpdate(GatoCharacteristic,QByteArray)));
+}
+
+MetaWatchBLETransport::~MetaWatchBLETransport()
+{
+ disconnectDevice();
+}
+
+void MetaWatchBLETransport::sendMessage(quint8 type, quint8 options, const QByteArray &payload)
+{
+ QByteArray packet = encode(type, options, payload);
+ qDebug() << "Send:" << packet.toHex();
+ _dev->writeValue(_out, packet);
+}
+
+void MetaWatchBLETransport::connectDevice()
+{
+ qDebug() << "Trying to connect to" << _dev->address();
+ _dev->connectPeripheral();
+}
+
+void MetaWatchBLETransport::disconnectDevice()
+{
+ _dev->disconnectPeripheral();
+}
+
+QByteArray MetaWatchBLETransport::encode(quint8 type, quint8 options, const QByteArray &payload)
+{
+ QByteArray packet;
+
+ quint8 message_size = 6 + payload.size();
+ packet.reserve(message_size);
+
+ packet.append(0x01);
+ packet.append(message_size);
+ packet.append(type);
+ packet.append(options);
+
+ packet.append(payload);
+ packet.append("\0\0", 2); // Empty CRC is OK for BLE
+
+ return packet;
+}
+
+bool MetaWatchBLETransport::decode(const QByteArray &packet, quint8 *type, quint8 *options, QByteArray *payload)
+{
+ if (packet.size() < 6) {
+ qWarning() << "Message too short";
+ }
+
+ if (packet.at(0) != 0x1) {
+ qWarning() << "Invalid packet header";
+ return false;
+ }
+
+ quint8 message_size = packet[1];
+ if (message_size < 6 || message_size > 32 || message_size != packet.size()) {
+ qWarning() << "Invalid message size:" << message_size;
+ }
+
+ *type = packet[2];
+ *options = packet[3];
+
+ *payload = packet.mid(4, message_size - 6);
+
+ // Ignore CRC on BLE: it's set to 0 by firmware.
+
+ return true;
+}
+
+void MetaWatchBLETransport::handleDeviceConnected()
+{
+ qDebug() << "MW connected";
+ if (_dev->services().isEmpty()) {
+ qDebug() << "Trying to discover services";
+ QList<GatoUUID> interesting_services;
+ interesting_services << ServiceUuid;
+ _dev->discoverServices();
+ }
+}
+
+void MetaWatchBLETransport::handleDeviceDisconnected()
+{
+ qDebug() << "MW disconnected";
+ emit disconnected();
+}
+
+void MetaWatchBLETransport::handleDeviceServices()
+{
+ QList<GatoService> services = _dev->services();
+ qDebug() << "Got" << services.size() << "services";
+ foreach (const GatoService &s, services) {
+ if (s.uuid() == ServiceUuid) {
+ _dev->discoverCharacteristics(s);
+ }
+ }
+}
+
+void MetaWatchBLETransport::handleDeviceCharacteristics(const GatoService &service)
+{
+ qDebug() << "Got characteristic";
+ foreach (const GatoCharacteristic &c, service.characteristics()) {
+ if (c.uuid() == InputCharacteristicUuid) {
+ _in = c;
+ _dev->setNotification(_in, true);
+ } else if (c.uuid() == OutputCharacteristicUuid) {
+ _out = c;
+ }
+ if (!_in.isNull() && !_out.isNull()) {
+ emit connected();
+ }
+ }
+}
+
+void MetaWatchBLETransport::handleDeviceUpdate(const GatoCharacteristic &characteristic, const QByteArray &value)
+{
+ qDebug() << "Recv:" << value.toHex();
+ quint8 type, options;
+ QByteArray payload;
+
+ if (decode(value, &type, &options, &payload)) {
+ emit messageReceived(type, options, payload);
+ } else {
+ qWarning() << "Failed to decode message from metawatch:" << value.toHex();
+ }
+}
diff --git a/src/metawatchbletransport.h b/src/metawatchbletransport.h
new file mode 100644
index 0000000..973c4a8
--- /dev/null
+++ b/src/metawatchbletransport.h
@@ -0,0 +1,46 @@
+#ifndef METAWATCHBLETRANSPORT_H
+#define METAWATCHBLETRANSPORT_H
+
+#include <QtCore/QObject>
+
+#include <gato/gatoperipheral.h>
+#include <gato/gatouuid.h>
+#include <gato/gatoservice.h>
+#include <gato/gatocharacteristic.h>
+
+#include "metawatchtransport.h"
+
+class MetaWatchBLETransport : public MetaWatchTransport
+{
+ Q_OBJECT
+public:
+ explicit MetaWatchBLETransport(GatoPeripheral *peripheral, QObject *parent = 0);
+ ~MetaWatchBLETransport();
+
+ static const GatoUUID ServiceUuid;
+ static const GatoUUID InputCharacteristicUuid;
+ static const GatoUUID OutputCharacteristicUuid;
+
+ void sendMessage(quint8 type, quint8 options, const QByteArray &payload);
+
+public slots:
+ void connectDevice();
+ void disconnectDevice();
+
+private:
+ static QByteArray encode(quint8 type, quint8 options, const QByteArray &payload);
+ static bool decode(const QByteArray &msg, quint8 *type, quint8 *options, QByteArray *payload);
+
+private slots:
+ void handleDeviceConnected();
+ void handleDeviceDisconnected();
+ void handleDeviceServices();
+ void handleDeviceCharacteristics(const GatoService &service);
+ void handleDeviceUpdate(const GatoCharacteristic &characteristic, const QByteArray &value);
+
+private:
+ GatoPeripheral *_dev;
+ GatoCharacteristic _in, _out;
+};
+
+#endif // METAWATCHBLETRANSPORT_H
diff --git a/src/metawatchtransport.cpp b/src/metawatchtransport.cpp
new file mode 100644
index 0000000..078d390
--- /dev/null
+++ b/src/metawatchtransport.cpp
@@ -0,0 +1,6 @@
+#include "metawatchtransport.h"
+
+MetaWatchTransport::MetaWatchTransport(QObject *parent) :
+ QObject(parent)
+{
+}
diff --git a/src/metawatchtransport.h b/src/metawatchtransport.h
new file mode 100644
index 0000000..3d78ee2
--- /dev/null
+++ b/src/metawatchtransport.h
@@ -0,0 +1,25 @@
+#ifndef METAWATCHTRANSPORT_H
+#define METAWATCHTRANSPORT_H
+
+#include <QObject>
+
+class MetaWatchTransport : public QObject
+{
+ Q_OBJECT
+public:
+ explicit MetaWatchTransport(QObject *parent = 0);
+
+public:
+ virtual void sendMessage(quint8 type, quint8 options, const QByteArray &payload) = 0;
+
+public slots:
+ virtual void connectDevice() = 0;
+ virtual void disconnectDevice() = 0;
+
+signals:
+ void connected();
+ void disconnected();
+ void messageReceived(quint8 type, quint8 options, const QByteArray &payload);
+};
+
+#endif // METAWATCHTRANSPORT_H
diff --git a/src/notificationmonitor.cpp b/src/notificationmonitor.cpp
new file mode 100644
index 0000000..ca6f713
--- /dev/null
+++ b/src/notificationmonitor.cpp
@@ -0,0 +1,6 @@
+#include "notificationmonitor.h"
+
+NotificationMonitor::NotificationMonitor(QObject *parent) :
+ QObject(parent)
+{
+}
diff --git a/src/notificationmonitor.h b/src/notificationmonitor.h
new file mode 100644
index 0000000..c7b6cb2
--- /dev/null
+++ b/src/notificationmonitor.h
@@ -0,0 +1,18 @@
+#ifndef NOTIFICATIONMONITOR_H
+#define NOTIFICATIONMONITOR_H
+
+#include <QObject>
+
+class NotificationMonitor : public QObject
+{
+ Q_OBJECT
+public:
+ explicit NotificationMonitor(QObject *parent = 0);
+
+signals:
+
+public slots:
+
+};
+
+#endif // NOTIFICATIONMONITOR_H
diff --git a/src/reconnecttimer.cpp b/src/reconnecttimer.cpp
new file mode 100644
index 0000000..eaf6cbb
--- /dev/null
+++ b/src/reconnecttimer.cpp
@@ -0,0 +1,64 @@
+#include <QtCore/QDebug>
+
+extern "C" {
+ #include <iphbd/libiphb.h>
+}
+
+#include "reconnecttimer.h"
+
+static unsigned int num_wait_times = 8;
+static unsigned short wait_times[8] = {
+ 2, 5, 10, 30, 2 * 60, 5 * 60, 10 * 60, 15 * 60
+};
+
+ReconnectTimer::ReconnectTimer(QObject *parent)
+ : QObject(parent),
+ _iphb(iphb_open(0)),
+ _notifier(new QSocketNotifier(iphb_get_fd(_iphb), QSocketNotifier::Read, this)),
+ _active(false)
+{
+ connect(_notifier, &QSocketNotifier::activated, this, &ReconnectTimer::handleIphbActivity);
+}
+
+ReconnectTimer::~ReconnectTimer()
+{
+ iphb_close(_iphb);
+}
+
+void ReconnectTimer::start()
+{
+ _active = true;
+ _counter = 0;
+ setupWait();
+}
+
+void ReconnectTimer::stop()
+{
+ _active = false;
+ _counter = 0;
+ iphb_wait(_iphb, 0, 0, 0);
+}
+
+void ReconnectTimer::setupWait()
+{
+ iphb_wait(_iphb, wait_times[_counter] / 2, wait_times[_counter], 0);
+}
+
+void ReconnectTimer::handleIphbActivity()
+{
+ iphb_discard_wakeups(_iphb);
+
+ qDebug() << "iphb wakeup";
+
+ if (!_active) {
+ // False awakening
+ return;
+ }
+
+ emit tick();
+
+ if (++_counter > num_wait_times)
+ _counter = num_wait_times;
+
+ setupWait();
+}
diff --git a/src/reconnecttimer.h b/src/reconnecttimer.h
new file mode 100644
index 0000000..c8d901f
--- /dev/null
+++ b/src/reconnecttimer.h
@@ -0,0 +1,35 @@
+#ifndef RECONNECTCONTROLLER_H
+#define RECONNECTCONTROLLER_H
+
+#include <QObject>
+#include <QSocketNotifier>
+
+class ReconnectTimer : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ReconnectTimer(QObject *parent = 0);
+ ~ReconnectTimer();
+
+public slots:
+ void start();
+ void stop();
+
+signals:
+ void tick();
+
+private:
+ void setupWait();
+
+private slots:
+ void handleIphbActivity();
+
+private:
+ void *_iphb;
+ QSocketNotifier *_notifier;
+ bool _active;
+ uint _counter;
+};
+
+#endif // RECONNECTCONTROLLER_H
diff --git a/src/salmeta.cpp b/src/salmeta.cpp
new file mode 100644
index 0000000..c4bcb0c
--- /dev/null
+++ b/src/salmeta.cpp
@@ -0,0 +1,43 @@
+#include <QtCore/QDebug>
+#include <sailfishapp.h>
+
+#include "controller.h"
+
+static bool launch_daemon = false;
+static QString settings_key_prefix;
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication *app = SailfishApp::application(argc, argv);
+
+ // TODO: Rudimentary command line parser ahead. Move to QCommandLineParser when it's ready.
+ const QStringList args = app->arguments();
+ auto it = args.begin();
+ while (it != args.end()) {
+ if (*it == "--daemon") {
+ launch_daemon = true;
+ } else if (*it == "--root") {
+ ++it;
+ settings_key_prefix = *it;
+ }
+
+ ++it;
+ }
+
+ if (launch_daemon) {
+ if (settings_key_prefix.isEmpty()) {
+ settings_key_prefix = "/apps/salmeta/watch0";
+ }
+
+ qDebug() << "Starting salmeta (daemon) with settings from" << settings_key_prefix;
+
+ new Controller(settings_key_prefix, SailfishApp::createView());
+ } else {
+ QQuickView *view = SailfishApp::createView();
+ view->setSource(SailfishApp::pathTo("qml/salmeta.qml"));
+ view->show();
+ }
+
+ return app->exec();
+}
+
diff --git a/src/widgetinfo.cpp b/src/widgetinfo.cpp
new file mode 100644
index 0000000..f8cd78a
--- /dev/null
+++ b/src/widgetinfo.cpp
@@ -0,0 +1,77 @@
+#include "widgetinfo.h"
+
+WidgetInfo::WidgetInfo(QObject *parent)
+ : QObject(parent)
+{
+
+}
+
+bool WidgetInfo::valid() const
+{
+ return !_url.isEmpty();
+}
+
+QUrl WidgetInfo::url() const
+{
+ return _url;
+}
+
+void WidgetInfo::setUrl(const QUrl &url)
+{
+ if (url != _url) {
+ _url = url;
+ emit urlChanged();
+ }
+}
+
+bool WidgetInfo::invert() const
+{
+ return _invert;
+}
+
+void WidgetInfo::setInvert(bool invert)
+{
+ if (invert != _invert) {
+ _invert = invert;
+ emit invertChanged();
+ }
+}
+
+int WidgetInfo::page() const
+{
+ return _page;
+}
+
+void WidgetInfo::setPage(int page)
+{
+ if (page != _page) {
+ _page = page;
+ emit pageChanged();
+ }
+}
+
+WidgetInfo::WidgetSize WidgetInfo::size() const
+{
+ return _size;
+}
+
+void WidgetInfo::setSize(const WidgetSize &size)
+{
+ if (size != _size) {
+ _size = size;
+ emit sizeChanged();
+ }
+}
+
+WidgetInfo::WidgetPosition WidgetInfo::position() const
+{
+ return _pos;
+}
+
+void WidgetInfo::setPosition(const WidgetPosition &pos)
+{
+ if (pos != _pos) {
+ _pos = pos;
+ emit positionChanged();
+ }
+}
diff --git a/src/widgetinfo.h b/src/widgetinfo.h
new file mode 100644
index 0000000..06a6149
--- /dev/null
+++ b/src/widgetinfo.h
@@ -0,0 +1,72 @@
+#ifndef WIDGETINFO_H
+#define WIDGETINFO_H
+
+#include <QtCore/QObject>
+#include <QtCore/QUrl>
+
+class WidgetInfo : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(WidgetSize WidgetPosition)
+
+ Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
+ Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
+ Q_PROPERTY(bool invert READ invert WRITE setInvert NOTIFY invertChanged)
+ Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+ Q_PROPERTY(WidgetSize size READ size WRITE setSize NOTIFY sizeChanged)
+ Q_PROPERTY(WidgetPosition position READ position WRITE setPosition NOTIFY positionChanged)
+
+public:
+ explicit WidgetInfo(QObject *parent = 0);
+
+ enum WidgetSize
+ {
+ Size1Q = 0,
+ Size2QHorizontal = 1,
+ Size2QVertical = 2,
+ Size4Q = 3
+ };
+
+ enum WidgetPosition
+ {
+ PosNW = 0,
+ PosNE = 1,
+ PosSW = 2,
+ PosSE = 3
+ };
+
+ bool valid() const;
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ bool invert() const;
+ void setInvert(bool invert);
+
+ int page() const;
+ void setPage(int page);
+
+ WidgetSize size() const;
+ void setSize(const WidgetSize &size);
+
+ WidgetPosition position() const;
+ void setPosition(const WidgetPosition &pos);
+
+signals:
+ void validChanged();
+ void urlChanged();
+ void invertChanged();
+ void pageChanged();
+ void sizeChanged();
+ void positionChanged();
+
+private:
+ QUrl _url;
+ bool _invert;
+ short _page;
+ WidgetSize _size;
+ WidgetPosition _pos;
+
+};
+
+#endif // WIDGETINFO_H
diff --git a/src/widgetview.cpp b/src/widgetview.cpp
new file mode 100644
index 0000000..e5bf21e
--- /dev/null
+++ b/src/widgetview.cpp
@@ -0,0 +1,50 @@
+#include <QtQml/QQmlContext>
+#include <sailfishapp.h>
+
+#include "widgetview.h"
+
+#define NUM_WIDGETS 16
+
+WidgetView::WidgetView(const QString &settingsPrefix, QQuickView *view, QObject *parent)
+ : QObject(parent),
+ _settings(new MDConfGroup(this)),
+ _view(view)
+{
+ _settings->setPath(settingsPrefix);
+
+ if (!_view) {
+ _view = new QQuickView;
+ }
+
+ for (int w = 0; w < NUM_WIDGETS; w++) {
+ WidgetInfo *info = new WidgetInfo(this);
+
+ _widgets.append(info);
+ _widgetObjects.append(static_cast<QObject*>(info));
+ }
+
+ reload();
+
+ _view->setResizeMode(QQuickView::SizeViewToRootObject);
+ _view->setSource(SailfishApp::pathTo("qml/widgetview.qml"));
+ _view->rootContext()->setContextProperty("widgets", QVariant::fromValue(_widgetObjects));
+}
+
+QList<WidgetInfo*> WidgetView::widgets()
+{
+ return _widgets;
+}
+
+void WidgetView::reload()
+{
+ for (int w = 0; w < _widgets.size(); w++) {
+ WidgetInfo *widget = _widgets[w];
+ QString base = QString("widget%1/").arg(w);
+
+ widget->setInvert(_settings->value(base + "invert").toBool());
+ widget->setPage(_settings->value(base + "page").toInt());
+ widget->setSize(static_cast<WidgetInfo::WidgetSize>(_settings->value(base + "size").toInt()));
+ widget->setPosition(static_cast<WidgetInfo::WidgetPosition>(_settings->value(base + "position").toInt()));
+ widget->setUrl(_settings->value(base + "url").toUrl());
+ }
+}
diff --git a/src/widgetview.h b/src/widgetview.h
new file mode 100644
index 0000000..6ce294d
--- /dev/null
+++ b/src/widgetview.h
@@ -0,0 +1,30 @@
+#ifndef WIDGETVIEW_H
+#define WIDGETVIEW_H
+
+#include <QtCore/QObject>
+#include <QtCore/QVector>
+#include <QtQuick/QQuickView>
+
+#include <MDConfGroup>
+
+#include "widgetinfo.h"
+
+class WidgetView : public QObject
+{
+ Q_OBJECT
+public:
+ WidgetView(const QString &settingsPrefix, QQuickView *view, QObject *parent = 0);
+
+ QList<WidgetInfo*> widgets();
+
+public slots:
+ void reload();
+
+private:
+ MDConfGroup *_settings;
+ QQuickView *_view;
+ QList<WidgetInfo*> _widgets;
+ QObjectList _widgetObjects;
+};
+
+#endif // WIDGETVIEW_H