From 49f1261bf9d635d5e3d881e87a93ed4e76abfe90 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 30 Nov 2014 17:27:08 +0100 Subject: allow receiving responses to commands in watchconnector * the skeleton is in place for watchconnector to allow query->response messages. I've used call/cc style because it is impossible to make QBluetoothSocket synchronous (waitForReadyRead() is a no-op) * remove watchcommands, instead create musicmanager to listen for the music endpoint. The other (simpler) endpoints are now listened in watchconnector itself. hangupAll() slot is moved to voicecallmanager. * instead of emitting signals for each received message, listeners can now register for receiving messages targeted towards a given endpoint * when reading from bluetoothsocket, properly handle short reads * remove useless 'watch' namespace * create appmanager, which mantains a database of installed apps (installed on the phone, that is; watch installed apps will come later) * all the *Managers are now instantiated by the main Manager itself * introduce Unpacker helper class for decoding watch messages * implement getAppbankStatus and getAppbankUuids messages and response parsers * remove file logging for now (20MB is bad for eMMC!) * use dbus object path /org/pebbled instead of / --- app/pebbledinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp index 05ca614..c6f5674 100644 --- a/app/pebbledinterface.cpp +++ b/app/pebbledinterface.cpp @@ -2,7 +2,7 @@ QString PebbledInterface::PEBBLED_SYSTEMD_UNIT("pebbled.service"); QString PebbledInterface::PEBBLED_DBUS_SERVICE("org.pebbled"); -QString PebbledInterface::PEBBLED_DBUS_PATH("/"); +QString PebbledInterface::PEBBLED_DBUS_PATH("/org/pebbled"); QString PebbledInterface::PEBBLED_DBUS_IFACE("org.pebbled"); #define PebbledDbusInterface QDBusInterface(PEBBLED_DBUS_SERVICE, PEBBLED_DBUS_PATH, PEBBLED_DBUS_IFACE) -- cgit v1.2.3 From 81f91639969de0f3852a3fe73db13b4cb0ecf3b4 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 1 Dec 2014 04:13:06 +0100 Subject: hackily implement openURL by signalling the URLs via D-Bus to the settings app, which pops a webview --- app/app.pro | 3 ++- app/pebbledinterface.cpp | 16 ++++++++++++++++ app/pebbledinterface.h | 4 ++++ app/qml/pages/WebViewPage.qml | 29 +++++++++++++++++++++++++++++ app/qml/pebble.qml | 5 +++++ daemon/dbusadaptor.cpp | 5 +++++ daemon/dbusadaptor.h | 8 ++++++++ daemon/jskitmanager.cpp | 10 ++++++++++ daemon/jskitmanager.h | 2 ++ daemon/jskitobjects.cpp | 10 ++++++++-- daemon/jskitobjects.h | 12 ++++++------ daemon/manager.cpp | 6 ++++++ daemon/manager.h | 3 ++- 13 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 app/qml/pages/WebViewPage.qml (limited to 'app') diff --git a/app/app.pro b/app/app.pro index cb4c33b..e08fa87 100644 --- a/app/app.pro +++ b/app/app.pro @@ -22,4 +22,5 @@ OTHER_FILES += \ qml/pebble.qml \ qml/images/* \ pebble.desktop \ - pebble.png + pebble.png \ + qml/pages/WebViewPage.qml diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp index c6f5674..8759863 100644 --- a/app/pebbledinterface.cpp +++ b/app/pebbledinterface.cpp @@ -19,6 +19,10 @@ PebbledInterface::PebbledInterface(QObject *parent) : PEBBLED_DBUS_SERVICE, PEBBLED_DBUS_PATH, PEBBLED_DBUS_IFACE, "pebbleChanged", this, SLOT(onPebbleChanged())); + QDBusConnection::sessionBus().connect( + PEBBLED_DBUS_SERVICE, PEBBLED_DBUS_PATH, PEBBLED_DBUS_IFACE, + "openUrl", this, SIGNAL(openUrl(QString))); + // simulate connected change on active changed // as the daemon might not had a chance to send 'connectedChanged' // when going down @@ -163,3 +167,15 @@ void PebbledInterface::reconnect() qDebug() << __FUNCTION__; PebbledDbusInterface.call("reconnect"); } + +void PebbledInterface::test() +{ + qDebug() << __FUNCTION__; + PebbledDbusInterface.call("test"); +} + +void PebbledInterface::webviewClosed(const QString &result) +{ + qDebug() << __FUNCTION__; + PebbledDbusInterface.call("webviewClosed", QVariant::fromValue(result)); +} diff --git a/app/pebbledinterface.h b/app/pebbledinterface.h index 0a6f15d..7d00110 100644 --- a/app/pebbledinterface.h +++ b/app/pebbledinterface.h @@ -45,6 +45,8 @@ signals: void nameChanged(); void addressChanged(); + void openUrl(const QString &url); + public slots: void setEnabled(bool); void setActive(bool); @@ -52,6 +54,8 @@ public slots: void time(); void disconnect(); void reconnect(); + void test(); + void webviewClosed(const QString &result); private slots: void getUnitProperties(); diff --git a/app/qml/pages/WebViewPage.qml b/app/qml/pages/WebViewPage.qml new file mode 100644 index 0000000..2c6fcf0 --- /dev/null +++ b/app/qml/pages/WebViewPage.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 +import QtQml 2.1 +import QtWebKit 3.0 +import Sailfish.Silica 1.0 + +Page { + id: webviewPage + + property alias url: webview.url + + SilicaWebView { + id: webview + anchors.fill: parent + + onNavigationRequested: { + console.log("navigation requested to " + request.url); + var url = request.url.toString() + if (/^pebblejs:\/\/close/.exec(url)) { + var data = decodeURI(url.substring(17)); + console.log("match with pebble close regexp. data: " + data); + pebbled.webviewClosed(data); + pageStack.pop(); + request.action = WebView.IgnoreRequest; + } else { + request.action = WebView.AcceptRequest; + } + } + } +} diff --git a/app/qml/pebble.qml b/app/qml/pebble.qml index da3bfb5..2ff0839 100644 --- a/app/qml/pebble.qml +++ b/app/qml/pebble.qml @@ -41,5 +41,10 @@ ApplicationWindow PebbledInterface { id: pebbled + + onOpenUrl: { + console.log("got open url: " + url); + pageStack.push(Qt.resolvedUrl("pages/WebViewPage.qml"), {url: url}); + } } } diff --git a/daemon/dbusadaptor.cpp b/daemon/dbusadaptor.cpp index 7bbf623..25e2508 100644 --- a/daemon/dbusadaptor.cpp +++ b/daemon/dbusadaptor.cpp @@ -85,3 +85,8 @@ void PebbledAdaptor::test() { QMetaObject::invokeMethod(parent(), "test"); } + +void PebbledAdaptor::webviewClosed(const QString &result) +{ + QMetaObject::invokeMethod(parent(), "webviewClosed", Q_ARG(QString, result)); +} diff --git a/daemon/dbusadaptor.h b/daemon/dbusadaptor.h index 54a0963..f347f92 100644 --- a/daemon/dbusadaptor.h +++ b/daemon/dbusadaptor.h @@ -47,6 +47,12 @@ class PebbledAdaptor: public QDBusAbstractAdaptor " \n" " \n" " \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" " \n" "") public: @@ -72,9 +78,11 @@ public Q_SLOTS: // METHODS void time(); void reconnect(); void test(); + void webviewClosed(const QString &result); Q_SIGNALS: // SIGNALS void connectedChanged(); void pebbleChanged(); + void openUrl(const QString &s); }; #endif diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp index 6023a9a..cfd860e 100644 --- a/daemon/jskitmanager.cpp +++ b/daemon/jskitmanager.cpp @@ -31,6 +31,16 @@ void JSKitManager::showConfiguration() } } +void JSKitManager::handleWebviewClosed(const QString &result) +{ + if (_engine) { + QJSValue eventObj = _engine->newObject(); + eventObj.setProperty("response", _engine->toScriptValue(result)); + + _jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj})); + } +} + void JSKitManager::handleAppStarted(const QUuid &uuid) { AppInfo info = _apps->info(uuid); diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h index 73d7853..fd6040d 100644 --- a/daemon/jskitmanager.h +++ b/daemon/jskitmanager.h @@ -22,9 +22,11 @@ public: signals: void appNotification(const QUuid &uuid, const QString &title, const QString &body); + void appOpenUrl(const QString &url); public slots: void showConfiguration(); + void handleWebviewClosed(const QString &result); private slots: void handleAppStarted(const QUuid &uuid); diff --git a/daemon/jskitobjects.cpp b/daemon/jskitobjects.cpp index f745233..9e4cfde 100644 --- a/daemon/jskitobjects.cpp +++ b/daemon/jskitobjects.cpp @@ -54,12 +54,15 @@ void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QSt emit _mgr->appNotification(_appInfo.uuid(), title, body); } -void JSKitPebble::openUrl(const QUrl &url) +void JSKitPebble::openURL(const QUrl &url) { logger()->debug() << "opening url" << url.toString(); + emit _mgr->appOpenUrl(url.toString()); +#if 0 /* Until we figure out how to do this. Maybe signal the daemon? */ if (!QDesktopServices::openUrl(url)) { logger()->warn() << "Failed to open URL:" << url; } +#endif } QJSValue JSKitPebble::createXMLHttpRequest() @@ -253,7 +256,10 @@ void JSKitXMLHttpRequest::handleReplyFinished() if (_onload.isCallable()) { logger()->debug() << "going to call onload handler:" << _onload.toString(); - _onload.callWithInstance(_mgr->engine()->toScriptValue(this)); + QJSValue result = _onload.callWithInstance(_mgr->engine()->newQObject(this)); + if (result.isError()) { + logger()->warn() << "JS error on onload handler:" << result.toString(); + } } else { logger()->debug() << "No onload set"; } diff --git a/daemon/jskitobjects.h b/daemon/jskitobjects.h index 1962e01..4f000f1 100644 --- a/daemon/jskitobjects.h +++ b/daemon/jskitobjects.h @@ -21,7 +21,7 @@ public: Q_INVOKABLE void showSimpleNotificationOnPebble(const QString &title, const QString &body); - Q_INVOKABLE void openUrl(const QUrl &url); + Q_INVOKABLE void openURL(const QUrl &url); Q_INVOKABLE QJSValue createXMLHttpRequest(); @@ -88,11 +88,11 @@ public: ~JSKitXMLHttpRequest(); enum ReadyStates { - UNSENT, - OPENED, - HEADERS_RECEIVED, - LOADING, - DONE + UNSENT = 0, + OPENED = 1, + HEADERS_RECEIVED = 2, + LOADING = 3, + DONE = 4 }; Q_INVOKABLE void open(const QString &method, const QString &url, bool async); diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 9db4c70..e732d38 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -61,6 +61,7 @@ Manager::Manager(Settings *settings, QObject *parent) : session.registerService("org.pebbled"); connect(dbus, SIGNAL(pebbleChanged()), adaptor, SIGNAL(pebbleChanged())); connect(watch, SIGNAL(connectedChanged()), adaptor, SIGNAL(connectedChanged())); + connect(js, SIGNAL(appOpenUrl(QString)), adaptor, SIGNAL(openUrl(QString))); QString currentProfile = getCurrentProfile(); defaultProfile = currentProfile.isEmpty() ? "ambience" : currentProfile; @@ -397,3 +398,8 @@ void Manager::test() js->showConfiguration(); } + +void Manager::onWebviewClosed(const QString &result) +{ + js->handleWebviewClosed(result); +} diff --git a/daemon/manager.h b/daemon/manager.h index f1dd53e..b0e15fb 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -84,6 +84,7 @@ public slots: private slots: void test(); + void onWebviewClosed(const QString &result); void onSettingChanged(const QString &key); void onSettingsChanged(); void onPebbleChanged(); @@ -123,7 +124,7 @@ public slots: void disconnect() { static_cast(parent())->watch->disconnect(); } void reconnect() { static_cast(parent())->watch->reconnect(); } void test() { static_cast(parent())->test(); } - + void webviewClosed(const QString &result) { static_cast(parent())->onWebviewClosed(result); } }; #endif // MANAGER_H -- cgit v1.2.3 From c35a3a9bea759cadf1e975a2a62e50789cad096c Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 2 Dec 2014 23:33:19 +0100 Subject: define new D-Bus interface, use qmake to generate adaptors/interfaces also slightly clean up the way d-bus is handled both in daemon and UI --- app/app.pro | 2 + app/pebbledinterface.cpp | 100 +++++++++++++++++++---------------------------- app/pebbledinterface.h | 45 +++++++-------------- daemon/daemon.pro | 5 +-- daemon/dbusadaptor.cpp | 92 ------------------------------------------- daemon/dbusadaptor.h | 88 ----------------------------------------- daemon/dbusconnector.h | 4 +- daemon/manager.cpp | 32 ++++++++------- daemon/manager.h | 91 ++++++++++++++++++++++++++++++------------ daemon/org.pebbled.xml | 20 ---------- org.pebbled.Watch.xml | 46 ++++++++++++++++++++++ 11 files changed, 191 insertions(+), 334 deletions(-) delete mode 100644 daemon/dbusadaptor.cpp delete mode 100644 daemon/dbusadaptor.h delete mode 100644 daemon/org.pebbled.xml create mode 100644 org.pebbled.Watch.xml (limited to 'app') diff --git a/app/app.pro b/app/app.pro index e08fa87..e0ff449 100644 --- a/app/app.pro +++ b/app/app.pro @@ -14,6 +14,8 @@ SOURCES += \ HEADERS += \ pebbledinterface.h +DBUS_INTERFACES += ../org.pebbled.Watch.xml + OTHER_FILES += \ qml/cover/CoverPage.qml \ qml/pages/ManagerPage.qml \ diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp index 8759863..fb937f2 100644 --- a/app/pebbledinterface.cpp +++ b/app/pebbledinterface.cpp @@ -1,37 +1,33 @@ #include "pebbledinterface.h" +#include "watch_interface.h" -QString PebbledInterface::PEBBLED_SYSTEMD_UNIT("pebbled.service"); -QString PebbledInterface::PEBBLED_DBUS_SERVICE("org.pebbled"); -QString PebbledInterface::PEBBLED_DBUS_PATH("/org/pebbled"); -QString PebbledInterface::PEBBLED_DBUS_IFACE("org.pebbled"); - -#define PebbledDbusInterface QDBusInterface(PEBBLED_DBUS_SERVICE, PEBBLED_DBUS_PATH, PEBBLED_DBUS_IFACE) - +static const QString PEBBLED_SYSTEMD_UNIT("pebbled.service"); +static const QString PEBBLED_DBUS_SERVICE("org.pebbled"); +static const QString PEBBLED_DBUS_PATH("/org/pebbled/watch"); +static const QString PEBBLED_DBUS_IFACE("org.pebbled.Watch"); PebbledInterface::PebbledInterface(QObject *parent) : - QObject(parent), systemd(0) -{ - QDBusConnection::sessionBus().connect( - PEBBLED_DBUS_SERVICE, PEBBLED_DBUS_PATH, PEBBLED_DBUS_IFACE, - "connectedChanged", this, SIGNAL(connectedChanged())); - - QDBusConnection::sessionBus().connect( - PEBBLED_DBUS_SERVICE, PEBBLED_DBUS_PATH, PEBBLED_DBUS_IFACE, - "pebbleChanged", this, SLOT(onPebbleChanged())); - - QDBusConnection::sessionBus().connect( - PEBBLED_DBUS_SERVICE, PEBBLED_DBUS_PATH, PEBBLED_DBUS_IFACE, - "openUrl", this, SIGNAL(openUrl(QString))); + QObject(parent), + systemd(new QDBusInterface("org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + QDBusConnection::sessionBus(), this)), + watch(new OrgPebbledWatchInterface(PEBBLED_DBUS_SERVICE, + PEBBLED_DBUS_PATH, + QDBusConnection::sessionBus(), this)) +{ + connect(watch, &OrgPebbledWatchInterface::NameChanged, + this, &PebbledInterface::nameChanged); + connect(watch, &OrgPebbledWatchInterface::AddressChanged, + this, &PebbledInterface::addressChanged); + connect(watch, &OrgPebbledWatchInterface::ConnectedChanged, + this, &PebbledInterface::connectedChanged); // simulate connected change on active changed // as the daemon might not had a chance to send 'connectedChanged' // when going down - connect(this, SIGNAL(activeChanged()), SIGNAL(connectedChanged())); - - systemd = new QDBusInterface("org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - QDBusConnection::sessionBus(), this); + connect(this, &PebbledInterface::activeChanged, + this, &PebbledInterface::connectedChanged); systemd->call("Subscribe"); @@ -59,9 +55,9 @@ void PebbledInterface::getUnitProperties() QDBusReply reply = QDBusConnection::sessionBus().call(request); if (reply.isValid()) { QVariantMap newProperties = reply.value(); - bool emitEnabledChanged = (properties["UnitFileState"] != newProperties["UnitFileState"]); - bool emitActiveChanged = (properties["ActiveState"] != newProperties["ActiveState"]); - properties = newProperties; + bool emitEnabledChanged = (unitProperties["UnitFileState"] != newProperties["UnitFileState"]); + bool emitActiveChanged = (unitProperties["ActiveState"] != newProperties["ActiveState"]); + unitProperties = newProperties; if (emitEnabledChanged) emit enabledChanged(); if (emitActiveChanged) emit activeChanged(); } else { @@ -73,22 +69,14 @@ void PebbledInterface::onPropertiesChanged(QString interface, QMapconnected(); } QString PebbledInterface::name() const { qDebug() << __FUNCTION__; - return PebbledDbusInterface.property(__FUNCTION__).toString(); + return watch->name(); } QString PebbledInterface::address() const { qDebug() << __FUNCTION__; - return PebbledDbusInterface.property(__FUNCTION__).toString(); + return watch->address(); } void PebbledInterface::ping() { qDebug() << __FUNCTION__; - PebbledDbusInterface.call("ping", 66); + watch->Ping(66); } void PebbledInterface::time() { qDebug() << __FUNCTION__; - PebbledDbusInterface.call("time"); + watch->SyncTime(); } void PebbledInterface::disconnect() { qDebug() << __FUNCTION__; - PebbledDbusInterface.call("disconnect"); + watch->Disconnect(); } void PebbledInterface::reconnect() { qDebug() << __FUNCTION__; - PebbledDbusInterface.call("reconnect"); + watch->Reconnect(); } -void PebbledInterface::test() +QUrl PebbledInterface::configureApp(const QUuid &uuid) { - qDebug() << __FUNCTION__; - PebbledDbusInterface.call("test"); + qDebug() << __FUNCTION__ << uuid; + QString url = watch->StartAppConfiguration(uuid.toString()); + return QUrl(url); } -void PebbledInterface::webviewClosed(const QString &result) +void PebbledInterface::setAppConfiguration(const QUuid &uuid, const QString &data) { - qDebug() << __FUNCTION__; - PebbledDbusInterface.call("webviewClosed", QVariant::fromValue(result)); + watch->SendAppConfigurationData(uuid.toString(), data); } diff --git a/app/pebbledinterface.h b/app/pebbledinterface.h index 7d00110..78724ad 100644 --- a/app/pebbledinterface.h +++ b/app/pebbledinterface.h @@ -2,51 +2,36 @@ #define PEBBLEDINTERFACE_H #include -#include -#include +#include +#include + +class OrgPebbledWatchInterface; class PebbledInterface : public QObject { Q_OBJECT - - static QString PEBBLED_SYSTEMD_UNIT; - static QString PEBBLED_DBUS_SERVICE; - static QString PEBBLED_DBUS_PATH; - static QString PEBBLED_DBUS_IFACE; - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) - bool enabled() const; - Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) - bool active() const; - Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) - bool connected() const; - - Q_PROPERTY(QVariantMap pebble READ pebble NOTIFY pebbleChanged) - QVariantMap pebble() const; - Q_PROPERTY(QString name READ name NOTIFY nameChanged) - QString name() const; - Q_PROPERTY(QString address READ address NOTIFY addressChanged) - QString address() const; - public: explicit PebbledInterface(QObject *parent = 0); + bool enabled() const; + bool active() const; + bool connected() const; + QString name() const; + QString address() const; + signals: void enabledChanged(); void activeChanged(); - void connectedChanged(); - void pebbleChanged(); void nameChanged(); void addressChanged(); - void openUrl(const QString &url); - public slots: void setEnabled(bool); void setActive(bool); @@ -54,19 +39,19 @@ public slots: void time(); void disconnect(); void reconnect(); - void test(); - void webviewClosed(const QString &result); + + QUrl configureApp(const QUuid &uuid); + void setAppConfiguration(const QUuid &uuid, const QString &data); private slots: void getUnitProperties(); void onPropertiesChanged(QString interface, QMap changed, QStringList invalidated); - void onPebbleChanged(); private: QDBusInterface *systemd; + OrgPebbledWatchInterface *watch; QDBusObjectPath unitPath; - - QVariantMap properties; + QVariantMap unitProperties; }; #endif // PEBBLEDINTERFACE_H diff --git a/daemon/daemon.pro b/daemon/daemon.pro index c59d408..3306541 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -20,7 +20,6 @@ SOURCES += \ notificationmanager.cpp \ watchconnector.cpp \ dbusconnector.cpp \ - dbusadaptor.cpp \ appmanager.cpp \ musicmanager.cpp \ datalogmanager.cpp \ @@ -38,7 +37,6 @@ HEADERS += \ notificationmanager.h \ watchconnector.h \ dbusconnector.h \ - dbusadaptor.h \ settings.h \ appmanager.h \ musicmanager.h \ @@ -51,10 +49,11 @@ HEADERS += \ packer.h OTHER_FILES += \ - org.pebbled.xml \ ../log4qt-debug.conf \ ../log4qt-release.conf +DBUS_ADAPTORS += ../org.pebbled.Watch.xml + INSTALLS += target pebbled confile target.path = /usr/bin diff --git a/daemon/dbusadaptor.cpp b/daemon/dbusadaptor.cpp deleted file mode 100644 index 25e2508..0000000 --- a/daemon/dbusadaptor.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.8 - * Command line was: qdbusxml2cpp -a dbusadaptor org.pebbled.xml - * - * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). - * - * This is an auto-generated file. - * Do not edit! All changes made to it will be lost. - */ - -#include "dbusadaptor.h" -#include -#include -#include -#include -#include -#include -#include - -/* - * Implementation of adaptor class PebbledAdaptor - */ - -PebbledAdaptor::PebbledAdaptor(QObject *parent) - : QDBusAbstractAdaptor(parent) -{ - // constructor - setAutoRelaySignals(true); -} - -PebbledAdaptor::~PebbledAdaptor() -{ - // destructor -} - -QString PebbledAdaptor::address() const -{ - // get the value of property address - return qvariant_cast< QString >(parent()->property("address")); -} - -bool PebbledAdaptor::connected() const -{ - // get the value of property connected - return qvariant_cast< bool >(parent()->property("connected")); -} - -QString PebbledAdaptor::name() const -{ - // get the value of property name - return qvariant_cast< QString >(parent()->property("name")); -} - -QVariantMap PebbledAdaptor::pebble() const -{ - // get the value of property pebble - return qvariant_cast< QVariantMap >(parent()->property("pebble")); -} - -void PebbledAdaptor::disconnect() -{ - // handle method call org.pebbled.disconnect - QMetaObject::invokeMethod(parent(), "disconnect"); -} - -void PebbledAdaptor::ping(int val) -{ - // handle method call org.pebbled.ping - QMetaObject::invokeMethod(parent(), "ping", Q_ARG(int, val)); -} - -void PebbledAdaptor::time() -{ - // handle method call org.pebbled.time - QMetaObject::invokeMethod(parent(), "time"); -} - -void PebbledAdaptor::reconnect() -{ - // handle method call org.pebbled.reconnect - QMetaObject::invokeMethod(parent(), "reconnect"); -} - -void PebbledAdaptor::test() -{ - QMetaObject::invokeMethod(parent(), "test"); -} - -void PebbledAdaptor::webviewClosed(const QString &result) -{ - QMetaObject::invokeMethod(parent(), "webviewClosed", Q_ARG(QString, result)); -} diff --git a/daemon/dbusadaptor.h b/daemon/dbusadaptor.h deleted file mode 100644 index f347f92..0000000 --- a/daemon/dbusadaptor.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.8 - * Command line was: qdbusxml2cpp -a dbusadaptor org.pebbled.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 DBUSADAPTOR_H_1404986135 -#define DBUSADAPTOR_H_1404986135 - -#include -#include -QT_BEGIN_NAMESPACE -class QByteArray; -template class QList; -template class QMap; -class QString; -class QStringList; -class QVariant; -QT_END_NAMESPACE - -/* - * Adaptor class for interface org.pebbled - */ -class PebbledAdaptor: public QDBusAbstractAdaptor -{ - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.pebbled") - Q_CLASSINFO("D-Bus Introspection", "" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" - "") -public: - PebbledAdaptor(QObject *parent); - virtual ~PebbledAdaptor(); - -public: // PROPERTIES - Q_PROPERTY(QString address READ address) - QString address() const; - - Q_PROPERTY(bool connected READ connected) - bool connected() const; - - Q_PROPERTY(QString name READ name) - QString name() const; - - Q_PROPERTY(QVariantMap pebble READ pebble) - QVariantMap pebble() const; - -public Q_SLOTS: // METHODS - void disconnect(); - void ping(int val); - void time(); - void reconnect(); - void test(); - void webviewClosed(const QString &result); -Q_SIGNALS: // SIGNALS - void connectedChanged(); - void pebbleChanged(); - void openUrl(const QString &s); -}; - -#endif diff --git a/daemon/dbusconnector.h b/daemon/dbusconnector.h index e6dd793..d4c1bcb 100644 --- a/daemon/dbusconnector.h +++ b/daemon/dbusconnector.h @@ -20,8 +20,8 @@ class DBusConnector : public QObject public: explicit DBusConnector(QObject *parent = 0); - QVariantMap pebble() { return pebbleProps; } - QStringList services() { return dbusServices; } + QVariantMap pebble() const { return pebbleProps; } + QStringList services() const { return dbusServices; } signals: void pebbleChanged(); diff --git a/daemon/manager.cpp b/daemon/manager.cpp index e732d38..38b3948 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -3,10 +3,11 @@ #include #include "manager.h" -#include "dbusadaptor.h" +#include "watch_adaptor.h" Manager::Manager(Settings *settings, QObject *parent) : QObject(parent), settings(settings), + proxy(new PebbledProxy(this)), watch(new WatchConnector(this)), dbus(new DBusConnector(this)), apps(new AppManager(this)), @@ -54,14 +55,16 @@ Manager::Manager(Settings *settings, QObject *parent) : connect(notifications, SIGNAL(twitterNotify(const QString &,const QString &)), SLOT(onTwitterNotify(const QString &,const QString &))); connect(notifications, SIGNAL(facebookNotify(const QString &,const QString &)), SLOT(onFacebookNotify(const QString &,const QString &))); - PebbledProxy *proxy = new PebbledProxy(this); - PebbledAdaptor *adaptor = new PebbledAdaptor(proxy); + connect(appmsg, &AppMsgManager::messageReceived, this, &Manager::onAppMessage); + QDBusConnection session = QDBusConnection::sessionBus(); - session.registerObject("/org/pebbled", proxy); + new WatchAdaptor(proxy); + session.registerObject("/org/pebbled/watch", proxy); session.registerService("org.pebbled"); - connect(dbus, SIGNAL(pebbleChanged()), adaptor, SIGNAL(pebbleChanged())); - connect(watch, SIGNAL(connectedChanged()), adaptor, SIGNAL(connectedChanged())); - connect(js, SIGNAL(appOpenUrl(QString)), adaptor, SIGNAL(openUrl(QString))); + + connect(dbus, &DBusConnector::pebbleChanged, proxy, &PebbledProxy::NameChanged); + connect(dbus, &DBusConnector::pebbleChanged, proxy, &PebbledProxy::AddressChanged); + connect(watch, &WatchConnector::connectedChanged, proxy, &PebbledProxy::ConnectedChanged); QString currentProfile = getCurrentProfile(); defaultProfile = currentProfile.isEmpty() ? "ambience" : currentProfile; @@ -289,7 +292,7 @@ void Manager::onMprisPropertiesChanged(QString interface, QMap logger()->debug() << "lastSeenMpris:" << lastSeenMpris; } -QString Manager::mpris() +QString Manager::mpris() const { const QStringList &services = dbus->services(); if (not lastSeenMpris.isEmpty() && services.contains(lastSeenMpris)) @@ -316,7 +319,7 @@ void Manager::setMprisMetadata(QVariantMap metadata) emit mprisMetadataChanged(mprisMetadata); } -QString Manager::getCurrentProfile() +QString Manager::getCurrentProfile() const { QDBusReply profile = QDBusConnection::sessionBus().call( QDBusMessage::createMethodCall("com.nokia.profiled", "/com/nokia/profiled", "com.nokia.profiled", "get_profile")); @@ -386,12 +389,6 @@ void Manager::transliterateMessage(const QString &text) } } -bool Manager::uploadApp(const QUuid &uuid, int slot) -{ - // TODO - return false; -} - void Manager::test() { logger()->debug() << "Starting test"; @@ -399,6 +396,11 @@ void Manager::test() js->showConfiguration(); } +void Manager::onAppMessage(const QUuid &uuid, const QVariantMap &data) +{ + emit proxy->AppMessage(uuid.toString(), data); +} + void Manager::onWebviewClosed(const QString &result) { js->handleWebviewClosed(result); diff --git a/daemon/manager.h b/daemon/manager.h index b0e15fb..c12d3dc 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -24,9 +24,9 @@ using namespace QtContacts; -class Manager : - public QObject, - protected QDBusContext +class PebbledProxy; + +class Manager : public QObject, protected QDBusContext { Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER @@ -40,6 +40,8 @@ class Manager : Settings *settings; + PebbledProxy *proxy; + WatchConnector *watch; DBusConnector *dbus; AppManager *apps; @@ -58,6 +60,7 @@ class Manager : QString defaultProfile; QString lastSeenMpris; + QVariantMap mprisMetadata; QScopedPointer transliterator; @@ -65,13 +68,11 @@ public: explicit Manager(Settings *settings, QObject *parent = 0); ~Manager(); - Q_INVOKABLE QString findPersonByNumber(QString number); - Q_INVOKABLE QString getCurrentProfile(); - Q_INVOKABLE QString mpris(); - QVariantMap mprisMetadata; - QVariantMap getMprisMetadata() { return mprisMetadata; } + QString findPersonByNumber(QString number); + QString getCurrentProfile() const; + QString mpris() const; - Q_INVOKABLE bool uploadApp(const QUuid &uuid, int slot = -1); + inline QVariantMap getMprisMetadata() const { return mprisMetadata; } protected: void transliterateMessage(const QString &text); @@ -100,31 +101,71 @@ private slots: void onMprisPropertiesChanged(QString,QMap,QStringList); void setMprisMetadata(QDBusArgument metadata); void setMprisMetadata(QVariantMap metadata); + + void onAppMessage(const QUuid &uuid, const QVariantMap &data); }; -class PebbledProxy : public QObject +/** This class is what's actually exported over D-Bus, + * so the names of the slots and properties must match with org.pebbled.Watch D-Bus interface. + * Case sensitive. Otherwise, _runtime_ failures will occur. */ +// The methods are marked inline so that they may be inlined inside qt_metacall +class PebbledProxy : public QObject, protected QDBusContext { Q_OBJECT - Q_PROPERTY(QVariantMap pebble READ pebble) - Q_PROPERTY(QString name READ pebbleName) - Q_PROPERTY(QString address READ pebbleAddress) - Q_PROPERTY(bool connected READ pebbleConnected) + Q_PROPERTY(QString Name READ Name NOTIFY NameChanged) + Q_PROPERTY(QString Address READ Address NOTIFY AddressChanged) + Q_PROPERTY(bool Connected READ Connected NOTIFY ConnectedChanged) - QVariantMap pebble() { return static_cast(parent())->dbus->pebble(); } - QString pebbleName() { return static_cast(parent())->dbus->pebble()["Name"].toString(); } - QString pebbleAddress() { return static_cast(parent())->dbus->pebble()["Address"].toString(); } - bool pebbleConnected() { return static_cast(parent())->watch->isConnected(); } + inline Manager* manager() const { return static_cast(parent()); } + inline QVariantMap pebble() const { return manager()->dbus->pebble(); } public: - explicit PebbledProxy(QObject *parent) : QObject(parent) {} + inline explicit PebbledProxy(QObject *parent) : QObject(parent) {} + + inline QString Name() const { return pebble()["Name"].toString(); } + inline QString Address() const { return pebble()["Address"].toString(); } + inline bool Connected() const { return manager()->watch->isConnected(); } public slots: - void ping(int val) { static_cast(parent())->watch->ping((unsigned int)val); } - void time() { static_cast(parent())->watch->time(); } - void disconnect() { static_cast(parent())->watch->disconnect(); } - void reconnect() { static_cast(parent())->watch->reconnect(); } - void test() { static_cast(parent())->test(); } - void webviewClosed(const QString &result) { static_cast(parent())->onWebviewClosed(result); } + inline void Disconnected() { manager()->watch->disconnect(); } + inline void Reconnect() { manager()->watch->reconnect(); } + inline void Ping(uint val) { manager()->watch->ping(val); } + inline void SyncTime() { manager()->watch->time(); } + + inline void LaunchApp(const QString &uuid) { /* TODO */ } + inline void CloseApp(const QString &uuid) { /* TODO */ } + + bool SendAppMessage(const QString &uuid, const QVariantMap &data) { + Q_ASSERT(calledFromDBus()); + const QDBusMessage msg = message(); + setDelayedReply(true); + manager()->appmsg->send(uuid, data, [this, msg]() { + QDBusMessage reply = msg.createReply(QVariant::fromValue(true)); + this->connection().send(reply); + }, [this, msg]() { + QDBusMessage reply = msg.createReply(QVariant::fromValue(false)); + this->connection().send(reply); + }); + return false; // D-Bus clients should never see this reply. + } + + QString StartAppConfiguration(const QString &uuid) { + Q_ASSERT(calledFromDBus()); + const QDBusMessage msg = message(); + setDelayedReply(true); + + // TODO + } + + inline void SendAppConfiguration(const QString &uuid, const QString &data) { + // TODO + } + +signals: + void NameChanged(); + void AddressChanged(); + void ConnectedChanged(); + void AppMessage(const QString &uuid, const QVariantMap &data); }; #endif // MANAGER_H diff --git a/daemon/org.pebbled.xml b/daemon/org.pebbled.xml deleted file mode 100644 index e255782..0000000 --- a/daemon/org.pebbled.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml new file mode 100644 index 0000000..72aab6b --- /dev/null +++ b/org.pebbled.Watch.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3 From 69822e4dcf541a52e4202d5ff566364fb90e6ec0 Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 3 Dec 2014 00:48:19 +0100 Subject: implement UI for JS app configuration --- app/app.pro | 2 +- app/pebbledinterface.cpp | 41 +++++++++++++++++++++++++---------------- app/pebbledinterface.h | 7 +++++-- app/qml/pages/AboutPage.qml | 2 +- app/qml/pages/AppConfigPage.qml | 34 ++++++++++++++++++++++++++++++++++ app/qml/pages/WatchPage.qml | 30 ++++++++++++++++++++++++++++++ app/qml/pages/WebViewPage.qml | 29 ----------------------------- app/qml/pebble.qml | 5 ----- daemon/jskitmanager.cpp | 4 ++++ daemon/manager.cpp | 6 +++++- daemon/manager.h | 3 +++ org.pebbled.Watch.xml | 5 ++++- 12 files changed, 112 insertions(+), 56 deletions(-) create mode 100644 app/qml/pages/AppConfigPage.qml delete mode 100644 app/qml/pages/WebViewPage.qml (limited to 'app') diff --git a/app/app.pro b/app/app.pro index e0ff449..ddf2dba 100644 --- a/app/app.pro +++ b/app/app.pro @@ -25,4 +25,4 @@ OTHER_FILES += \ qml/images/* \ pebble.desktop \ pebble.png \ - qml/pages/WebViewPage.qml + qml/pages/AppConfigPage.qml diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp index fb937f2..0aceaa0 100644 --- a/app/pebbledinterface.cpp +++ b/app/pebbledinterface.cpp @@ -3,7 +3,7 @@ static const QString PEBBLED_SYSTEMD_UNIT("pebbled.service"); static const QString PEBBLED_DBUS_SERVICE("org.pebbled"); -static const QString PEBBLED_DBUS_PATH("/org/pebbled/watch"); +static const QString PEBBLED_DBUS_PATH("/org/pebbled/Watch"); static const QString PEBBLED_DBUS_IFACE("org.pebbled.Watch"); PebbledInterface::PebbledInterface(QObject *parent) : @@ -22,6 +22,8 @@ PebbledInterface::PebbledInterface(QObject *parent) : this, &PebbledInterface::addressChanged); connect(watch, &OrgPebbledWatchInterface::ConnectedChanged, this, &PebbledInterface::connectedChanged); + connect(watch, &OrgPebbledWatchInterface::AppUuidChanged, + this, &PebbledInterface::appUuidChanged); // simulate connected change on active changed // as the daemon might not had a chance to send 'connectedChanged' @@ -67,7 +69,7 @@ void PebbledInterface::getUnitProperties() void PebbledInterface::onPropertiesChanged(QString interface, QMap changed, QStringList invalidated) { - qDebug() << __FUNCTION__ << interface << changed << invalidated; + qDebug() << Q_FUNC_INFO << interface << changed << invalidated; if (interface != "org.freedesktop.systemd1.Unit") return; if (invalidated.contains("UnitFileState") || invalidated.contains("ActiveState")) getUnitProperties(); @@ -75,7 +77,7 @@ void PebbledInterface::onPropertiesChanged(QString interface, QMapconnected(); } QString PebbledInterface::name() const { - qDebug() << __FUNCTION__; + qDebug() << Q_FUNC_INFO; return watch->name(); } QString PebbledInterface::address() const { - qDebug() << __FUNCTION__; + qDebug() << Q_FUNC_INFO; return watch->address(); } +QString PebbledInterface::appUuid() const +{ + qDebug() << Q_FUNC_INFO; + return watch->appUuid(); +} + void PebbledInterface::ping() { - qDebug() << __FUNCTION__; + qDebug() << Q_FUNC_INFO; watch->Ping(66); } void PebbledInterface::time() { - qDebug() << __FUNCTION__; + qDebug() << Q_FUNC_INFO; watch->SyncTime(); } void PebbledInterface::disconnect() { - qDebug() << __FUNCTION__; + qDebug() << Q_FUNC_INFO; watch->Disconnect(); } void PebbledInterface::reconnect() { - qDebug() << __FUNCTION__; + qDebug() << Q_FUNC_INFO; watch->Reconnect(); } -QUrl PebbledInterface::configureApp(const QUuid &uuid) +QUrl PebbledInterface::configureApp(const QString &uuid) { - qDebug() << __FUNCTION__ << uuid; - QString url = watch->StartAppConfiguration(uuid.toString()); + qDebug() << Q_FUNC_INFO << uuid; + QString url = watch->StartAppConfiguration(uuid); return QUrl(url); } -void PebbledInterface::setAppConfiguration(const QUuid &uuid, const QString &data) +void PebbledInterface::setAppConfiguration(const QString &uuid, const QString &data) { - watch->SendAppConfigurationData(uuid.toString(), data); + qDebug() << Q_FUNC_INFO << uuid << data; + watch->SendAppConfigurationData(uuid, data); } diff --git a/app/pebbledinterface.h b/app/pebbledinterface.h index 78724ad..f506e67 100644 --- a/app/pebbledinterface.h +++ b/app/pebbledinterface.h @@ -15,6 +15,7 @@ class PebbledInterface : public QObject Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString address READ address NOTIFY addressChanged) + Q_PROPERTY(QString appUuid READ appUuid NOTIFY appUuidChanged) public: explicit PebbledInterface(QObject *parent = 0); @@ -24,6 +25,7 @@ public: bool connected() const; QString name() const; QString address() const; + QString appUuid() const; signals: void enabledChanged(); @@ -31,6 +33,7 @@ signals: void connectedChanged(); void nameChanged(); void addressChanged(); + void appUuidChanged(); public slots: void setEnabled(bool); @@ -40,8 +43,8 @@ public slots: void disconnect(); void reconnect(); - QUrl configureApp(const QUuid &uuid); - void setAppConfiguration(const QUuid &uuid, const QString &data); + QUrl configureApp(const QString &uuid); + void setAppConfiguration(const QString &uuid, const QString &data); private slots: void getUnitProperties(); diff --git a/app/qml/pages/AboutPage.qml b/app/qml/pages/AboutPage.qml index bec1031..8fd009e 100644 --- a/app/qml/pages/AboutPage.qml +++ b/app/qml/pages/AboutPage.qml @@ -40,7 +40,7 @@ Page { anchors { left: parent.left right: parent.right - margins: Theme.paddingSmall + margins: Theme.paddingMedium } font.pixelSize: Theme.fontSizeTiny horizontalAlignment: Text.AlignJustify diff --git a/app/qml/pages/AppConfigPage.qml b/app/qml/pages/AppConfigPage.qml new file mode 100644 index 0000000..8fb31ca --- /dev/null +++ b/app/qml/pages/AppConfigPage.qml @@ -0,0 +1,34 @@ +import QtQuick 2.0 +import QtQml 2.1 +import QtWebKit 3.0 +import Sailfish.Silica 1.0 + +Page { + id: appConfigPage + + property alias url: webview.url + property string uuid + + SilicaWebView { + id: webview + anchors.fill: parent + + header: PageHeader { + title: "Configuring " + uuid + } + + onNavigationRequested: { + console.log("appconfig navigation requested to " + request.url); + var url = request.url.toString(); + if (/^pebblejs:\/\/close/.exec(url)) { + var data = decodeURI(url.substring(17)); + console.log("appconfig requesting close; data: " + data); + pebbled.setAppConfiguration(uuid, data); + pageStack.pop(); + request.action = WebView.IgnoreRequest; + } else { + request.action = WebView.AcceptRequest; + } + } + } +} diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml index 90e5ec9..8169507 100644 --- a/app/qml/pages/WatchPage.qml +++ b/app/qml/pages/WatchPage.qml @@ -77,6 +77,36 @@ Page { } } + + Label { + text: qsTr("App configuration") + font.family: Theme.fontFamilyHeading + color: Theme.highlightColor + anchors.right: parent.right + anchors.rightMargin: Theme.paddingMedium + } + + Button { + text: "Configure current app" + anchors { + left: parent.left + right: parent.right + margins: Theme.paddingLarge + } + onClicked: { + var uuid = pebbled.appUuid; + console.log("going to configureApp " + uuid); + var url = pebbled.configureApp(uuid); + console.log("obtained configure URL " + url); + if (url) { + pageStack.push(Qt.resolvedUrl("AppConfigPage.qml"), { + url: url, + uuid: uuid + }); + } + } + } + } } } diff --git a/app/qml/pages/WebViewPage.qml b/app/qml/pages/WebViewPage.qml deleted file mode 100644 index 2c6fcf0..0000000 --- a/app/qml/pages/WebViewPage.qml +++ /dev/null @@ -1,29 +0,0 @@ -import QtQuick 2.0 -import QtQml 2.1 -import QtWebKit 3.0 -import Sailfish.Silica 1.0 - -Page { - id: webviewPage - - property alias url: webview.url - - SilicaWebView { - id: webview - anchors.fill: parent - - onNavigationRequested: { - console.log("navigation requested to " + request.url); - var url = request.url.toString() - if (/^pebblejs:\/\/close/.exec(url)) { - var data = decodeURI(url.substring(17)); - console.log("match with pebble close regexp. data: " + data); - pebbled.webviewClosed(data); - pageStack.pop(); - request.action = WebView.IgnoreRequest; - } else { - request.action = WebView.AcceptRequest; - } - } - } -} diff --git a/app/qml/pebble.qml b/app/qml/pebble.qml index 2ff0839..da3bfb5 100644 --- a/app/qml/pebble.qml +++ b/app/qml/pebble.qml @@ -41,10 +41,5 @@ ApplicationWindow PebbledInterface { id: pebbled - - onOpenUrl: { - console.log("got open url: " + url); - pageStack.push(Qt.resolvedUrl("pages/WebViewPage.qml"), {url: url}); - } } } diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp index 9efc5c8..e1e4073 100644 --- a/daemon/jskitmanager.cpp +++ b/daemon/jskitmanager.cpp @@ -33,6 +33,8 @@ void JSKitManager::showConfiguration() { if (_engine) { _jspebble->invokeCallbacks("showConfiguration"); + } else { + logger()->warn() << "requested to show configuration, but JS engine is not running"; } } @@ -43,6 +45,8 @@ void JSKitManager::handleWebviewClosed(const QString &result) eventObj.setProperty("response", _engine->toScriptValue(result)); _jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj})); + } else { + logger()->warn() << "webview closed event, but JS engine is not running"; } } diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 0666de0..1fc8a20 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -61,7 +61,7 @@ Manager::Manager(Settings *settings, QObject *parent) : QDBusConnection session = QDBusConnection::sessionBus(); new WatchAdaptor(proxy); - session.registerObject("/org/pebbled/watch", proxy); + session.registerObject("/org/pebbled/Watch", proxy); session.registerService("org.pebbled"); connect(dbus, &DBusConnector::pebbleChanged, proxy, &PebbledProxy::NameChanged); @@ -399,6 +399,7 @@ void Manager::onAppMessage(const QUuid &uuid, const QVariantMap &data) void Manager::onAppOpened(const QUuid &uuid) { currentAppUuid = uuid; + emit proxy->AppUuidChanged(); emit proxy->AppOpened(uuid.toString()); } @@ -406,6 +407,7 @@ void Manager::onAppClosed(const QUuid &uuid) { currentAppUuid = QUuid(); emit proxy->AppClosed(uuid.toString()); + emit proxy->AppUuidChanged(); } bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data) { @@ -468,5 +470,7 @@ QString PebbledProxy::StartAppConfiguration(const QString &uuid) { manager()->js->showConfiguration(); + // Note that the above signal handler _might_ have been already called by this point. + return QString(); // This return value should never be used. } diff --git a/daemon/manager.h b/daemon/manager.h index f099aac..22382b8 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -117,6 +117,7 @@ class PebbledProxy : public QObject, protected QDBusContext Q_PROPERTY(QString Name READ Name NOTIFY NameChanged) Q_PROPERTY(QString Address READ Address NOTIFY AddressChanged) Q_PROPERTY(bool Connected READ Connected NOTIFY ConnectedChanged) + Q_PROPERTY(QString AppUuid READ AppUuid NOTIFY AppUuidChanged) inline Manager* manager() const { return static_cast(parent()); } inline QVariantMap pebble() const { return manager()->dbus->pebble(); } @@ -127,6 +128,7 @@ public: inline QString Name() const { return pebble()["Name"].toString(); } inline QString Address() const { return pebble()["Address"].toString(); } inline bool Connected() const { return manager()->watch->isConnected(); } + inline QString AppUuid() const { return manager()->currentAppUuid.toString(); } public slots: inline void Disconnected() { manager()->watch->disconnect(); } @@ -148,6 +150,7 @@ signals: void NameChanged(); void AddressChanged(); void ConnectedChanged(); + void AppUuidChanged(); void AppMessage(const QString &uuid, const QVariantMap &data); void AppOpened(const QString &uuid); void AppClosed(const QString &uuid); diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml index 11f20b7..759a6db 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -1,15 +1,18 @@ - + + + + -- cgit v1.2.3 From 87de94ea74c68b5ea0a4b125f1745c7449fbe4f1 Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 3 Dec 2014 01:19:57 +0100 Subject: fix actually setting the configuration, improve xhr support --- app/qml/pages/AppConfigPage.qml | 2 +- daemon/jskitmanager.cpp | 2 ++ daemon/jskitobjects.cpp | 67 ++++++++++++++++++++++++++++++++++++++--- daemon/jskitobjects.h | 11 ++++++- daemon/manager.cpp | 26 ++++++++++++++-- daemon/manager.h | 5 +-- 6 files changed, 100 insertions(+), 13 deletions(-) (limited to 'app') diff --git a/app/qml/pages/AppConfigPage.qml b/app/qml/pages/AppConfigPage.qml index 8fb31ca..7b969a3 100644 --- a/app/qml/pages/AppConfigPage.qml +++ b/app/qml/pages/AppConfigPage.qml @@ -21,7 +21,7 @@ Page { console.log("appconfig navigation requested to " + request.url); var url = request.url.toString(); if (/^pebblejs:\/\/close/.exec(url)) { - var data = decodeURI(url.substring(17)); + var data = decodeURIComponent(url.substring(17)); console.log("appconfig requesting close; data: " + data); pebbled.setAppConfiguration(uuid, data); pageStack.pop(); diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp index e1e4073..7bf8cdc 100644 --- a/daemon/jskitmanager.cpp +++ b/daemon/jskitmanager.cpp @@ -44,6 +44,8 @@ void JSKitManager::handleWebviewClosed(const QString &result) QJSValue eventObj = _engine->newObject(); eventObj.setProperty("response", _engine->toScriptValue(result)); + logger()->debug() << "webview closed with the following result: " << result; + _jspebble->invokeCallbacks("webviewclosed", QJSValueList({eventObj})); } else { logger()->warn() << "webview closed event, but JS engine is not running"; diff --git a/daemon/jskitobjects.cpp b/daemon/jskitobjects.cpp index a0bc0ba..728daf7 100644 --- a/daemon/jskitobjects.cpp +++ b/daemon/jskitobjects.cpp @@ -40,11 +40,27 @@ void JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSV logger()->debug() << "sendAppMessage" << data; _mgr->_appmsg->send(_appInfo.uuid(), data, [this, callbackForAck]() mutable { - logger()->debug() << "Invoking ack callback"; - callbackForAck.call(); + if (callbackForAck.isCallable()) { + logger()->debug() << "Invoking ack callback"; + QJSValue result = callbackForAck.call(); + if (result.isError()) { + logger()->warn() << "error while invoking ACK callback" << callbackForAck.toString() << ":" + << result.toString(); + } + } else { + logger()->debug() << "Ack callback not callable"; + } }, [this, callbackForNack]() mutable { - logger()->debug() << "Invoking nack callback"; - callbackForNack.call(); + if (callbackForNack.isCallable()) { + logger()->debug() << "Invoking nack callback"; + QJSValue result = callbackForNack.call(); + if (result.isError()) { + logger()->warn() << "error while invoking NACK callback" << callbackForNack.toString() << ":" + << result.toString(); + } + } else { + logger()->debug() << "Nack callback not callable"; + } }); } @@ -187,7 +203,10 @@ void JSKitXMLHttpRequest::send(const QString &body) buffer->setData(body.toUtf8()); logger()->debug() << "sending" << _verb << "to" << _request.url() << "with" << body; _reply = _net->sendCustomRequest(_request, _verb.toLatin1(), buffer); - connect(_reply, &QNetworkReply::finished, this, &JSKitXMLHttpRequest::handleReplyFinished); + connect(_reply, &QNetworkReply::finished, + this, &JSKitXMLHttpRequest::handleReplyFinished); + connect(_reply, static_cast(&QNetworkReply::error), + this, &JSKitXMLHttpRequest::handleReplyError); buffer->setParent(_reply); // So that it gets deleted alongside the reply object. } @@ -209,6 +228,26 @@ void JSKitXMLHttpRequest::setOnload(const QJSValue &value) _onload = value; } +QJSValue JSKitXMLHttpRequest::ontimeout() const +{ + return _ontimeout; +} + +void JSKitXMLHttpRequest::setOntimeout(const QJSValue &value) +{ + _ontimeout = value; +} + +QJSValue JSKitXMLHttpRequest::onerror() const +{ + return _onerror; +} + +void JSKitXMLHttpRequest::setOnerror(const QJSValue &value) +{ + _onerror = value; +} + unsigned short JSKitXMLHttpRequest::readyState() const { if (!_reply) { @@ -259,3 +298,21 @@ void JSKitXMLHttpRequest::handleReplyFinished() logger()->debug() << "No onload set"; } } + +void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code) +{ + if (!_reply) { + logger()->info() << "reply error too late"; + return; + } + + logger()->info() << "reply error" << code; + + if (_onerror.isCallable()) { + logger()->debug() << "going to call onerror handler:" << _onload.toString(); + QJSValue result = _onerror.callWithInstance(_mgr->engine()->newQObject(this)); + if (result.isError()) { + logger()->warn() << "JS error on onerror handler:" << result.toString(); + } + } +} diff --git a/daemon/jskitobjects.h b/daemon/jskitobjects.h index 4f000f1..849529f 100644 --- a/daemon/jskitobjects.h +++ b/daemon/jskitobjects.h @@ -17,7 +17,7 @@ public: Q_INVOKABLE void addEventListener(const QString &type, QJSValue function); Q_INVOKABLE void removeEventListener(const QString &type, QJSValue function); - Q_INVOKABLE void sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack); + Q_INVOKABLE void sendAppMessage(QJSValue message, QJSValue callbackForAck = QJSValue(), QJSValue callbackForNack = QJSValue()); Q_INVOKABLE void showSimpleNotificationOnPebble(const QString &title, const QString &body); @@ -79,6 +79,8 @@ class JSKitXMLHttpRequest : public QObject LOG4QT_DECLARE_QCLASS_LOGGER Q_PROPERTY(QJSValue onload READ onload WRITE setOnload) + Q_PROPERTY(QJSValue ontimeout READ ontimeout WRITE setOntimeout) + Q_PROPERTY(QJSValue onerror READ onerror WRITE setOnerror) Q_PROPERTY(unsigned short readyState READ readyState NOTIFY readyStateChanged) Q_PROPERTY(unsigned short status READ status NOTIFY statusChanged) Q_PROPERTY(QString responseText READ responseText NOTIFY responseTextChanged) @@ -102,6 +104,10 @@ public: QJSValue onload() const; void setOnload(const QJSValue &value); + QJSValue ontimeout() const; + void setOntimeout(const QJSValue &value); + QJSValue onerror() const; + void setOnerror(const QJSValue &value); unsigned short readyState() const; unsigned short status() const; @@ -114,6 +120,7 @@ signals: private slots: void handleReplyFinished(); + void handleReplyError(QNetworkReply::NetworkError code); private: JSKitManager *_mgr; @@ -123,6 +130,8 @@ private: QNetworkReply *_reply; QByteArray _response; QJSValue _onload; + QJSValue _ontimeout; + QJSValue _onerror; }; #endif // JSKITMANAGER_P_H diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 1fc8a20..6fd47a4 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -410,7 +410,8 @@ void Manager::onAppClosed(const QUuid &uuid) emit proxy->AppUuidChanged(); } -bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data) { +bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data) +{ Q_ASSERT(calledFromDBus()); const QDBusMessage msg = message(); setDelayedReply(true); @@ -424,7 +425,8 @@ bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data) return false; // D-Bus clients should never see this reply. } -QString PebbledProxy::StartAppConfiguration(const QString &uuid) { +QString PebbledProxy::StartAppConfiguration(const QString &uuid) +{ Q_ASSERT(calledFromDBus()); const QDBusMessage msg = message(); @@ -474,3 +476,23 @@ QString PebbledProxy::StartAppConfiguration(const QString &uuid) { return QString(); // This return value should never be used. } + +void PebbledProxy::SendAppConfigurationData(const QString &uuid, const QString &data) +{ + Q_ASSERT(calledFromDBus()); + const QDBusMessage msg = message(); + + if (manager()->currentAppUuid != uuid) { + sendErrorReply(msg.interface() + ".Error.AppNotRunning", + "The requested app is not currently opened in the watch"); + return; + } + + if (!manager()->js->isJSKitAppRunning()) { + sendErrorReply(msg.interface() + ".Error.JSNotActive", + "The requested app is not a PebbleKit JS application"); + return; + } + + manager()->js->handleWebviewClosed(data); +} diff --git a/daemon/manager.h b/daemon/manager.h index 22382b8..239c212 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -141,10 +141,7 @@ public slots: bool SendAppMessage(const QString &uuid, const QVariantMap &data); QString StartAppConfiguration(const QString &uuid); - - void SendAppConfiguration(const QString &uuid, const QString &data) { - // TODO - } + void SendAppConfigurationData(const QString &uuid, const QString &data); signals: void NameChanged(); -- cgit v1.2.3 From df30ca18eebd2dfec03c589b607d45a5891cf2b2 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 14 Dec 2014 00:45:55 +0100 Subject: add UI to install/remove apps from watch --- app/app.pro | 3 +- app/pebbledinterface.cpp | 140 ++++++++++++++++++++++++++++++++++++- app/pebbledinterface.h | 29 +++++++- app/qml/pages/AppConfigPage.qml | 9 ++- app/qml/pages/InstallAppDialog.qml | 66 +++++++++++++++++ app/qml/pages/WatchPage.qml | 125 ++++++++++++++++++++++++++++----- 6 files changed, 347 insertions(+), 25 deletions(-) create mode 100644 app/qml/pages/InstallAppDialog.qml (limited to 'app') diff --git a/app/app.pro b/app/app.pro index ddf2dba..d375800 100644 --- a/app/app.pro +++ b/app/app.pro @@ -25,4 +25,5 @@ OTHER_FILES += \ qml/images/* \ pebble.desktop \ pebble.png \ - qml/pages/AppConfigPage.qml + qml/pages/AppConfigPage.qml \ + qml/pages/InstallAppDialog.qml diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp index 0aceaa0..588e24a 100644 --- a/app/pebbledinterface.cpp +++ b/app/pebbledinterface.cpp @@ -24,6 +24,13 @@ PebbledInterface::PebbledInterface(QObject *parent) : this, &PebbledInterface::connectedChanged); connect(watch, &OrgPebbledWatchInterface::AppUuidChanged, this, &PebbledInterface::appUuidChanged); + connect(watch, &OrgPebbledWatchInterface::AppSlotsChanged, + this, &PebbledInterface::refreshAppSlots); + connect(watch, &OrgPebbledWatchInterface::AllAppsChanged, + this, &PebbledInterface::refreshAllApps); + + connect(watch, &OrgPebbledWatchInterface::ConnectedChanged, + this, &PebbledInterface::onWatchConnectedChanged); // simulate connected change on active changed // as the daemon might not had a chance to send 'connectedChanged' @@ -46,6 +53,11 @@ PebbledInterface::PebbledInterface(QObject *parent) : } else { qWarning() << unit.error().message(); } + + if (watch->isValid()) { + refreshAllApps(); + refreshAppSlots(); + } } void PebbledInterface::getUnitProperties() @@ -161,8 +173,27 @@ void PebbledInterface::reconnect() QUrl PebbledInterface::configureApp(const QString &uuid) { qDebug() << Q_FUNC_INFO << uuid; - QString url = watch->StartAppConfiguration(uuid); - return QUrl(url); + QDBusPendingReply reply = watch->StartAppConfiguration(uuid); + reply.waitForFinished(); + if (reply.isError()) { + qWarning() << "Received error:" << reply.error().message(); + return QUrl(); + } else { + return QUrl(reply.value()); + } +} + +bool PebbledInterface::isAppInstalled(const QString &uuid) +{ + QUuid u(uuid); + + foreach (const QString &s, _appSlots) { + if (QUuid(s) == u) { + return true; + } + } + + return false; } void PebbledInterface::setAppConfiguration(const QString &uuid, const QString &data) @@ -170,3 +201,108 @@ void PebbledInterface::setAppConfiguration(const QString &uuid, const QString &d qDebug() << Q_FUNC_INFO << uuid << data; watch->SendAppConfigurationData(uuid, data); } + +void PebbledInterface::launchApp(const QString &uuid) +{ + qDebug() << Q_FUNC_INFO << uuid; + QDBusPendingReply<> reply = watch->LaunchApp(uuid); + reply.waitForFinished(); + + // TODO Terrible hack; need to give time for the watch to open the app + // A better solution would be to wait until AppUuidChanged is generated. + int sleep_count = 0; + while (watch->appUuid() != uuid && sleep_count < 5) { + QThread::sleep(1); + sleep_count++; + } +} + +void PebbledInterface::uploadApp(const QString &uuid, int slot) +{ + qDebug() << Q_FUNC_INFO << uuid << slot; + QDBusPendingReply<> reply = watch->UploadApp(uuid, slot); + reply.waitForFinished(); +} + +void PebbledInterface::unloadApp(int slot) +{ + qDebug() << Q_FUNC_INFO << slot; + QDBusPendingReply<> reply = watch->UnloadApp(slot); + reply.waitForFinished(); +} + +QStringList PebbledInterface::appSlots() const +{ + return _appSlots; +} + +QVariantList PebbledInterface::allApps() const +{ + return _apps; +} + +QVariantMap PebbledInterface::appInfoByUuid(const QString &uuid) const +{ + int index = _appsByUuid.value(QUuid(uuid), -1); + if (index >= 0) { + return _apps[index].toMap(); + } else { + return QVariantMap(); + } +} + +void PebbledInterface::onWatchConnectedChanged() +{ + qDebug() << Q_FUNC_INFO; + if (watch->connected()) { + refreshAllApps(); + refreshAppSlots(); + } +} + +void PebbledInterface::refreshAppSlots() +{ + qDebug() << "refreshing app slots list"; + _appSlots = watch->appSlots(); + emit appSlotsChanged(); +} + +void PebbledInterface::refreshAllApps() +{ + _apps.clear(); + _appsByUuid.clear(); + + qDebug() << "refreshing all apps list"; + + const QVariantList l = watch->allApps(); + foreach (const QVariant &v, l) { + QVariantMap orig = qdbus_cast(v.value()); + QUuid uuid = orig.value("uuid").toUuid(); + if (uuid.isNull()) { + qWarning() << "Invalid app uuid received" << orig; + continue; + } + + QVariantMap m; + m.insert("uuid", uuid.toString()); + m.insert("shortName", orig.value("short-name")); + m.insert("longName", orig.value("long-name")); + + _apps.append(QVariant::fromValue(m)); + } + + std::sort(_apps.begin(), _apps.end(), [](const QVariant &v1, const QVariant &v2) { + const QVariantMap &a = v1.toMap(); + const QVariantMap &b = v2.toMap(); + return a.value("shortName").toString() < b.value("shortName").toString(); + }); + + for (int i = 0; i < _apps.size(); ++i) { + QUuid uuid = _apps[i].toMap().value("uuid").toUuid(); + _appsByUuid.insert(uuid, i); + } + + qDebug() << _appsByUuid.size() << "different app uuids known"; + + emit allAppsChanged(); +} diff --git a/app/pebbledinterface.h b/app/pebbledinterface.h index f506e67..e468505 100644 --- a/app/pebbledinterface.h +++ b/app/pebbledinterface.h @@ -3,6 +3,8 @@ #include #include +#include +#include #include class OrgPebbledWatchInterface; @@ -17,6 +19,9 @@ class PebbledInterface : public QObject Q_PROPERTY(QString address READ address NOTIFY addressChanged) Q_PROPERTY(QString appUuid READ appUuid NOTIFY appUuidChanged) + Q_PROPERTY(QStringList appSlots READ appSlots NOTIFY appSlotsChanged) + Q_PROPERTY(QVariantList allApps READ allApps NOTIFY allAppsChanged) + public: explicit PebbledInterface(QObject *parent = 0); @@ -27,6 +32,15 @@ public: QString address() const; QString appUuid() const; + QStringList appSlots() const; + QVariantList allApps() const; + + Q_INVOKABLE QVariantMap appInfoByUuid(const QString& uuid) const; + + Q_INVOKABLE QUrl configureApp(const QString &uuid); + + Q_INVOKABLE bool isAppInstalled(const QString &uuid); + signals: void enabledChanged(); void activeChanged(); @@ -34,6 +48,8 @@ signals: void nameChanged(); void addressChanged(); void appUuidChanged(); + void appSlotsChanged(); + void allAppsChanged(); public slots: void setEnabled(bool); @@ -43,18 +59,29 @@ public slots: void disconnect(); void reconnect(); - QUrl configureApp(const QString &uuid); void setAppConfiguration(const QString &uuid, const QString &data); + void launchApp(const QString &uuid); + void uploadApp(const QString &uuid, int slot); + void unloadApp(int slot); + private slots: + void onWatchConnectedChanged(); void getUnitProperties(); void onPropertiesChanged(QString interface, QMap changed, QStringList invalidated); + void refreshAppSlots(); + void refreshAllApps(); private: QDBusInterface *systemd; OrgPebbledWatchInterface *watch; QDBusObjectPath unitPath; QVariantMap unitProperties; + + // Cached properties + QStringList _appSlots; + QVariantList _apps; + QHash _appsByUuid; }; #endif // PEBBLEDINTERFACE_H diff --git a/app/qml/pages/AppConfigPage.qml b/app/qml/pages/AppConfigPage.qml index 7b969a3..10fbe05 100644 --- a/app/qml/pages/AppConfigPage.qml +++ b/app/qml/pages/AppConfigPage.qml @@ -7,14 +7,14 @@ Page { id: appConfigPage property alias url: webview.url - property string uuid + property string name SilicaWebView { id: webview anchors.fill: parent header: PageHeader { - title: "Configuring " + uuid + title: "Configuring " + name } onNavigationRequested: { @@ -31,4 +31,9 @@ Page { } } } + + ViewPlaceholder { + enabled: url == "" + text: qsTr("No configuration settings available") + } } diff --git a/app/qml/pages/InstallAppDialog.qml b/app/qml/pages/InstallAppDialog.qml new file mode 100644 index 0000000..3a3c0b1 --- /dev/null +++ b/app/qml/pages/InstallAppDialog.qml @@ -0,0 +1,66 @@ +import QtQuick 2.0 +import QtQml 2.1 +import Sailfish.Silica 1.0 + +Dialog { + id: installAppPage + + property string selectedUuid; + + SilicaListView { + id: appList + anchors.fill: parent + + header: DialogHeader { + title: qsTr("Install app") + defaultAcceptText: qsTr("Install") + } + + VerticalScrollDecorator { flickable: flickable } + + currentIndex: -1 + + delegate: ListItem { + id: appDelegate + contentHeight: Theme.itemSizeSmall + + property string uuid: modelData.uuid + property bool alreadyInstalled: pebbled.isAppInstalled(uuid) + + Image { + id: appImage + anchors { + top: parent.top + left: parent.left + leftMargin: Theme.paddingLarge + } + width: Theme.itemSizeSmall + } + + Label { + id: appName + anchors { + left: appImage.right + leftMargin: Theme.paddingMedium + right: parent.right + rightMargin: Theme.paddiumLarge + verticalCenter: parent.verticalCenter + } + text: modelData.longName + color: appDelegate.highlighted ? Theme.highlightColor : Theme.primaryColor + } + + onClicked: { + appList.currentIndex = index + if (!alreadyInstalled) { + selectedUuid = uuid + accept(); + } + } + } + + model: pebbled.allApps + } + + canAccept: appList.currentIndex >= 0 && !appList.currentItem.alreadyInstalled +} diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml index 8169507..ce9d636 100644 --- a/app/qml/pages/WatchPage.qml +++ b/app/qml/pages/WatchPage.qml @@ -34,7 +34,7 @@ import QtQml 2.1 import Sailfish.Silica 1.0 Page { - id: page + id: watchPage SilicaFlickable { id: flickable @@ -45,9 +45,8 @@ Page { Column { id: column + width: watchPage.width - width: page.width - spacing: Theme.paddingLarge PageHeader { title: pebbled.name } @@ -77,36 +76,124 @@ Page { } } + Item { + height: Theme.paddingMedium + } Label { - text: qsTr("App configuration") + text: qsTr("Installed applications") font.family: Theme.fontFamilyHeading color: Theme.highlightColor anchors.right: parent.right anchors.rightMargin: Theme.paddingMedium } - Button { - text: "Configure current app" - anchors { - left: parent.left - right: parent.right - margins: Theme.paddingLarge - } - onClicked: { - var uuid = pebbled.appUuid; - console.log("going to configureApp " + uuid); - var url = pebbled.configureApp(uuid); - console.log("obtained configure URL " + url); - if (url) { + Repeater { + id: slotsRepeater + model: pebbled.appSlots + + ListItem { + id: slotDelegate + menu: slotMenu + contentHeight: Theme.itemSizeSmall + + property bool isEmptySlot: modelData === "" + property var appInfo: pebbled.appInfoByUuid(modelData) + property bool isKnownApp: appInfo.hasOwnProperty("uuid") + property bool busy: false + + function configure() { + var uuid = modelData; + pebbled.launchApp(uuid); + console.log("going to call configure on app with uuid " + uuid); + var url = pebbled.configureApp(uuid); + console.log("received url: " + url); pageStack.push(Qt.resolvedUrl("AppConfigPage.qml"), { url: url, - uuid: uuid + name: appInfo.longName }); } + + function remove() { + remorseAction(qsTr("Uninstalling"), function() { + busy = true; + pebbled.unloadApp(index); + }); + } + + function install() { + var dialog = pageStack.push(Qt.resolvedUrl("InstallAppDialog.qml")); + dialog.accepted.connect(function() { + var uuid = dialog.selectedUuid; + + if (pebbled.isAppInstalled(uuid)) { + console.warn("uuid already installed"); + return; + } + + var slot = index; + console.log("installing " + uuid + " into " + slot); + busy = true; + pebbled.uploadApp(uuid, slot); + }); + + } + + Image { + id: slotImage + anchors { + top: parent.top + left: parent.left + leftMargin: Theme.paddingLarge + } + width: Theme.itemSizeSmall + } + + BusyIndicator { + id: slotBusy + anchors.centerIn: slotImage + running: slotDelegate.busy + } + + Label { + id: slotName + anchors { + left: slotImage.right + leftMargin: Theme.paddingMedium + right: parent.right + rightMargin: Theme.paddiumLarge + verticalCenter: parent.verticalCenter + } + text: isEmptySlot ? qsTr("(empty slot)") : (isKnownApp ? appInfo.longName : qsTr("(slot in use by unknown app)")) + color: slotDelegate.highlighted ? Theme.highlightColor : Theme.primaryColor + onTextChanged: slotDelegate.busy = false; + } + + Component { + id: slotMenu + ContextMenu { + MenuItem { + text: qsTr("Configure...") + visible: !isEmptySlot && isKnownApp + onClicked: configure(); + } + MenuItem { + text: qsTr("Uninstall") + visible: !isEmptySlot + onClicked: remove(); + } + } + } + + onClicked: { + if (isEmptySlot) { + install(); + } else { + showMenu(); + } + } } } - } } } -- cgit v1.2.3 From f40514fe681f5163deb5f579140ef4f7ac77f5a8 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 14 Dec 2014 03:26:46 +0100 Subject: add icons to the slots managament UI --- app/app.pro | 8 ++- app/pebble.cpp | 12 +++- app/pebbleappiconprovider.cpp | 28 ++++++++ app/pebbleappiconprovider.h | 18 +++++ app/pebbledinterface.cpp | 18 ++++- app/pebbledinterface.h | 6 +- app/qml/pages/AppConfigPage.qml | 14 +++- app/qml/pages/InstallAppDialog.qml | 17 +++-- app/qml/pages/WatchPage.qml | 49 ++++++++++--- app/qml/pebble.qml | 4 -- daemon/appinfo.cpp | 25 ++++++- daemon/appinfo.h | 9 ++- daemon/appmanager.cpp | 142 ++++++++++++++++++++++++++++++++++--- daemon/appmanager.h | 2 + daemon/manager.cpp | 5 ++ 15 files changed, 315 insertions(+), 42 deletions(-) create mode 100644 app/pebbleappiconprovider.cpp create mode 100644 app/pebbleappiconprovider.h (limited to 'app') diff --git a/app/app.pro b/app/app.pro index d375800..48fcf68 100644 --- a/app/app.pro +++ b/app/app.pro @@ -3,16 +3,18 @@ TARGET = pebble CONFIG += sailfishapp QT += dbus -QMAKE_CXXFLAGS += -std=c++0x +CONFIG += c++11 DEFINES += APP_VERSION=\\\"$$VERSION\\\" SOURCES += \ pebble.cpp \ - pebbledinterface.cpp + pebbledinterface.cpp \ + pebbleappiconprovider.cpp HEADERS += \ - pebbledinterface.h + pebbledinterface.h \ + pebbleappiconprovider.h DBUS_INTERFACES += ../org.pebbled.Watch.xml diff --git a/app/pebble.cpp b/app/pebble.cpp index 44f1aeb..41da080 100644 --- a/app/pebble.cpp +++ b/app/pebble.cpp @@ -33,16 +33,22 @@ #include #include "pebbledinterface.h" +#include "pebbleappiconprovider.h" int main(int argc, char *argv[]) { - // Register Pebble daemon interface object on QML side - qmlRegisterType("org.pebbled", 0, 1, "PebbledInterface"); - QScopedPointer app(SailfishApp::application(argc, argv)); + qmlRegisterUncreatableType("org.pebbled", 0, 1, "PebbledInterface", + "Please use pebbled context property"); + QScopedPointer view(SailfishApp::createView()); + QScopedPointer pebbled(new PebbledInterface); + QScopedPointer appicons(new PebbleAppIconProvider(pebbled.data())); + view->rootContext()->setContextProperty("APP_VERSION", APP_VERSION); + view->rootContext()->setContextProperty("pebbled", pebbled.data()); + view->engine()->addImageProvider("pebble-app-icon", appicons.data()); view->setSource(SailfishApp::pathTo("qml/pebble.qml")); view->show(); diff --git a/app/pebbleappiconprovider.cpp b/app/pebbleappiconprovider.cpp new file mode 100644 index 0000000..0e694ff --- /dev/null +++ b/app/pebbleappiconprovider.cpp @@ -0,0 +1,28 @@ +#include +#include +#include "pebbleappiconprovider.h" + +PebbleAppIconProvider::PebbleAppIconProvider(PebbledInterface *interface) + : QQuickImageProvider(QQmlImageProviderBase::Image), pebbled(interface) +{ +} + +QImage PebbleAppIconProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + QUuid uuid(QUrl::fromPercentEncoding(id.toLatin1())); + QImage img = pebbled->menuIconForApp(uuid); + + if (requestedSize.width() > 0 && requestedSize.height() > 0) { + img = img.scaled(requestedSize, Qt::KeepAspectRatio); + } else if (requestedSize.width() > 0) { + img = img.scaledToWidth(requestedSize.width()); + } else if (requestedSize.height() > 0) { + img = img.scaledToHeight(requestedSize.height()); + } + + if (size) { + *size = img.size(); + } + + return img; +} diff --git a/app/pebbleappiconprovider.h b/app/pebbleappiconprovider.h new file mode 100644 index 0000000..c76641a --- /dev/null +++ b/app/pebbleappiconprovider.h @@ -0,0 +1,18 @@ +#ifndef PEBBLEAPPICONPROVIDER_H +#define PEBBLEAPPICONPROVIDER_H + +#include +#include "pebbledinterface.h" + +class PebbleAppIconProvider : public QQuickImageProvider +{ +public: + explicit PebbleAppIconProvider(PebbledInterface *interface); + + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); + +private: + PebbledInterface *pebbled; +}; + +#endif // PEBBLEAPPICONPROVIDER_H diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp index 588e24a..c978dd0 100644 --- a/app/pebbledinterface.cpp +++ b/app/pebbledinterface.cpp @@ -183,7 +183,7 @@ QUrl PebbledInterface::configureApp(const QString &uuid) } } -bool PebbledInterface::isAppInstalled(const QString &uuid) +bool PebbledInterface::isAppInstalled(const QString &uuid) const { QUuid u(uuid); @@ -196,6 +196,11 @@ bool PebbledInterface::isAppInstalled(const QString &uuid) return false; } +QImage PebbledInterface::menuIconForApp(const QUuid &uuid) const +{ + return _appMenuIcons.value(uuid); +} + void PebbledInterface::setAppConfiguration(const QString &uuid, const QString &data) { qDebug() << Q_FUNC_INFO << uuid << data; @@ -210,8 +215,11 @@ void PebbledInterface::launchApp(const QString &uuid) // TODO Terrible hack; need to give time for the watch to open the app // A better solution would be to wait until AppUuidChanged is generated. + QUuid u(uuid); + if (u.isNull()) return; int sleep_count = 0; - while (watch->appUuid() != uuid && sleep_count < 5) { + while (QUuid(watch->appUuid()) != u && sleep_count < 5) { + qDebug() << "Waiting for" << u.toString() << "to launch"; QThread::sleep(1); sleep_count++; } @@ -271,6 +279,7 @@ void PebbledInterface::refreshAllApps() { _apps.clear(); _appsByUuid.clear(); + _appMenuIcons.clear(); qDebug() << "refreshing all apps list"; @@ -288,6 +297,11 @@ void PebbledInterface::refreshAllApps() m.insert("shortName", orig.value("short-name")); m.insert("longName", orig.value("long-name")); + QByteArray pngIcon = orig.value("menu-icon").toByteArray(); + if (!pngIcon.isEmpty()) { + _appMenuIcons.insert(uuid, QImage::fromData(pngIcon, "PNG")); + } + _apps.append(QVariant::fromValue(m)); } diff --git a/app/pebbledinterface.h b/app/pebbledinterface.h index e468505..51efa12 100644 --- a/app/pebbledinterface.h +++ b/app/pebbledinterface.h @@ -5,6 +5,7 @@ #include #include #include +#include #include class OrgPebbledWatchInterface; @@ -39,7 +40,9 @@ public: Q_INVOKABLE QUrl configureApp(const QString &uuid); - Q_INVOKABLE bool isAppInstalled(const QString &uuid); + Q_INVOKABLE bool isAppInstalled(const QString &uuid) const; + + QImage menuIconForApp(const QUuid &uuid) const; signals: void enabledChanged(); @@ -82,6 +85,7 @@ private: QStringList _appSlots; QVariantList _apps; QHash _appsByUuid; + QHash _appMenuIcons; }; #endif // PEBBLEDINTERFACE_H diff --git a/app/qml/pages/AppConfigPage.qml b/app/qml/pages/AppConfigPage.qml index 10fbe05..00eb05c 100644 --- a/app/qml/pages/AppConfigPage.qml +++ b/app/qml/pages/AppConfigPage.qml @@ -11,6 +11,7 @@ Page { SilicaWebView { id: webview + visible: url != "" anchors.fill: parent header: PageHeader { @@ -32,8 +33,17 @@ Page { } } - ViewPlaceholder { - enabled: url == "" + Text { + anchors.centerIn: parent + visible: url == "" text: qsTr("No configuration settings available") + width: parent.width - 2*Theme.paddingLarge + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + font { + pixelSize: Theme.fontSizeLarge + family: Theme.fontFamilyHeading + } + color: Theme.highlightColor } } diff --git a/app/qml/pages/InstallAppDialog.qml b/app/qml/pages/InstallAppDialog.qml index 3a3c0b1..79283a6 100644 --- a/app/qml/pages/InstallAppDialog.qml +++ b/app/qml/pages/InstallAppDialog.qml @@ -27,20 +27,29 @@ Dialog { property string uuid: modelData.uuid property bool alreadyInstalled: pebbled.isAppInstalled(uuid) - Image { - id: appImage + Item { + id: appIcon + width: Theme.itemSizeSmall + height: Theme.itemSizeSmall + anchors { top: parent.top left: parent.left leftMargin: Theme.paddingLarge } - width: Theme.itemSizeSmall + + Image { + id: appImage + anchors.centerIn: parent + source: "image://pebble-app-icon/" + uuid; + scale: 2 + } } Label { id: appName anchors { - left: appImage.right + left: appIcon.right leftMargin: Theme.paddingMedium right: parent.right rightMargin: Theme.paddiumLarge diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml index ce9d636..3a712ab 100644 --- a/app/qml/pages/WatchPage.qml +++ b/app/qml/pages/WatchPage.qml @@ -77,7 +77,8 @@ Page { } Item { - height: Theme.paddingMedium + width: parent.width + height: Theme.paddingLarge } Label { @@ -139,26 +140,49 @@ Page { } - Image { - id: slotImage + Item { + id: slotIcon + width: Theme.itemSizeSmall + height: Theme.itemSizeSmall + anchors { top: parent.top left: parent.left leftMargin: Theme.paddingLarge } - width: Theme.itemSizeSmall - } - BusyIndicator { - id: slotBusy - anchors.centerIn: slotImage - running: slotDelegate.busy + Image { + id: slotImage + anchors.centerIn: parent + source: isKnownApp ? "image://pebble-app-icon/" + modelData : "" + scale: 2 + visible: !isEmptySlot && isKnownApp && !slotBusy.running + } + + Rectangle { + width: 30 + height: 30 + anchors.centerIn: parent + scale: 2 + border { + width: 2 + color: slotDelegate.highlighted ? Theme.highlightColor : Theme.primaryColor + } + color: "transparent" + visible: isEmptySlot && !slotBusy.running + } + + BusyIndicator { + id: slotBusy + anchors.centerIn: parent + running: slotDelegate.busy + } } Label { id: slotName anchors { - left: slotImage.right + left: slotIcon.right leftMargin: Theme.paddingMedium right: parent.right rightMargin: Theme.paddiumLarge @@ -172,6 +196,11 @@ Page { Component { id: slotMenu ContextMenu { + MenuItem { + text: qsTr("Install app...") + visible: isEmptySlot + onClicked: install(); + } MenuItem { text: qsTr("Configure...") visible: !isEmptySlot && isKnownApp diff --git a/app/qml/pebble.qml b/app/qml/pebble.qml index da3bfb5..2e26ebe 100644 --- a/app/qml/pebble.qml +++ b/app/qml/pebble.qml @@ -38,8 +38,4 @@ ApplicationWindow { initialPage: Component { ManagerPage { } } cover: Qt.resolvedUrl("cover/CoverPage.qml") - - PebbledInterface { - id: pebbled - } } diff --git a/daemon/appinfo.cpp b/daemon/appinfo.cpp index fd43248..4397abc 100644 --- a/daemon/appinfo.cpp +++ b/daemon/appinfo.cpp @@ -1,5 +1,6 @@ -#include "appinfo.h" #include +#include +#include "appinfo.h" struct AppInfoData : public QSharedData { QUuid uuid; @@ -13,6 +14,7 @@ struct AppInfoData : public QSharedData { AppInfo::Capabilities capabilities; QHash keyInts; QHash keyNames; + QImage menuIcon; QString path; }; @@ -21,6 +23,7 @@ AppInfo::AppInfo() : d(new AppInfoData) d->versionCode = 0; d->watchface = false; d->jskit = false; + d->capabilities = 0; } AppInfo::AppInfo(const AppInfo &rhs) : d(rhs.d) @@ -154,6 +157,26 @@ int AppInfo::valueForAppKey(const QString &key) const return d->keyInts.value(key, -1); } +QImage AppInfo::menuIcon() const +{ + return d->menuIcon; +} + +QByteArray AppInfo::menuIconAsPng() const +{ + QByteArray data; + QBuffer buf(&data); + buf.open(QIODevice::WriteOnly); + d->menuIcon.save(&buf, "PNG"); + buf.close(); + return data; +} + +void AppInfo::setMenuIcon(const QImage &img) +{ + d->menuIcon = img; +} + QString AppInfo::path() const { return d->path; diff --git a/daemon/appinfo.h b/daemon/appinfo.h index 6f97639..3d5c4b4 100644 --- a/daemon/appinfo.h +++ b/daemon/appinfo.h @@ -1,10 +1,10 @@ #ifndef APPINFO_H #define APPINFO_H -#include +#include #include #include -#include +#include class AppInfoData; @@ -28,6 +28,7 @@ public: Q_PROPERTY(bool watchface READ isWatchface WRITE setWatchface) Q_PROPERTY(bool jskit READ isJSKit WRITE setJSKit) Q_PROPERTY(Capabilities capabilities READ capabilities WRITE setCapabilities) + Q_PROPERTY(QImage menuIcon READ menuIcon WRITE setMenuIcon) Q_PROPERTY(QString path READ path WRITE setPath) public: @@ -71,6 +72,10 @@ public: bool hasAppKey(const QString &key) const; int valueForAppKey(const QString &key) const; + QImage menuIcon() const; + QByteArray menuIconAsPng() const; + void setMenuIcon(const QImage &img); + QString path() const; void setPath(const QString &string); diff --git a/daemon/appmanager.cpp b/daemon/appmanager.cpp index 10f2e3e..8745160 100644 --- a/daemon/appmanager.cpp +++ b/daemon/appmanager.cpp @@ -4,6 +4,17 @@ #include #include #include "appmanager.h" +#include "unpacker.h" +#include "stm32crc.h" + +namespace { +struct ResourceEntry { + int index; + quint32 offset; + quint32 length; + quint32 crc; +}; +} AppManager::AppManager(QObject *parent) : QObject(parent), @@ -116,18 +127,55 @@ void AppManager::scanApp(const QString &path) info.setWatchface(watchapp["watchface"].toBool()); info.setJSKit(appDir.exists("pebble-js-app.js")); - const QJsonArray capabilities = root["capabilities"].toArray(); - AppInfo::Capabilities caps = 0; - for (QJsonArray::const_iterator it = capabilities.constBegin(); it != capabilities.constEnd(); ++it) { - QString cap = (*it).toString(); - if (cap == "location") caps |= AppInfo::Location; - if (cap == "configurable") caps |= AppInfo::Configurable; + if (root.contains("capabilities")) { + const QJsonArray capabilities = root["capabilities"].toArray(); + AppInfo::Capabilities caps = 0; + for (auto it = capabilities.constBegin(); it != capabilities.constEnd(); ++it) { + QString cap = (*it).toString(); + if (cap == "location") caps |= AppInfo::Location; + if (cap == "configurable") caps |= AppInfo::Configurable; + } + info.setCapabilities(caps); } - info.setCapabilities(caps); - const QJsonObject appkeys = root["appKeys"].toObject(); - for (QJsonObject::const_iterator it = appkeys.constBegin(); it != appkeys.constEnd(); ++it) { - info.addAppKey(it.key(), it.value().toInt()); + if (root.contains("appKeys")) { + const QJsonObject appkeys = root["appKeys"].toObject(); + for (auto it = appkeys.constBegin(); it != appkeys.constEnd(); ++it) { + info.addAppKey(it.key(), it.value().toInt()); + } + } + + if (root.contains("resources")) { + const QJsonObject resources = root["resources"].toObject(); + const QJsonArray media = resources["media"].toArray(); + int index = 0; + + for (auto it = media.constBegin(); it != media.constEnd(); ++it) { + const QJsonObject res = (*it).toObject(); + const QJsonValue menuIcon = res["menuIcon"]; + + bool is_menu_icon = false; + switch (menuIcon.type()) { + case QJsonValue::Bool: + is_menu_icon = menuIcon.toBool(); + break; + case QJsonValue::String: + is_menu_icon = !menuIcon.toString().isEmpty(); + break; + default: + break; + } + + if (is_menu_icon) { + QByteArray data = extractFromResourcePack(appDir.filePath("app_resources.pbpack"), index); + if (!data.isEmpty()) { + QImage icon = decodeResourceImage(data); + info.setMenuIcon(icon); + } + } + + index++; + } } info.setPath(path); @@ -143,3 +191,77 @@ void AppManager::scanApp(const QString &path) const char *type = info.isWatchface() ? "watchface" : "app"; logger()->debug() << "found installed" << type << info.shortName() << info.versionLabel() << "with uuid" << info.uuid().toString(); } + +QByteArray AppManager::extractFromResourcePack(const QString &file, int wanted_id) const +{ + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) { + logger()->warn() << "cannot open resource file" << f.fileName(); + return QByteArray(); + } + + QByteArray data = f.readAll(); + Unpacker u(data); + + int num_files = u.readLE(); + u.readLE(); // crc for entire file + u.readLE(); // timestamp + + logger()->debug() << "reading" << num_files << "resources from" << file; + + QList table; + + for (int i = 0; i < num_files; i++) { + ResourceEntry e; + e.index = u.readLE(); + e.offset = u.readLE(); + e.length = u.readLE(); + e.crc = u.readLE(); + + if (u.bad()) { + logger()->warn() << "short read on resource file"; + return QByteArray(); + } + + table.append(e); + } + + if (wanted_id >= table.size()) { + logger()->warn() << "specified resource does not exist"; + return QByteArray(); + } + + const ResourceEntry &e = table[wanted_id]; + + int offset = 12 + 256 * 16 + e.offset; + + QByteArray res = data.mid(offset, e.length); + + Stm32Crc crc; + crc.addData(res); + + if (crc.result() != e.crc) { + logger()->warn() << "CRC failure in resource" << e.index << "on file" << file; + return QByteArray(); + } + + return res; +} + +QImage AppManager::decodeResourceImage(const QByteArray &data) const +{ + Unpacker u(data); + int scanline = u.readLE(); + u.skip(sizeof(quint16) + sizeof(quint32)); + int width = u.readLE(); + int height = u.readLE(); + + QImage img(width, height, QImage::Format_MonoLSB); + const uchar *src = reinterpret_cast(&data.constData()[12]); + for (int line = 0; line < height; ++line) { + memcpy(img.scanLine(line), src, qMin(scanline, img.bytesPerLine())); + src += scanline; + } + + return img; +} diff --git a/daemon/appmanager.h b/daemon/appmanager.h index 1725c14..d5e5ba1 100644 --- a/daemon/appmanager.h +++ b/daemon/appmanager.h @@ -30,6 +30,8 @@ signals: private: void scanApp(const QString &path); + QByteArray extractFromResourcePack(const QString &file, int id) const; + QImage decodeResourceImage(const QByteArray &data) const; private: QFileSystemWatcher *_watcher; diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 25908e4..212a1d7 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -380,6 +380,11 @@ QVariantList PebbledProxy::AllApps() const m.insert("company-name", QVariant::fromValue(info.companyName())); m.insert("version-label", QVariant::fromValue(info.versionLabel())); m.insert("is-watchface", QVariant::fromValue(info.isWatchface())); + + if (!info.menuIcon().isNull()) { + m.insert("menu-icon", QVariant::fromValue(info.menuIconAsPng())); + } + l.append(QVariant::fromValue(m)); } -- cgit v1.2.3 From 24a27dcfdd6ce8f3e5a635404e6650081ebd63ca Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 14 Dec 2014 06:40:01 +0100 Subject: convert appconfig into a dialog --- app/app.pro | 4 +-- app/qml/pages/AppConfigDialog.qml | 53 +++++++++++++++++++++++++++++++++++++++ app/qml/pages/AppConfigPage.qml | 49 ------------------------------------ app/qml/pages/WatchPage.qml | 3 ++- 4 files changed, 57 insertions(+), 52 deletions(-) create mode 100644 app/qml/pages/AppConfigDialog.qml delete mode 100644 app/qml/pages/AppConfigPage.qml (limited to 'app') diff --git a/app/app.pro b/app/app.pro index 48fcf68..9cc2d09 100644 --- a/app/app.pro +++ b/app/app.pro @@ -27,5 +27,5 @@ OTHER_FILES += \ qml/images/* \ pebble.desktop \ pebble.png \ - qml/pages/AppConfigPage.qml \ - qml/pages/InstallAppDialog.qml + qml/pages/InstallAppDialog.qml \ + qml/pages/AppConfigDialog.qml diff --git a/app/qml/pages/AppConfigDialog.qml b/app/qml/pages/AppConfigDialog.qml new file mode 100644 index 0000000..65a1f5b --- /dev/null +++ b/app/qml/pages/AppConfigDialog.qml @@ -0,0 +1,53 @@ +import QtQuick 2.0 +import QtQml 2.1 +import QtWebKit 3.0 +import Sailfish.Silica 1.0 + +Dialog { + id: appConfigPage + + property alias url: webview.url + property string uuid + property string name + + SilicaWebView { + id: webview + visible: url != "" + anchors.fill: parent + + header: DialogHeader { + title: "Configuring " + name + } + + onNavigationRequested: { + console.log("appconfig navigation requested to " + request.url); + var url = request.url.toString(); + if (/^pebblejs:\/\/close/.exec(url)) { + var data = decodeURIComponent(url.substring(17)); + console.log("appconfig requesting close; data: " + data); + pebbled.setAppConfiguration(uuid, data); + appConfigPage.canAccept = true; + appConfigPage.accept(); + request.action = WebView.IgnoreRequest; + } else { + request.action = WebView.AcceptRequest; + } + } + } + + Text { + anchors.centerIn: parent + visible: url == "" + text: qsTr("No configuration settings available") + width: parent.width - 2*Theme.paddingLarge + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + font { + pixelSize: Theme.fontSizeLarge + family: Theme.fontFamilyHeading + } + color: Theme.highlightColor + } + + canAccept: false +} diff --git a/app/qml/pages/AppConfigPage.qml b/app/qml/pages/AppConfigPage.qml deleted file mode 100644 index 00eb05c..0000000 --- a/app/qml/pages/AppConfigPage.qml +++ /dev/null @@ -1,49 +0,0 @@ -import QtQuick 2.0 -import QtQml 2.1 -import QtWebKit 3.0 -import Sailfish.Silica 1.0 - -Page { - id: appConfigPage - - property alias url: webview.url - property string name - - SilicaWebView { - id: webview - visible: url != "" - anchors.fill: parent - - header: PageHeader { - title: "Configuring " + name - } - - onNavigationRequested: { - console.log("appconfig navigation requested to " + request.url); - var url = request.url.toString(); - if (/^pebblejs:\/\/close/.exec(url)) { - var data = decodeURIComponent(url.substring(17)); - console.log("appconfig requesting close; data: " + data); - pebbled.setAppConfiguration(uuid, data); - pageStack.pop(); - request.action = WebView.IgnoreRequest; - } else { - request.action = WebView.AcceptRequest; - } - } - } - - Text { - anchors.centerIn: parent - visible: url == "" - text: qsTr("No configuration settings available") - width: parent.width - 2*Theme.paddingLarge - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.Wrap - font { - pixelSize: Theme.fontSizeLarge - family: Theme.fontFamilyHeading - } - color: Theme.highlightColor - } -} diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml index 3a712ab..9096df6 100644 --- a/app/qml/pages/WatchPage.qml +++ b/app/qml/pages/WatchPage.qml @@ -109,8 +109,9 @@ Page { console.log("going to call configure on app with uuid " + uuid); var url = pebbled.configureApp(uuid); console.log("received url: " + url); - pageStack.push(Qt.resolvedUrl("AppConfigPage.qml"), { + pageStack.push(Qt.resolvedUrl("AppConfigDialog.qml"), { url: url, + uuid: uuid, name: appInfo.longName }); } -- cgit v1.2.3 From 7c48bbe17251fef1d3045ac8b5b6fe8058b4fb10 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 14 Dec 2014 17:19:46 +0100 Subject: add i18n support to UI app also 'es' translation --- app/app.pro | 10 +- app/qml/pages/WatchPage.qml | 4 +- app/translations/pebble-es.ts | 254 ++++++++++++++++++++++++++++++++++++++++++ app/translations/pebble.ts | 253 +++++++++++++++++++++++++++++++++++++++++ rpm/pebble.spec | 1 + rpm/pebble.yaml | 1 + 6 files changed, 518 insertions(+), 5 deletions(-) create mode 100644 app/translations/pebble-es.ts create mode 100644 app/translations/pebble.ts (limited to 'app') diff --git a/app/app.pro b/app/app.pro index 9cc2d09..ca03ab1 100644 --- a/app/app.pro +++ b/app/app.pro @@ -23,9 +23,13 @@ OTHER_FILES += \ qml/pages/ManagerPage.qml \ qml/pages/WatchPage.qml \ qml/pages/AboutPage.qml \ + qml/pages/InstallAppDialog.qml \ + qml/pages/AppConfigDialog.qml \ qml/pebble.qml \ qml/images/* \ + translations/*.ts \ pebble.desktop \ - pebble.png \ - qml/pages/InstallAppDialog.qml \ - qml/pages/AppConfigDialog.qml + pebble.png + +CONFIG += sailfishapp_i18n +TRANSLATIONS += translations/pebble-es.ts diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml index 9096df6..2d69306 100644 --- a/app/qml/pages/WatchPage.qml +++ b/app/qml/pages/WatchPage.qml @@ -60,7 +60,7 @@ Page { Button { - text: "Ping" + text: qsTr("Ping") width: parent.width / 2 onClicked: { pebbled.ping(66) @@ -68,7 +68,7 @@ Page { } Button { - text: "Sync Time" + text: qsTr("Sync Time") width: parent.width / 2 onClicked: { pebbled.time() diff --git a/app/translations/pebble-es.ts b/app/translations/pebble-es.ts new file mode 100644 index 0000000..7332e4c --- /dev/null +++ b/app/translations/pebble-es.ts @@ -0,0 +1,254 @@ + + + + + AboutPage + + + Version + Versión + + + + 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. + + + + + Bugs? + ¿Errores? + + + + AppConfigDialog + + + No configuration settings available + No hay opciones disponibles para configurar + + + + CoverPage + + + connected + conectado + + + + disconnected + desconectado + + + + InstallAppDialog + + + Install app + Instalar app + + + + Install + Instalar + + + + ManagerPage + + + About + Acerca de + + + + Pebble Manager + + + + + Waiting for watch... +If it can't be found please check it's available and paired in Bluetooth settings. + Buscando el reloj +Si esto tarda mucho, comprueba que el reloj esté emparejado correctamente. + + + + Service + Servicio + + + + Enabled + Habilitado + + + + Automatic startup + Inicio automático + + + + Manual startup + Inicio manual + + + + Active + Activo + + + + Running + Ejecutándose + + + + Dead + Detenido + + + + Connection + Conexión + + + + Connected + Conectado + + + + Disconnected + Desconectado + + + + Settings + Configuración + + + + Forward phone calls + Transferir llamadas + + + + Silent when connected + Modo silencio automático + + + + Sets phone profile to "silent" when Pebble is connected + Activa el modo silencio cuando se conecte un Pebble + + + + Transliterate messages + Transliterar mensajes + + + + Messages are transliterated to ASCII before sending to Pebble + Codifica los mensajes entrates a ASCII antes de enviarlos a Pebble + + + + Notifications + Notificaciones + + + + Messaging + Mensajería + + + + SMS and IM + SMS y chat + + + + Missed call + Llamadas perdidas + + + + Emails + Correos electrónicos + + + + Mitakuuluu + + + + + Twitter + Twitter + + + + Facebook + Facebook + + + + Other notifications + Resto de notificaciones + + + + All notifications + Todas las notificaciones + + + + WatchPage + + + Ping + Ping + + + + Sync Time + Ajustar hora + + + + Installed applications + Aplicaciones instaladas + + + + Uninstalling + Desinstalando + + + + (empty slot) + (hueco libre) + + + + (slot in use by unknown app) + (hueco en uso) + + + + Install app... + Instalar app... + + + + Configure... + Configurar... + + + + Uninstall + Desinstalar + + + diff --git a/app/translations/pebble.ts b/app/translations/pebble.ts new file mode 100644 index 0000000..3cc8b6d --- /dev/null +++ b/app/translations/pebble.ts @@ -0,0 +1,253 @@ + + + + + AboutPage + + + Version + + + + + 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. + + + + + Bugs? + + + + + AppConfigDialog + + + No configuration settings available + + + + + CoverPage + + + connected + + + + + disconnected + + + + + InstallAppDialog + + + Install app + + + + + Install + + + + + ManagerPage + + + About + + + + + Pebble Manager + + + + + Waiting for watch... +If it can't be found please check it's available and paired in Bluetooth settings. + + + + + Service + + + + + Enabled + + + + + Automatic startup + + + + + Manual startup + + + + + Active + + + + + Running + + + + + Dead + + + + + Connection + + + + + Connected + + + + + Disconnected + + + + + Settings + + + + + Forward phone calls + + + + + Silent when connected + + + + + Sets phone profile to "silent" when Pebble is connected + + + + + Transliterate messages + + + + + Messages are transliterated to ASCII before sending to Pebble + + + + + Notifications + + + + + Messaging + + + + + SMS and IM + + + + + Missed call + + + + + Emails + + + + + Mitakuuluu + + + + + Twitter + + + + + Facebook + + + + + Other notifications + + + + + All notifications + + + + + WatchPage + + + Ping + + + + + Sync Time + + + + + Installed applications + + + + + Uninstalling + + + + + (empty slot) + + + + + (slot in use by unknown app) + + + + + Install app... + + + + + Configure... + + + + + Uninstall + + + + diff --git a/rpm/pebble.spec b/rpm/pebble.spec index 8779710..9fb9575 100644 --- a/rpm/pebble.spec +++ b/rpm/pebble.spec @@ -81,6 +81,7 @@ systemctl --user daemon-reload %{_bindir} %{_datadir}/%{name}/qml %{_datadir}/%{name}/js +%{_datadir}/%{name}/translations %{_datadir}/applications/%{name}.desktop %{_datadir}/icons/hicolor/86x86/apps/%{name}.png %{_libdir}/systemd/user/%{name}d.service diff --git a/rpm/pebble.yaml b/rpm/pebble.yaml index 80a5d20..c788c27 100644 --- a/rpm/pebble.yaml +++ b/rpm/pebble.yaml @@ -32,6 +32,7 @@ Files: - '%{_bindir}' - '%{_datadir}/%{name}/qml' - '%{_datadir}/%{name}/js' +- '%{_datadir}/%{name}/translations' - '%{_datadir}/applications/%{name}.desktop' - '%{_datadir}/icons/hicolor/86x86/apps/%{name}.png' - '%{_libdir}/systemd/user/%{name}d.service' -- cgit v1.2.3 From 6b1ffd680201314171d3e23c7abbec83f32f1dad Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 14 Dec 2014 17:48:24 +0100 Subject: add progress bar and scroll indicator to appconfig dialog --- app/qml/pages/AppConfigDialog.qml | 16 ++++++++++++++++ app/translations/pebble-es.ts | 2 +- app/translations/pebble.ts | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/qml/pages/AppConfigDialog.qml b/app/qml/pages/AppConfigDialog.qml index 65a1f5b..92f188f 100644 --- a/app/qml/pages/AppConfigDialog.qml +++ b/app/qml/pages/AppConfigDialog.qml @@ -33,6 +33,22 @@ Dialog { request.action = WebView.AcceptRequest; } } + + VerticalScrollDecorator { flickable: webview } + } + + ProgressBar { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + + visible: webview.visible && webview.loading + minimumValue: 0 + maximumValue: 100 + indeterminate: webview.loadProgress === 0 + value: webview.loadProgress } Text { diff --git a/app/translations/pebble-es.ts b/app/translations/pebble-es.ts index 7332e4c..3a8da7d 100644 --- a/app/translations/pebble-es.ts +++ b/app/translations/pebble-es.ts @@ -22,7 +22,7 @@ AppConfigDialog - + No configuration settings available No hay opciones disponibles para configurar diff --git a/app/translations/pebble.ts b/app/translations/pebble.ts index 3cc8b6d..0c61f38 100644 --- a/app/translations/pebble.ts +++ b/app/translations/pebble.ts @@ -22,7 +22,7 @@ AppConfigDialog - + No configuration settings available -- cgit v1.2.3 From e1d94fc21ecbce16815810c4f274f349b49e82a3 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 21 Dec 2014 21:03:18 +0100 Subject: add itemselector to the appconfig webview --- app/app.pro | 1 + app/qml/pages/AppConfigDialog.qml | 18 ++++++++++++++- app/qml/pages/WebItemSelDialog.qml | 45 ++++++++++++++++++++++++++++++++++++++ app/translations/pebble-es.ts | 2 +- app/translations/pebble.ts | 2 +- 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 app/qml/pages/WebItemSelDialog.qml (limited to 'app') diff --git a/app/app.pro b/app/app.pro index ca03ab1..97c6232 100644 --- a/app/app.pro +++ b/app/app.pro @@ -25,6 +25,7 @@ OTHER_FILES += \ qml/pages/AboutPage.qml \ qml/pages/InstallAppDialog.qml \ qml/pages/AppConfigDialog.qml \ + qml/pages/WebItemSelDialog.qml \ qml/pebble.qml \ qml/images/* \ translations/*.ts \ diff --git a/app/qml/pages/AppConfigDialog.qml b/app/qml/pages/AppConfigDialog.qml index 92f188f..304bced 100644 --- a/app/qml/pages/AppConfigDialog.qml +++ b/app/qml/pages/AppConfigDialog.qml @@ -19,6 +19,8 @@ Dialog { title: "Configuring " + name } + VerticalScrollDecorator { flickable: webview } + onNavigationRequested: { console.log("appconfig navigation requested to " + request.url); var url = request.url.toString(); @@ -34,7 +36,21 @@ Dialog { } } - VerticalScrollDecorator { flickable: webview } + experimental.itemSelector: Component { + Item { + Component.onCompleted: { + var dialog = pageStack.push(Qt.resolvedUrl("WebItemSelDialog.qml"), { + model: model.items + }); + dialog.onRejected.connect(function() { + model.reject(); + }); + dialog.onAccepted.connect(function() { + model.accept(dialog.selectedIndex); + }); + } + } + } } ProgressBar { diff --git a/app/qml/pages/WebItemSelDialog.qml b/app/qml/pages/WebItemSelDialog.qml new file mode 100644 index 0000000..f8c49f2 --- /dev/null +++ b/app/qml/pages/WebItemSelDialog.qml @@ -0,0 +1,45 @@ +import QtQuick 2.0 +import QtQml 2.1 +import Sailfish.Silica 1.0 + +Dialog { + id: itemSelDialog + property alias model: listView.model + property int selectedIndex: -1 + + SilicaListView { + id: listView + anchors.fill: parent + + VerticalScrollDecorator { flickable: webview } + + header: PageHeader { + } + + delegate: ListItem { + id: itemDelegate + contentHeight: Theme.itemSizeSmall + + Label { + anchors { + left: parent.left + leftMargin: Theme.paddingMedium + right: parent.right + rightMargin: Theme.paddingMedium + verticalCenter: parent.verticalCenter + } + text: model.text + color: model.enabled ? + (itemDelegate.highlighted ? Theme.highlightColor : Theme.primaryColor) + : Theme.secondaryColor + truncationMode: TruncationMode.Fade + } + + enabled: model.enabled + onClicked: { + selectedIndex = model.index; + accept(); + } + } + } +} diff --git a/app/translations/pebble-es.ts b/app/translations/pebble-es.ts index 3a8da7d..18b1ec2 100644 --- a/app/translations/pebble-es.ts +++ b/app/translations/pebble-es.ts @@ -22,7 +22,7 @@ AppConfigDialog - + No configuration settings available No hay opciones disponibles para configurar diff --git a/app/translations/pebble.ts b/app/translations/pebble.ts index 0c61f38..222645c 100644 --- a/app/translations/pebble.ts +++ b/app/translations/pebble.ts @@ -22,7 +22,7 @@ AppConfigDialog - + No configuration settings available -- cgit v1.2.3 From ed43269c2adebed7baf4e2452f998d7e60c797e6 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 21 Dec 2014 21:04:35 +0100 Subject: fix typos --- app/qml/pages/InstallAppDialog.qml | 2 +- app/qml/pages/WatchPage.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/qml/pages/InstallAppDialog.qml b/app/qml/pages/InstallAppDialog.qml index 79283a6..fa96c28 100644 --- a/app/qml/pages/InstallAppDialog.qml +++ b/app/qml/pages/InstallAppDialog.qml @@ -52,7 +52,7 @@ Dialog { left: appIcon.right leftMargin: Theme.paddingMedium right: parent.right - rightMargin: Theme.paddiumLarge + rightMargin: Theme.paddingLarge verticalCenter: parent.verticalCenter } text: modelData.longName diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml index 2d69306..43c2b99 100644 --- a/app/qml/pages/WatchPage.qml +++ b/app/qml/pages/WatchPage.qml @@ -186,7 +186,7 @@ Page { left: slotIcon.right leftMargin: Theme.paddingMedium right: parent.right - rightMargin: Theme.paddiumLarge + rightMargin: Theme.paddingLarge verticalCenter: parent.verticalCenter } text: isEmptySlot ? qsTr("(empty slot)") : (isKnownApp ? appInfo.longName : qsTr("(slot in use by unknown app)")) -- cgit v1.2.3 From e6ec758b364fcaf9fda35e56740c3fcd7e8fe25e Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 21 Dec 2014 22:36:14 +0100 Subject: use overridePageStackNavigation to ensure accept() works --- app/qml/pages/AppConfigDialog.qml | 2 ++ app/translations/pebble-es.ts | 2 +- app/translations/pebble.ts | 2 +- rpm/pebble.spec | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/qml/pages/AppConfigDialog.qml b/app/qml/pages/AppConfigDialog.qml index 304bced..1562985 100644 --- a/app/qml/pages/AppConfigDialog.qml +++ b/app/qml/pages/AppConfigDialog.qml @@ -21,6 +21,8 @@ Dialog { VerticalScrollDecorator { flickable: webview } + overridePageStackNavigation: true + onNavigationRequested: { console.log("appconfig navigation requested to " + request.url); var url = request.url.toString(); diff --git a/app/translations/pebble-es.ts b/app/translations/pebble-es.ts index 18b1ec2..0316ecb 100644 --- a/app/translations/pebble-es.ts +++ b/app/translations/pebble-es.ts @@ -22,7 +22,7 @@ AppConfigDialog - + No configuration settings available No hay opciones disponibles para configurar diff --git a/app/translations/pebble.ts b/app/translations/pebble.ts index 222645c..7a78d9d 100644 --- a/app/translations/pebble.ts +++ b/app/translations/pebble.ts @@ -22,7 +22,7 @@ AppConfigDialog - + No configuration settings available diff --git a/rpm/pebble.spec b/rpm/pebble.spec index 9fb9575..02cbe6c 100644 --- a/rpm/pebble.spec +++ b/rpm/pebble.spec @@ -13,7 +13,7 @@ Name: pebble %{!?qtc_make:%define qtc_make make} %{?qtc_builddir:%define _builddir %qtc_builddir} Summary: Support for Pebble watch in SailfishOS -Version: 0.12.1b +Version: 0.12.1c Release: 1 Group: Qt/Qt License: GPL3 -- cgit v1.2.3