diff options
-rw-r--r-- | qml/pages/AddWidget.qml | 5 | ||||
-rw-r--r-- | qml/pages/MainPage.qml | 137 | ||||
-rw-r--r-- | qml/pages/WatchView.qml | 5 | ||||
-rw-r--r-- | qml/watch/faces/builtinface3.png | bin | 0 -> 892 bytes | |||
-rw-r--r-- | qml/watch/faces/builtinface3.qml | 11 | ||||
-rw-r--r-- | rpm/salmeta.spec | 3 | ||||
-rw-r--r-- | rpm/salmeta.yaml | 3 | ||||
-rw-r--r-- | salmeta.pro | 9 | ||||
-rw-r--r-- | src/availablewidgetsmodel.cpp | 9 | ||||
-rw-r--r-- | src/controller.cpp | 152 | ||||
-rw-r--r-- | src/controller.h | 27 | ||||
-rw-r--r-- | src/metawatch.cpp | 56 | ||||
-rw-r--r-- | src/metawatch.h | 19 | ||||
-rw-r--r-- | src/notification.cpp | 6 | ||||
-rw-r--r-- | src/notification.h | 17 | ||||
-rw-r--r-- | src/notificationadaptor.cpp | 27 | ||||
-rw-r--r-- | src/notificationadaptor.h | 65 | ||||
-rw-r--r-- | src/notificationmonitor.cpp | 55 | ||||
-rw-r--r-- | src/notificationmonitor.h | 21 | ||||
-rw-r--r-- | src/org.freedesktop.Notifications.xml | 46 | ||||
-rw-r--r-- | src/widgetinfo.cpp | 18 | ||||
-rw-r--r-- | src/widgetinfo.h | 2 | ||||
-rw-r--r-- | src/widgetinfomodel.cpp | 6 | ||||
-rw-r--r-- | src/widgetinfomodel.h | 2 | ||||
-rw-r--r-- | translations/salmeta.ts | 16 |
25 files changed, 654 insertions, 63 deletions
diff --git a/qml/pages/AddWidget.qml b/qml/pages/AddWidget.qml index 50bf63d..d79ede9 100644 --- a/qml/pages/AddWidget.qml +++ b/qml/pages/AddWidget.qml @@ -22,13 +22,14 @@ Page { contentHeight: Theme.itemSizeMedium Label { - text: url + x: Theme.paddingLarge + text: model.description truncationMode: TruncationMode.Elide color: highlighted ? Theme.highlightColor : Theme.primaryColor } onClicked: { - curWidgets.addWidget(url, addToPage, addToPos, size); + curWidgets.addWidget(model.url, addToPage, addToPos, model.size); pageStack.pop(); } } diff --git a/qml/pages/MainPage.qml b/qml/pages/MainPage.qml index 5448754..9014a48 100644 --- a/qml/pages/MainPage.qml +++ b/qml/pages/MainPage.qml @@ -12,6 +12,18 @@ Page { defaultValue: "Select" } + ConfigurationValue { + id: curPageSetting + key: curSettingsPrefix + "/cur-page" + defaultValue: 0 + + onValueChanged: { + if (watchView.curPage !== value) { + watchView.switchToPage(value); + } + } + } + SilicaFlickable { anchors.fill: parent @@ -26,7 +38,7 @@ Page { Column { id: column width: page.width - spacing: Theme.paddingLarge + spacing: Theme.paddingMedium PageHeader { title: "Salmeta" @@ -37,7 +49,6 @@ Page { } ValueButton { - x: Theme.paddingLarge label: "Device" value: deviceAddress.value @@ -52,6 +63,122 @@ Page { } } + ComboBox { + id: timeFormat + label: qsTr("Time format"); + + function load() { + if (showSecondsSetting.value) { + currentIndex = timeFormatSetting.value ? 3 : 2; + } else { + currentIndex = timeFormatSetting.value ? 1 : 0; + } + } + + ConfigurationValue { + id: timeFormatSetting + key: curSettingsPrefix + "/24h-mode" + defaultValue: 0 + } + + ConfigurationValue { + id: showSecondsSetting + key: curSettingsPrefix + "/show-seconds" + defaultValue: 0 + } + + Component.onCompleted: { + load(); + } + + menu: ContextMenu { + MenuItem { + text: Qt.formatTime(new Date(), "hh:mm ap") + } + MenuItem { + text: Qt.formatTime(new Date(), "hh:mm") + } + MenuItem { + text: Qt.formatTime(new Date(), "hh:mm:ss ap") + } + MenuItem { + text: Qt.formatTime(new Date(), "hh:mm:ss") + } + } + + onCurrentIndexChanged: { + timeFormatSetting.value = currentIndex === 1 || currentIndex === 3; + showSecondsSetting.value = currentIndex === 2 || currentIndex === 3; + } + } + + ComboBox { + id: dateFormat + label: qsTr("Date format"); + + ConfigurationValue { + id: dateFormatSetting + key: curSettingsPrefix + "/ddmm-mode" + defaultValue: 0 + } + + menu: ContextMenu { + MenuItem { + text: Qt.formatDate(new Date(), "M/d") + } + MenuItem { + text: Qt.formatDate(new Date(), "d/M") + } + } + + onCurrentIndexChanged: { + dateFormatSetting.value = currentIndex; + } + + Component.onCompleted: { + currentIndex = dateFormatSetting.value ? 1 : 0; + } + } + + TextSwitch { + id: separators + text: qsTr("Show separator lines"); + + ConfigurationValue { + id: separatorsSetting + key: curSettingsPrefix + "/show-separation-lines" + defaultValue: false + } + + onCheckedChanged: { + separatorsSetting.value = checked; + } + + Component.onCompleted: { + checked = separatorsSetting.value; + } + } + + TextSwitch { + id: autobacklight + text: qsTr("Auto-backlight"); + + ConfigurationValue { + id: autobacklightSetting + key: curSettingsPrefix + "/auto-backlight" + defaultValue: false + } + + onCheckedChanged: { + autobacklightSetting.value = checked; + } + + Component.onCompleted: { + checked = autobacklightSetting.value; + } + } + + SectionHeader { text: qsTr("Widgets"); } @@ -65,6 +192,12 @@ Page { id: watchView anchors.centerIn: parent scale: 4 + + onCurPageChanged: { + if (curPageSetting.value !== curPage) { + curPageSetting.value = curPage; + } + } } } diff --git a/qml/pages/WatchView.qml b/qml/pages/WatchView.qml index e738f4e..a5d755e 100644 --- a/qml/pages/WatchView.qml +++ b/qml/pages/WatchView.qml @@ -42,4 +42,9 @@ Flickable { curPage = Math.round(watchView.contentX / watchView.width) pivotAnim.start() } + + function switchToPage(page) { + curPage = page; + pivotAnim.start(); + } } diff --git a/qml/watch/faces/builtinface3.png b/qml/watch/faces/builtinface3.png Binary files differnew file mode 100644 index 0000000..9154c9d --- /dev/null +++ b/qml/watch/faces/builtinface3.png diff --git a/qml/watch/faces/builtinface3.qml b/qml/watch/faces/builtinface3.qml new file mode 100644 index 0000000..ca542d4 --- /dev/null +++ b/qml/watch/faces/builtinface3.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +Rectangle { + width: 96 + height: 96 + + Image { + anchors.fill: parent + source: "builtinface3.png" + } +} diff --git a/rpm/salmeta.spec b/rpm/salmeta.spec index de24543..d1f3b26 100644 --- a/rpm/salmeta.spec +++ b/rpm/salmeta.spec @@ -33,6 +33,7 @@ BuildRequires: pkgconfig(Qt5DBus) BuildRequires: pkgconfig(Qt5Bluetooth) BuildRequires: pkgconfig(mlite5) BuildRequires: pkgconfig(libiphb) +BuildRequires: libgato-devel BuildRequires: desktop-file-utils %description @@ -73,7 +74,7 @@ desktop-file-install --delete-original \ # >> post if [ "$1" -ge 1 ]; then systemctl-user daemon-reload || : -systemctl-user restart salmeta.service || : +#systemctl-user restart salmeta.service || : fi # << post diff --git a/rpm/salmeta.yaml b/rpm/salmeta.yaml index d664d45..17ec9bd 100644 --- a/rpm/salmeta.yaml +++ b/rpm/salmeta.yaml @@ -22,6 +22,9 @@ PkgConfigBR: - mlite5 - libiphb +PkgBR: + - libgato-devel + Requires: - sailfishsilica-qt5 >= 0.10.9 - sailfish-components-bluetooth-qt5 diff --git a/salmeta.pro b/salmeta.pro index acc91c4..4a75956 100644 --- a/salmeta.pro +++ b/salmeta.pro @@ -18,7 +18,9 @@ SOURCES += src/salmeta.cpp \ src/notificationmonitor.cpp \ src/widgetinfo.cpp \ src/widgetinfomodel.cpp \ - src/availablewidgetsmodel.cpp + src/availablewidgetsmodel.cpp \ + src/notificationadaptor.cpp \ + src/notification.cpp HEADERS += \ src/controller.h \ @@ -29,7 +31,9 @@ HEADERS += \ src/notificationmonitor.h \ src/widgetinfo.h \ src/widgetinfomodel.h \ - src/availablewidgetsmodel.h + src/availablewidgetsmodel.h \ + src/notificationadaptor.h \ + src/notification.h OTHER_FILES += qml/salmeta.qml \ qml/cover/CoverPage.qml \ @@ -43,6 +47,7 @@ OTHER_FILES += qml/salmeta.qml \ qml/pages/MainPage.qml \ qml/watch/WidgetView.qml \ qml/pages/WatchView.qml qml/watch/add_widget.png \ + qml/watch/faces/builtinface3.qml qml/watch/faces/builtinface3.png \ qml/watch/faces/builtinface4.qml qml/watch/faces/builtinface4.png \ qml/pages/AddWidget.qml diff --git a/src/availablewidgetsmodel.cpp b/src/availablewidgetsmodel.cpp index 02ca746..b95e79a 100644 --- a/src/availablewidgetsmodel.cpp +++ b/src/availablewidgetsmodel.cpp @@ -13,6 +13,7 @@ QHash<int, QByteArray> AvailableWidgetsModel::roleNames() const QHash<int, QByteArray> roles; roles[UrlRole] = "url"; + roles[DescriptionRole] = "description"; roles[SizeRole] = "size"; return roles; @@ -51,14 +52,16 @@ void AvailableWidgetsModel::reload() // Load builtin widgets WidgetInfo info; - info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface4.qml")); - info.setDescription("Watchface #4"); + info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface3.qml")); + info.setDescription("Builtin watchface #3"); info.setSize(WidgetInfo::Size4Q); + Q_ASSERT(info.builtinClockfaceId() == 3); // Autodetected from passed URL _widgets.append(info); info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface4.qml")); - info.setDescription("Watchface #4"); + info.setDescription("Builtin watchface #4"); info.setSize(WidgetInfo::Size4Q); + Q_ASSERT(info.builtinClockfaceId() == 4); _widgets.append(info); endResetModel(); diff --git a/src/controller.cpp b/src/controller.cpp index 9d1961c..a5ff22d 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -4,9 +4,12 @@ #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"); +static const QLatin1String setting_cur_page("cur-page"); +static const QLatin1String setting_24h_mode("24h-mode"); +static const QLatin1String setting_ddmm_mode("ddmm-mode"); +static const QLatin1String setting_seconds("show-seconds"); +static const QLatin1String setting_separation_lines("show-separation-lines"); +static const QLatin1String setting_auto_backlight("auto-backlight"); Controller::Controller(const QString &settingsPrefix, QQuickView *view, QObject *parent) : QObject(parent), @@ -14,22 +17,39 @@ Controller::Controller(const QString &settingsPrefix, QQuickView *view, QObject _metawatch(0), _reconnect(new ReconnectTimer(this)), _widgets(new WidgetInfoModel(settingsPrefix, this)), + _curMode(MetaWatch::WatchModeIdle), _curPage(0), _batteryCharge(0), _batteryCharging(false) { _settings->setPath(settingsPrefix); - _metawatch = new MetaWatch(_settings->value(setting_address).toString(), this); + _curPage = _settings->value(setting_cur_page).toInt(); 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::scheduleNextAttempt); - connect(_metawatch, &MetaWatch::batteryStatus, this, &Controller::handleMetaWatchBatteryStatus); - connect(_reconnect, &ReconnectTimer::tryReconnect, _metawatch, &MetaWatch::connectDevice); - connect(_recc) + connect(_widgets, &QAbstractItemModel::dataChanged, this, &Controller::handleWidgetChanged); - _reconnect->scheduleNextAttempt(); + connectToAddress(_settings->value(setting_address).toString()); +} + +Controller::~Controller() +{ +} + +MetaWatch::WatchMode Controller::mode() const +{ + return _curMode; +} + +int Controller::page() const +{ + return _curPage; +} - reloadPages(); +void Controller::setPage(int page) +{ + if (page != _curPage) { + _curPage = page; + _metawatch->updateLcdDisplayPage(_curPage); + emit pageChanged(); + } } int Controller::batteryCharge() const @@ -42,22 +62,79 @@ bool Controller::batteryCharging() const return _batteryCharging; } -void Controller::reloadPages() +void Controller::connectToAddress(const QString &address) { + if (_metawatch) { + if (address == _address) { + // Connecting to the same address and there's already a watch object created + return; + } + + _reconnect->stop(); + disconnect(_metawatch, 0, this, 0); + disconnect(_metawatch, 0, _reconnect, 0); + delete _metawatch; + _metawatch = 0; + } + + _address = address; + + if (_address.isEmpty()) { + qDebug() << "Empty address, doing nothing except wasting memory"; + return; + } + + _metawatch = new MetaWatch(_address, this); + connect(_metawatch, &MetaWatch::connected, _reconnect, &ReconnectTimer::stop); + connect(_metawatch, &MetaWatch::connected, this, &Controller::handleMetaWatchConnected); + connect(_metawatch, &MetaWatch::disconnected, _reconnect, &ReconnectTimer::scheduleNextAttempt); + connect(_metawatch, &MetaWatch::modeChange, this, &Controller::handleMetaWatchModeChange); + connect(_metawatch, &MetaWatch::batteryStatus, this, &Controller::handleMetaWatchBatteryStatus); + connect(_reconnect, &ReconnectTimer::tryReconnect, _metawatch, &MetaWatch::connectDevice); + _reconnect->scheduleNextAttempt(); } -void Controller::switchToPage(int page) +void Controller::updateProperties() { + MetaWatch::WatchProperties props; + + if (_settings->value(setting_24h_mode).toBool()) { + props |= MetaWatch::WatchPropertyHourFormat24h; + } + + if (_settings->value(setting_ddmm_mode).toBool()) { + props |= MetaWatch::WatchPropertyDateFormatDDMM; + } + + if (_settings->value(setting_seconds).toBool()) { + props |= MetaWatch::WatchPropertyShowSeconds; + } + + if (_settings->value(setting_separation_lines).toBool()) { + props |= MetaWatch::WatchPropertyShowSeparationLines; + } + + if (_settings->value(setting_auto_backlight).toBool()) { + props |= MetaWatch::WatchPropertyAutoBacklight; + } + _metawatch->configure(props); } void Controller::handleSettingChanged(const QString &key) { qDebug() << "Setting" << key << "changed"; - if (key == setting_num_pages) { - _settings->value(setting_num_pages, 0); - switchToPage(0); + if (key == setting_address) { + connectToAddress(_settings->value(setting_address).toString()); + } else if (key == setting_cur_page) { + int page = _settings->value(setting_cur_page).toInt(); + if (_curPage != page) { + setPage(page); + } + } else if (key == setting_24h_mode || key == setting_ddmm_mode || key == setting_seconds + || key == setting_separation_lines || key == setting_auto_backlight) { + updateProperties(); } } @@ -68,9 +145,33 @@ void Controller::handleMetaWatchConnected() _metawatch->updateDeviceType(); _metawatch->updateBatteryStatus(); _metawatch->setDateTime(QDateTime::currentDateTime()); - _metawatch->updateLcdDisplay(); + _metawatch->updateWidgetList(_widgets->toList()); + _metawatch->updateLcdDisplayPage(_curPage); + + _curMode = MetaWatch::WatchModeIdle; + + updateProperties(); +} - // TODO _metawatch->updateWidgetList(_view->widgets()); +void Controller::handleMetaWatchModeChange(MetaWatch::WatchMode mode, int page) +{ + switch (mode) { + case MetaWatch::WatchModeIdle: + if (_curMode != mode) { + _curMode = mode; + emit modeChanged(); + } + if (page != _curPage) { + _curPage = page; + _settings->setValue(setting_cur_page, QVariant::fromValue(_curPage)); + emit pageChanged(); + } + + break; + default: + qWarning() << "Unhandled metawatch mode:" << mode; + break; + } } void Controller::handleMetaWatchBatteryStatus(bool charging, int charge) @@ -84,3 +185,16 @@ void Controller::handleMetaWatchBatteryStatus(bool charging, int charge) emit batteryChargeChanged(); } } + +void Controller::handleWidgetChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) +{ + if (roles.indexOf(WidgetInfoModel::UrlRole) >= 0) { + // Can't send partial updates to watch, so entire list must be + // resubmitted everytime something important changes + _metawatch->updateWidgetList(_widgets->toList()); + + if (_widgets->data(topLeft, WidgetInfoModel::PageRole).toInt() == _curPage) { + _metawatch->updateLcdDisplayPage(_curPage); + } + } +} diff --git a/src/controller.h b/src/controller.h index 3570ff4..604da2e 100644 --- a/src/controller.h +++ b/src/controller.h @@ -1,8 +1,8 @@ #ifndef DAEMON_H #define DAEMON_H -#include <QObject> -#include <QQuickView> +#include <QtCore/QObject> +#include <QtQuick/QQuickView> #include <MDConfGroup> #include "metawatch.h" @@ -13,30 +13,39 @@ class Controller : public QObject { Q_OBJECT + Q_PROPERTY(MetaWatch::WatchMode mode READ mode NOTIFY modeChanged) + Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged) 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); + ~Controller(); + + MetaWatch::WatchMode mode() const; + + int page() const; + void setPage(int page); int batteryCharge() const; bool batteryCharging() const; signals: + void modeChanged(); + void pageChanged(); void batteryChargeChanged(); void batteryChargingChanged(); -public slots: - void reloadPages(); - void switchToPage(int page); - private: - static int parseClockUrl(const QUrl &url); + void connectToAddress(const QString &address); + void updateProperties(); private slots: void handleSettingChanged(const QString &key); void handleMetaWatchConnected(); + void handleMetaWatchModeChange(MetaWatch::WatchMode mode, int page); void handleMetaWatchBatteryStatus(bool charging, int charge); + void handleWidgetChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles); private: MDConfGroup *_settings; @@ -45,7 +54,11 @@ private: WidgetInfoModel *_widgets; + QString _address; + // Watch status + MetaWatch::WatchMode _curMode; + int _curPage; int _batteryCharge; bool _batteryCharging; }; diff --git a/src/metawatch.cpp b/src/metawatch.cpp index b1ea8af..b6042af 100644 --- a/src/metawatch.cpp +++ b/src/metawatch.cpp @@ -59,8 +59,16 @@ void MetaWatch::updateBatteryStatus() void MetaWatch::updateLcdDisplay() { - // Switches to UiGen2 mode and to page 0. - _transport->sendMessage(MessageUpdateLcdDisplay, 0x80, QByteArray()); + _transport->sendMessage(MessageUpdateLcdDisplay, + 1 << 7, + QByteArray()); +} + +void MetaWatch::updateLcdDisplayPage(int page) +{ + _transport->sendMessage(MessageUpdateLcdDisplay, + 1 << 7 | 1 << 5 | ((page << 2) & 0xC), + QByteArray()); } void MetaWatch::updateWidgetList(const QList<WidgetInfo> &widgets) @@ -69,8 +77,8 @@ void MetaWatch::updateWidgetList(const QList<WidgetInfo> &widgets) // Count valid widgets for (int w = 0; w < widgets.size(); w++) { - const WidgetInfo *info = widgets[w]; - if (info->valid()) num_widgets++; + const WidgetInfo &info = widgets[w]; + if (info.valid()) num_widgets++; } const int max_widgets_in_one_msg = 7; @@ -82,6 +90,8 @@ void MetaWatch::updateWidgetList(const QList<WidgetInfo> &widgets) msg.reserve(max_widgets_in_one_msg * 2); + qDebug() << "Sending widget list:" << num_widgets << "widgets," << num_messages << "messages"; + if (num_widgets == 0) { msg.append(static_cast<char>(0xFF)); msg.append(static_cast<char>(0x00)); @@ -92,27 +102,24 @@ void MetaWatch::updateWidgetList(const QList<WidgetInfo> &widgets) 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 + for (int i = 0; i < widgets.size(); i++) { + const WidgetInfo &info = widgets[i]; + 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); + quint8 id = i; + 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; + int clockFace = info.builtinClockfaceId(); + if (clockFace >= 0) { + id |= (clockFace << 4) & 0xF0; options |= 0x80; } - if (info->invert()) { + if (info.invert()) { options |= 0x40; } - qDebug() << QString::number(id, 16) << QString::number(options, 16); - msg.append(id); msg.append(options); @@ -166,6 +173,21 @@ void MetaWatch::handleTransportMessage(quint8 type, quint8 options, const QByteA break; case MessageModeChangeIndication: qDebug() << "Got mode change indication"; + if (payload.size() < 1) { + qWarning() << "Invalid mode change indicator size"; + } + switch (payload.at(0)) { + case 1: + emit modeChange(WatchMode(options & 0xF), (options >> 4) & 0xF); + break; + default: + qWarning() << "Unknown mode change indicator: " << payload.toHex(); + break; + } + + break; + case MessageWatchPropertyOperationResponse: + qDebug() << "Got watch property operation response"; break; case MessageReadBatteryStatusResponse: if (payload.size() < 6) { diff --git a/src/metawatch.h b/src/metawatch.h index 9068c08..6b50ead 100644 --- a/src/metawatch.h +++ b/src/metawatch.h @@ -12,7 +12,7 @@ QT_USE_NAMESPACE_BLUETOOTH class MetaWatch : public QObject { Q_OBJECT - Q_ENUMS(MessageTypes WatchProperty UiStyle) + Q_ENUMS(MessageTypes DeviceType WatchMode WatchProperty UiStyle) Q_FLAGS(WatchProperties) public: @@ -25,6 +25,7 @@ public: MessageSetRealTimeClock = 0x26, MessageWatchPropertyOperation = 0x30, + MessageWatchPropertyOperationResponse = 0x31, MessageModeChangeIndication = 0x33, @@ -48,6 +49,10 @@ public: DeviceMetaWatchDigitalGen2 = 5 }; + enum WatchMode { + WatchModeIdle = 0 + }; + enum WatchProperty { WatchPropertyHourFormat12h = 0, WatchPropertyHourFormat24h = 1, @@ -55,10 +60,10 @@ public: WatchPropertyDateFormatDDMM = 1 << 1, WatchPropertyShowSeconds = 1 << 2, WatchPropertyShowSeparationLines = 1 << 3, - WatchPropertyAutobacklight = 1 << 4, + WatchPropertyAutoBacklight = 1 << 4, - WatchPropertyOperationRead = 0 << 7, - WatchPropertyOperationWrite = 1 << 7 + WatchPropertyOperationRead = 1 << 7, + WatchPropertyOperationWrite = 0 << 7 }; Q_DECLARE_FLAGS(WatchProperties, WatchProperty) @@ -74,7 +79,10 @@ public: void updateDeviceType(); void updateBatteryStatus(); - void updateLcdDisplay(); // TODO: More overloads + /** Switches to v2 UI, goes to idle mode */ + void updateLcdDisplay(); + /** Goes to idle mode and a specific page */ + void updateLcdDisplayPage(int page); void updateWidgetList(const QList<WidgetInfo>& widgets); signals: @@ -82,6 +90,7 @@ signals: void disconnected(); void deviceType(DeviceType type); + void modeChange(WatchMode mode, int page); void batteryStatus(bool charging, int charge); public slots: diff --git a/src/notification.cpp b/src/notification.cpp new file mode 100644 index 0000000..00d66ad --- /dev/null +++ b/src/notification.cpp @@ -0,0 +1,6 @@ +#include "notification.h" + +Notification::Notification(QObject *parent) : + QObject(parent) +{ +} diff --git a/src/notification.h b/src/notification.h new file mode 100644 index 0000000..899a9aa --- /dev/null +++ b/src/notification.h @@ -0,0 +1,17 @@ +#ifndef NOTIFICATION_H +#define NOTIFICATION_H + +#include <QObject> + +class Notification : public QObject +{ +public: + explicit Notification(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // NOTIFICATION_H diff --git a/src/notificationadaptor.cpp b/src/notificationadaptor.cpp new file mode 100644 index 0000000..4279831 --- /dev/null +++ b/src/notificationadaptor.cpp @@ -0,0 +1,27 @@ +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp -c NotificationAdaptor -a notificationadaptor.cpp org.freedesktop.Notifications.xml + * + * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "notificationadaptor.h" + +/* + * Implementation of adaptor class NotificationAdaptor + */ + +NotificationAdaptor::NotificationAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ +} + +uint NotificationAdaptor::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) +{ + // handle method call org.freedesktop.Notifications.Notify + static_cast<NotificationMonitor*>(parent())->handleNotification(app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout); +} diff --git a/src/notificationadaptor.h b/src/notificationadaptor.h new file mode 100644 index 0000000..0fba973 --- /dev/null +++ b/src/notificationadaptor.h @@ -0,0 +1,65 @@ +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp -c NotificationAdaptor -a notificationadaptor.cpp org.freedesktop.Notifications.xml + * + * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#ifndef NOTIFICATIONADAPTOR +#define NOTIFICATIONADAPTOR + +#include <QtCore/QObject> +#include <QtCore/QMetaObject> +#include <QtCore/QVariant> +#include <QtDBus/QDBusAbstractAdaptor> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +#include "notificationmonitor.h" + +/* + * Adaptor class for interface org.freedesktop.Notifications + */ +class NotificationAdaptor: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Notifications") + Q_CLASSINFO("D-Bus Introspection", "" +" <interface name=\"org.freedesktop.Notifications\">\n" +" <method name=\"Notify\">\n" +" <arg direction=\"in\" type=\"s\" name=\"app_name\"/>\n" +" <arg direction=\"in\" type=\"u\" name=\"replaces_id\"/>\n" +" <arg direction=\"in\" type=\"s\" name=\"app_icon\"/>\n" +" <arg direction=\"in\" type=\"s\" name=\"summary\"/>\n" +" <arg direction=\"in\" type=\"s\" name=\"body\"/>\n" +" <arg direction=\"in\" type=\"as\" name=\"actions\"/>\n" +" <arg direction=\"in\" type=\"a{sv}\" name=\"hints\"/>\n" +" <arg direction=\"in\" type=\"i\" name=\"expire_timeout\"/>\n" +" <arg direction=\"out\" type=\"u\" name=\"id\"/>\n" +" <annotation value=\"QVariantHash\" name=\"org.qtproject.QtDBus.QtTypeName.In6\"/>\n" +" </method>\n" +" <method name=\"CloseNotification\">\n" +" <arg direction=\"in\" type=\"u\" name=\"id\"/>\n" +" </method>\n" +" </interface>\n" + <method name="CloseNotification"> + <arg name="id" type="u" direction="in"/> + </method> + "") +public: + explicit NotificationAdaptor(QObject *parent); + +public: // PROPERTIES +public Q_SLOTS: // METHODS + 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); + void NotificationClosed(uint id, uint reason); +}; + +#endif diff --git a/src/notificationmonitor.cpp b/src/notificationmonitor.cpp index ca6f713..cdf4d50 100644 --- a/src/notificationmonitor.cpp +++ b/src/notificationmonitor.cpp @@ -1,6 +1,59 @@ +#include <QtCore/QDebug> +#include <QtDBus/QDBusConnection> +#include <QtDBus/QDBusConnectionInterface> + #include "notificationmonitor.h" +#include "notificationadaptor.h" + +static NotificationMonitor *global_monitor = 0; + +namespace +{ + +void handleEavesdropCallFinished(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + QDBusError error = reply.error(); + qWarning() << "Failure to eavesdrop for notifications:" << error.message(); + } else { + qDebug() << "Now eavesdropping notifications"; + } +} + +} NotificationMonitor::NotificationMonitor(QObject *parent) : - QObject(parent) + QObject(parent) +{ +} + +NotificationMonitor::~NotificationMonitor() +{ +} + +NotificationMonitor *NotificationMonitor::instance() { + if (!global_monitor) { + QDBusConnection bus = QDBusConnection::sessionBus(); + + // First register the global monitor object that will listen for notifications... + global_monitor = new NotificationMonitor; + new NotificationAdaptor(global_monitor); + bus.registerObject("/org/freedesktop/Notifications", global_monitor); + + // Now register for eavesdropping + QDBusConnectionInterface *dbus = bus.interface(); + QDBusPendingCall call = dbus->asyncCall("AddMatch", + "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call); + connect(watcher, &QDBusPendingCallWatcher::finished, &handleEavesdropCallFinished); + } + return global_monitor; +} + +void NotificationMonitor::handleNotification(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) +{ + } diff --git a/src/notificationmonitor.h b/src/notificationmonitor.h index c7b6cb2..de9a0e7 100644 --- a/src/notificationmonitor.h +++ b/src/notificationmonitor.h @@ -1,18 +1,33 @@ #ifndef NOTIFICATIONMONITOR_H #define NOTIFICATIONMONITOR_H -#include <QObject> +#include <QtCore/QObject> +#include <QtCore/QHash> + +#include "notification.h" class NotificationMonitor : public QObject { Q_OBJECT + public: - explicit NotificationMonitor(QObject *parent = 0); + ~NotificationMonitor(); + + static NotificationMonitor *instance(); signals: + void newNotification(Notification *notification); + +private: + explicit NotificationMonitor(QObject *parent = 0); -public slots: +private slots: + void handleNotification(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); + void handleClosedNotification(uint id); + friend class NotificationAdaptor; +private: + QHash<int, }; #endif // NOTIFICATIONMONITOR_H diff --git a/src/org.freedesktop.Notifications.xml b/src/org.freedesktop.Notifications.xml new file mode 100644 index 0000000..89947c9 --- /dev/null +++ b/src/org.freedesktop.Notifications.xml @@ -0,0 +1,46 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.freedesktop.Notifications"> + <!-- Desktop Notification Specification interface --> + <method name="GetCapabilities"> + <arg name="capabilities" type="as" direction="out"/> + </method> + <method name="Notify"> + <arg name="app_name" type="s" direction="in"/> + <arg name="replaces_id" type="u" direction="in"/> + <arg name="app_icon" type="s" direction="in"/> + <arg name="summary" type="s" direction="in"/> + <arg name="body" type="s" direction="in"/> + <arg name="actions" type="as" direction="in"/> + <arg name="hints" type="a{sv}" direction="in"/> + <arg name="expire_timeout" type="i" direction="in"/> + <arg name="id" type="u" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="QVariantHash"/> + </method> + <method name="CloseNotification"> + <arg name="id" type="u" direction="in"/> + </method> + <method name="GetServerInformation"> + <arg type="s" direction="out"/> + <arg name="name" type="s" direction="out"/> + <arg name="vendor" type="s" direction="out"/> + <arg name="version" type="s" direction="out"/> + </method> + <signal name="NotificationClosed"> + <arg name="id" type="u"/> + <arg name="reason" type="u"/> + </signal> + <signal name="ActionInvoked"> + <arg name="id" type="u"/> + <arg name="action_key" type="s"/> + </signal> + + <!-- Nemo extensions --> + <method name="GetNotifications"> + <arg name="app_name" type="s" direction="in"/> + <arg name="notifications" type="a(sussasa{sv}i)" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList < Notification > "/> + </method> + </interface> +</node> + diff --git a/src/widgetinfo.cpp b/src/widgetinfo.cpp index d23da8f..f7b766c 100644 --- a/src/widgetinfo.cpp +++ b/src/widgetinfo.cpp @@ -1,3 +1,5 @@ +#include <QtCore/QRegularExpression> + #include "widgetinfo.h" struct WidgetInfoData : public QSharedData @@ -48,6 +50,22 @@ void WidgetInfo::setUrl(const QUrl &url) d->url = url; } +int WidgetInfo::builtinClockfaceId() const +{ + QString file = d->url.toLocalFile(); + if (file.isEmpty()) return -1; + + + // I'm definitely evil: + static const QRegularExpression re("qml/watch/faces/builtinface([0-9]+).qml$"); + QRegularExpressionMatch match = re.match(file); + if (match.hasMatch()) { + return match.captured(1).toInt(); + } else { + return -1; + } +} + QString WidgetInfo::description() const { return d->desc; diff --git a/src/widgetinfo.h b/src/widgetinfo.h index 51d617b..b5f46fe 100644 --- a/src/widgetinfo.h +++ b/src/widgetinfo.h @@ -37,6 +37,8 @@ public: bool valid() const; + int builtinClockfaceId() const; + QUrl url() const; void setUrl(const QUrl &url); diff --git a/src/widgetinfomodel.cpp b/src/widgetinfomodel.cpp index 6c4d5d2..e2f145f 100644 --- a/src/widgetinfomodel.cpp +++ b/src/widgetinfomodel.cpp @@ -64,6 +64,11 @@ QVariant WidgetInfoModel::data(const QModelIndex &index, int role) const } } +QList<WidgetInfo> WidgetInfoModel::toList() const +{ + return _widgets.toList(); +} + void WidgetInfoModel::reload() { beginResetModel(); @@ -165,7 +170,6 @@ void WidgetInfoModel::handleSettingChanged(const QString &key) } const QModelIndex index = createIndex(slot, 0); - qDebug() << "Data changed" << slot << roles.at(0); emit dataChanged(index, index, roles); } else { qWarning() << "Unknown widget key changed:" << key; diff --git a/src/widgetinfomodel.h b/src/widgetinfomodel.h index 6b30a91..4b2a700 100644 --- a/src/widgetinfomodel.h +++ b/src/widgetinfomodel.h @@ -25,6 +25,8 @@ public: int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; + QList<WidgetInfo> toList() const; + signals: public slots: diff --git a/translations/salmeta.ts b/translations/salmeta.ts index 79cf7af..786af13 100644 --- a/translations/salmeta.ts +++ b/translations/salmeta.ts @@ -19,5 +19,21 @@ <source>Notifications</source> <translation type="unfinished"></translation> </message> + <message> + <source>Time format</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Date format</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Show separator lines</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Auto-backlight</source> + <translation type="unfinished"></translation> + </message> </context> </TS> |