From 1adf7f1bcde493ccaacedb0d9778911ad69ff335 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 4 Sep 2014 01:55:14 +0200 Subject: Initial import --- qml/cover/CoverPage.qml | 54 +++++++++++++ qml/pages/FirstPage.qml | 73 +++++++++++++++++ qml/pages/SecondPage.qml | 62 ++++++++++++++ qml/salmeta.qml | 41 ++++++++++ qml/watch/main.qml | 8 ++ qml/widgetview.qml | 15 ++++ rpm/salmeta.changes.in | 15 ++++ rpm/salmeta.spec | 94 ++++++++++++++++++++++ rpm/salmeta.yaml | 35 ++++++++ salmeta.desktop | 7 ++ salmeta.pro | 52 ++++++++++++ salmeta.service | 11 +++ src/controller.cpp | 84 +++++++++++++++++++ src/controller.h | 53 ++++++++++++ src/metawatch.cpp | 183 ++++++++++++++++++++++++++++++++++++++++++ src/metawatch.h | 101 +++++++++++++++++++++++ src/metawatchbletransport.cpp | 141 ++++++++++++++++++++++++++++++++ src/metawatchbletransport.h | 46 +++++++++++ src/metawatchtransport.cpp | 6 ++ src/metawatchtransport.h | 25 ++++++ src/notificationmonitor.cpp | 6 ++ src/notificationmonitor.h | 18 +++++ src/reconnecttimer.cpp | 64 +++++++++++++++ src/reconnecttimer.h | 35 ++++++++ src/salmeta.cpp | 43 ++++++++++ src/widgetinfo.cpp | 77 ++++++++++++++++++ src/widgetinfo.h | 72 +++++++++++++++++ src/widgetview.cpp | 50 ++++++++++++ src/widgetview.h | 30 +++++++ translations/salmeta.ts | 37 +++++++++ 30 files changed, 1538 insertions(+) create mode 100644 qml/cover/CoverPage.qml create mode 100644 qml/pages/FirstPage.qml create mode 100644 qml/pages/SecondPage.qml create mode 100644 qml/salmeta.qml create mode 100644 qml/watch/main.qml create mode 100644 qml/widgetview.qml create mode 100644 rpm/salmeta.changes.in create mode 100644 rpm/salmeta.spec create mode 100644 rpm/salmeta.yaml create mode 100644 salmeta.desktop create mode 100644 salmeta.pro create mode 100644 salmeta.service create mode 100644 src/controller.cpp create mode 100644 src/controller.h create mode 100644 src/metawatch.cpp create mode 100644 src/metawatch.h create mode 100644 src/metawatchbletransport.cpp create mode 100644 src/metawatchbletransport.h create mode 100644 src/metawatchtransport.cpp create mode 100644 src/metawatchtransport.h create mode 100644 src/notificationmonitor.cpp create mode 100644 src/notificationmonitor.h create mode 100644 src/reconnecttimer.cpp create mode 100644 src/reconnecttimer.h create mode 100644 src/salmeta.cpp create mode 100644 src/widgetinfo.cpp create mode 100644 src/widgetinfo.h create mode 100644 src/widgetview.cpp create mode 100644 src/widgetview.h create mode 100644 translations/salmeta.ts diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml new file mode 100644 index 0000000..786b78f --- /dev/null +++ b/qml/cover/CoverPage.qml @@ -0,0 +1,54 @@ +/* + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Jolla Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +CoverBackground { + Label { + id: label + anchors.centerIn: parent + text: qsTr("My Cover") + } + + CoverActionList { + id: coverAction + + CoverAction { + iconSource: "image://theme/icon-cover-next" + } + + CoverAction { + iconSource: "image://theme/icon-cover-pause" + } + } +} + + diff --git a/qml/pages/FirstPage.qml b/qml/pages/FirstPage.qml new file mode 100644 index 0000000..14d7410 --- /dev/null +++ b/qml/pages/FirstPage.qml @@ -0,0 +1,73 @@ +/* + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Jolla Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + + +Page { + id: page + + // To enable PullDownMenu, place our content in a SilicaFlickable + SilicaFlickable { + anchors.fill: parent + + // PullDownMenu and PushUpMenu must be declared in SilicaFlickable, SilicaListView or SilicaGridView + PullDownMenu { + MenuItem { + text: qsTr("Show Page 2") + onClicked: pageStack.push(Qt.resolvedUrl("SecondPage.qml")) + } + } + + // Tell SilicaFlickable the height of its content. + contentHeight: column.height + + // Place our content in a Column. The PageHeader is always placed at the top + // of the page, followed by our content. + Column { + id: column + + width: page.width + spacing: Theme.paddingLarge + PageHeader { + title: qsTr("UI Template") + } + Label { + x: Theme.paddingLarge + text: qsTr("Hello Sailors") + color: Theme.secondaryHighlightColor + font.pixelSize: Theme.fontSizeExtraLarge + } + } + } +} + + diff --git a/qml/pages/SecondPage.qml b/qml/pages/SecondPage.qml new file mode 100644 index 0000000..4f37362 --- /dev/null +++ b/qml/pages/SecondPage.qml @@ -0,0 +1,62 @@ +/* + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Jolla Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + + +Page { + id: page + SilicaListView { + id: listView + model: 20 + anchors.fill: parent + header: PageHeader { + title: qsTr("Nested Page") + } + delegate: BackgroundItem { + id: delegate + + Label { + x: Theme.paddingLarge + text: qsTr("Item") + " " + index + anchors.verticalCenter: parent.verticalCenter + color: delegate.highlighted ? Theme.highlightColor : Theme.primaryColor + } + onClicked: console.log("Clicked " + index) + } + VerticalScrollDecorator {} + } +} + + + + + diff --git a/qml/salmeta.qml b/qml/salmeta.qml new file mode 100644 index 0000000..bb031a4 --- /dev/null +++ b/qml/salmeta.qml @@ -0,0 +1,41 @@ +/* + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Jolla Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import "pages" + +ApplicationWindow +{ + initialPage: Component { FirstPage { } } + cover: Qt.resolvedUrl("cover/CoverPage.qml") +} + + diff --git a/qml/watch/main.qml b/qml/watch/main.qml new file mode 100644 index 0000000..200a8b0 --- /dev/null +++ b/qml/watch/main.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Rectangle { + width: 96 + height: 96 + + color: "white" +} diff --git a/qml/widgetview.qml b/qml/widgetview.qml new file mode 100644 index 0000000..077431d --- /dev/null +++ b/qml/widgetview.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 + +Rectangle { + width: 96*4 + height: 96 + + color: Qt.rgba(0, 0, 0, 1); + + Repeater { + model: 16 + Loader { + + } + } +} diff --git a/rpm/salmeta.changes.in b/rpm/salmeta.changes.in new file mode 100644 index 0000000..9661218 --- /dev/null +++ b/rpm/salmeta.changes.in @@ -0,0 +1,15 @@ +# Rename this file as salmeta.changes to include changelog +# entries in your RPM file. +# +# Add new changelog entries following the format below. +# Add newest entries to the top of the list. +# Separate entries from eachother with a blank line. + +# * date Author's Name version-release +# - Summary of changes + +* Sun Apr 13 2014 Jack Tar 0.0.1-1 +- Scrubbed the deck +- Hoisted the sails + + diff --git a/rpm/salmeta.spec b/rpm/salmeta.spec new file mode 100644 index 0000000..dfafb60 --- /dev/null +++ b/rpm/salmeta.spec @@ -0,0 +1,94 @@ +# +# Do NOT Edit the Auto-generated Part! +# Generated by: spectacle version 0.27 +# + +Name: salmeta + +# >> macros +# << macros + +%{!?qtc_qmake:%define qtc_qmake %qmake} +%{!?qtc_qmake5:%define qtc_qmake5 %qmake5} +%{!?qtc_make:%define qtc_make make} +%{?qtc_builddir:%define _builddir %qtc_builddir} +Summary: Salmeta +Version: 0.1 +Release: 1 +Group: Communications/Bluetooth +License: GPLv3 +URL: http://javispedro.com +Source0: %{name}-%{version}.tar.bz2 +Source100: salmeta.yaml +Requires: sailfishsilica-qt5 >= 0.10.9 +Requires: systemd +Requires: systemd-user-session-targets +BuildRequires: pkgconfig(sailfishapp) >= 1.0.2 +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(Qt5Qml) +BuildRequires: pkgconfig(Qt5Quick) +BuildRequires: pkgconfig(Qt5DBus) +BuildRequires: pkgconfig(Qt5Bluetooth) +BuildRequires: pkgconfig(mlite5) +BuildRequires: pkgconfig(libiphb) +BuildRequires: desktop-file-utils + +%description +Salmeta is a Metawatch manager program for Sailfish devices. + + +%prep +%setup -q -n %{name}-%{version} + +# >> setup +# << setup + +%build +# >> build pre +# << build pre + +%qtc_qmake5 + +%qtc_make %{?_smp_mflags} + +# >> build post +# << build post + +%install +rm -rf %{buildroot} +# >> install pre +# << install pre +%qmake5_install + +# >> install post +# << install post + +desktop-file-install --delete-original \ + --dir %{buildroot}%{_datadir}/applications \ + %{buildroot}%{_datadir}/applications/*.desktop + +%post +# >> post +if [ "$1" -ge 1 ]; then +systemctl-user daemon-reload || : +systemctl-user restart salmeta.service || : +fi +# << post + +%postun +# >> postun +if [ "$1" -eq 0 ]; then +systemctl-user stop salmeta.service || : +systemctl-user daemon-reload || : +fi +# << postun + +%files +%defattr(-,root,root,-) +%{_bindir} +%{_datadir}/%{name} +%{_datadir}/applications/%{name}.desktop +%{_datadir}/icons/hicolor/86x86/apps/%{name}.png +%{_libdir}/systemd/user/salmeta.service +# >> files +# << files diff --git a/rpm/salmeta.yaml b/rpm/salmeta.yaml new file mode 100644 index 0000000..14e443f --- /dev/null +++ b/rpm/salmeta.yaml @@ -0,0 +1,35 @@ +Name: salmeta +Summary: Salmeta +Version: 0.1 +Release: 1 +Group: Communications/Bluetooth +URL: http://javispedro.com +License: GPLv3 +Sources: +- '%{name}-%{version}.tar.bz2' +Description: | + Salmeta is a Metawatch manager program for Sailfish devices. +Configure: none +Builder: qtc5 + +PkgConfigBR: + - sailfishapp >= 1.0.2 + - Qt5Core + - Qt5Qml + - Qt5Quick + - Qt5DBus + - Qt5Bluetooth + - mlite5 + - libiphb + +Requires: + - sailfishsilica-qt5 >= 0.10.9 + - systemd + - systemd-user-session-targets + +Files: + - '%{_bindir}' + - '%{_datadir}/%{name}' + - '%{_datadir}/applications/%{name}.desktop' + - '%{_datadir}/icons/hicolor/86x86/apps/%{name}.png' + - '%{_libdir}/systemd/user/salmeta.service' diff --git a/salmeta.desktop b/salmeta.desktop new file mode 100644 index 0000000..7d08500 --- /dev/null +++ b/salmeta.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +X-Nemo-Application-Type=silica-qt5 +Icon=salmeta +Exec=salmeta +Name=Salmeta + diff --git a/salmeta.pro b/salmeta.pro new file mode 100644 index 0000000..7db3699 --- /dev/null +++ b/salmeta.pro @@ -0,0 +1,52 @@ +TARGET = salmeta + +QT += qml quick dbus bluetooth + +CONFIG += link_pkgconfig sailfishapp c++11 +PKGCONFIG += mlite5 libiphb +LIBS += -lgato + +# Remove this if QtCreator can find headers on your machine +CONFIG(debug, debug|release) : INCLUDEPATH += /usr/include/mlite5 + +SOURCES += src/salmeta.cpp \ + src/controller.cpp \ + src/metawatchtransport.cpp \ + src/metawatch.cpp \ + src/metawatchbletransport.cpp \ + src/reconnecttimer.cpp \ + src/notificationmonitor.cpp \ + src/widgetview.cpp \ + src/widgetinfo.cpp + +HEADERS += \ + src/controller.h \ + src/metawatchtransport.h \ + src/metawatch.h \ + src/metawatchbletransport.h \ + src/reconnecttimer.h \ + src/notificationmonitor.h \ + src/widgetview.h \ + src/widgetinfo.h + +OTHER_FILES += qml/salmeta.qml \ + qml/cover/CoverPage.qml \ + qml/pages/FirstPage.qml \ + qml/pages/SecondPage.qml \ + rpm/salmeta.changes.in \ + rpm/salmeta.spec \ + rpm/salmeta.yaml \ + translations/*.ts \ + salmeta.desktop \ + qml/watch/main.qml \ + qml/widgetview.qml \ + salmeta.service + +# to disable building translations every time, comment out the +# following CONFIG line +CONFIG += sailfishapp_i18n +#TRANSLATIONS += translations/salmeta-de.ts + +unit.path = /usr/lib/systemd/user/ +unit.files = salmeta.service +INSTALLS += unit diff --git a/salmeta.service b/salmeta.service new file mode 100644 index 0000000..70cca61 --- /dev/null +++ b/salmeta.service @@ -0,0 +1,11 @@ +[Unit] +Description=Salmeta MetaWatch manager daemon +Requires=dbus.socket bluetooth.target +After=lipstick.service dbus.socket bluetooth.target + +[Service] +ExecStart=/usr/bin/salmeta --daemon +Restart=always + +[Install] +WantedBy=user-session.target 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 +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 MetaWatch::availableClocks() +{ + QList 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 &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(0xFF)); + msg.append(static_cast(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(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 +#include + +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 availableClocks(); + + void setDateTime(const QDateTime &dt); + void configure(WatchProperties props); + + void updateDeviceType(); + void updateBatteryStatus(); + void updateLcdDisplay(); // TODO: More overloads + void updateWidgetList(const QList& 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 interesting_services; + interesting_services << ServiceUuid; + _dev->discoverServices(); + } +} + +void MetaWatchBLETransport::handleDeviceDisconnected() +{ + qDebug() << "MW disconnected"; + emit disconnected(); +} + +void MetaWatchBLETransport::handleDeviceServices() +{ + QList 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 + +#include +#include +#include +#include + +#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 + +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 + +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 + +extern "C" { + #include +} + +#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 +#include + +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 +#include + +#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 +#include + +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 +#include + +#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(info)); + } + + reload(); + + _view->setResizeMode(QQuickView::SizeViewToRootObject); + _view->setSource(SailfishApp::pathTo("qml/widgetview.qml")); + _view->rootContext()->setContextProperty("widgets", QVariant::fromValue(_widgetObjects)); +} + +QList 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(_settings->value(base + "size").toInt())); + widget->setPosition(static_cast(_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 +#include +#include + +#include + +#include "widgetinfo.h" + +class WidgetView : public QObject +{ + Q_OBJECT +public: + WidgetView(const QString &settingsPrefix, QQuickView *view, QObject *parent = 0); + + QList widgets(); + +public slots: + void reload(); + +private: + MDConfGroup *_settings; + QQuickView *_view; + QList _widgets; + QObjectList _widgetObjects; +}; + +#endif // WIDGETVIEW_H diff --git a/translations/salmeta.ts b/translations/salmeta.ts new file mode 100644 index 0000000..0374c0e --- /dev/null +++ b/translations/salmeta.ts @@ -0,0 +1,37 @@ + + + + + CoverPage + + My Cover + + + + + FirstPage + + Show Page 2 + + + + UI Template + + + + Hello Sailors + + + + + SecondPage + + Nested Page + + + + Item + + + + -- cgit v1.2.3