From 67e85358bc789e93f3f17527d9f721d6a9e5f94d Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 29 Nov 2014 21:08:56 +0100 Subject: introduce the appmanager --- daemon/manager.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'daemon/manager.h') diff --git a/daemon/manager.h b/daemon/manager.h index 9de5667..5946bf0 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -5,6 +5,7 @@ #include "dbusconnector.h" #include "voicecallmanager.h" #include "notificationmanager.h" +#include "appmanager.h" #include "watchcommands.h" #include "settings.h" @@ -38,6 +39,7 @@ class Manager : DBusConnector *dbus; VoiceCallManager *voice; NotificationManager *notifications; + AppManager *apps; WatchCommands *commands; @@ -55,7 +57,7 @@ class Manager : QScopedPointer transliterator; public: - explicit Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallManager *voice, NotificationManager *notifications, Settings *settings); + explicit Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallManager *voice, NotificationManager *notifications, AppManager *apps, Settings *settings); Q_INVOKABLE QString findPersonByNumber(QString number); Q_INVOKABLE QString getCurrentProfile(); -- cgit v1.2.3 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 +- daemon/appmanager.cpp | 109 +++++++++++++++++++++-- daemon/appmanager.h | 31 ++++++- daemon/daemon.cpp | 10 +-- daemon/daemon.pro | 9 +- daemon/dbusadaptor.cpp | 5 +- daemon/dbusadaptor.h | 2 + daemon/manager.cpp | 66 ++++++++++---- daemon/manager.h | 21 +++-- daemon/musicmanager.cpp | 97 ++++++++++++++++++++ daemon/musicmanager.h | 25 ++++++ daemon/unpacker.h | 87 ++++++++++++++++++ daemon/voicecallmanager.cpp | 7 ++ daemon/voicecallmanager.h | 1 + daemon/watchcommands.cpp | 122 ------------------------- daemon/watchcommands.h | 31 ------- daemon/watchconnector.cpp | 211 ++++++++++++++++++++++++++++++++++++-------- daemon/watchconnector.h | 57 +++++++----- log4qt-debug.conf | 4 +- log4qt-release.conf | 4 +- 20 files changed, 637 insertions(+), 264 deletions(-) create mode 100644 daemon/musicmanager.cpp create mode 100644 daemon/musicmanager.h create mode 100644 daemon/unpacker.h delete mode 100644 daemon/watchcommands.cpp delete mode 100644 daemon/watchcommands.h (limited to 'daemon/manager.h') 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) diff --git a/daemon/appmanager.cpp b/daemon/appmanager.cpp index b4a3d68..34af3af 100644 --- a/daemon/appmanager.cpp +++ b/daemon/appmanager.cpp @@ -1,15 +1,112 @@ #include -#include +#include +#include +#include #include "appmanager.h" AppManager::AppManager(QObject *parent) - : QObject(parent) + : QObject(parent), + _watcher(new QFileSystemWatcher(this)) { + connect(_watcher, &QFileSystemWatcher::directoryChanged, + this, &AppManager::rescan); + + QDir dataDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); + if (!dataDir.exists("apps")) { + if (!dataDir.mkdir("apps")) { + logger()->warn() << "could not create dir" << dataDir.absoluteFilePath("apps"); + } + } + logger()->debug() << "install apps in" << dataDir.absoluteFilePath("apps"); + + rescan(); +} + +QStringList AppManager::appPaths() const +{ + return QStandardPaths::locateAll(QStandardPaths::DataLocation, + QLatin1String("apps"), + QStandardPaths::LocateDirectory); +} + +void AppManager::rescan() +{ + QStringList watchedDirs = _watcher->directories(); + if (!watchedDirs.isEmpty()) _watcher->removePaths(watchedDirs); + QStringList watchedFiles = _watcher->files(); + if (!watchedFiles.isEmpty()) _watcher->removePaths(watchedFiles); + _apps.clear(); + _names.clear(); + + Q_FOREACH(const QString &path, appPaths()) { + QDir dir(path); + _watcher->addPath(dir.absolutePath()); + logger()->debug() << "scanning dir" << dir.absolutePath(); + QStringList entries = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Executable); + logger()->debug() << "scanning dir results" << entries; + Q_FOREACH(const QString &path, entries) { + QString appPath = dir.absoluteFilePath(path); + _watcher->addPath(appPath); + if (dir.exists(path + "/appinfo.json")) { + _watcher->addPath(appPath + "/appinfo.json"); + scanApp(appPath); + } + } + } + + logger()->debug() << "now watching" << _watcher->directories() << _watcher->files(); } -QString AppManager::getAppDir(const QUuid& uuid) const +void AppManager::scanApp(const QString &path) { - return QStandardPaths::locate(QStandardPaths::DataLocation, - QString("apps/%1").arg(uuid.toString()), - QStandardPaths::LocateDirectory); + logger()->debug() << "scanning app" << path; + QDir appDir(path); + if (!appDir.isReadable()) { + logger()->warn() << "app" << appDir.absolutePath() << "is not readable"; + return; + } + + QFile appInfoFile(path + "/appinfo.json"); + if (!appInfoFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + logger()->warn() << "cannot open app info file" << appInfoFile.fileName() << ":" + << appInfoFile.errorString(); + return; + } + + QJsonParseError parseError; + QJsonDocument doc = QJsonDocument::fromJson(appInfoFile.readAll(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + logger()->warn() << "cannot parse app info file" << appInfoFile.fileName() << ":" + << parseError.errorString(); + return; + } + + const QJsonObject root = doc.object(); + AppInfo info; + info.uuid = QUuid(root["uuid"].toString()); + info.shortName = root["shortName"].toString(); + info.longName = root["longName"].toString(); + info.company = root["companyName"].toString(); + info.versionCode = root["versionCode"].toInt(); + info.versionLabel = root["versionLabel"].toString(); + + const QJsonObject watchapp = root["watchapp"].toObject(); + info.isWatchface = watchapp["watchface"].toBool(); + info.isJSKit = appDir.exists("pebble-js-app.js"); + + const QJsonObject appkeys = root["appKeys"].toObject(); + for (QJsonObject::const_iterator it = appkeys.constBegin(); it != appkeys.constEnd(); ++it) { + info.appKeys.insert(it.key(), it.value().toInt()); + } + + if (info.uuid.isNull() || info.shortName.isEmpty()) { + logger()->warn() << "invalid or empty uuid/name in" << appInfoFile.fileName(); + return; + } + + _apps.insert(info.uuid, info); + _names.insert(info.shortName, info.uuid); + + const char *type = info.isWatchface ? "watchface" : "app"; + logger()->debug() << "found installed" << type << info.shortName << info.versionLabel << "with uuid" << info.uuid.toString(); } diff --git a/daemon/appmanager.h b/daemon/appmanager.h index 59d0bd7..5e150ab 100644 --- a/daemon/appmanager.h +++ b/daemon/appmanager.h @@ -2,19 +2,46 @@ #define APPMANAGER_H #include +#include +#include +#include +#include class AppManager : public QObject { Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER public: explicit AppManager(QObject *parent = 0); + struct AppInfo { + QUuid uuid; + QString shortName; + QString longName; + QString company; + int versionCode; + QString versionLabel; + bool isWatchface; + bool isJSKit; + QHash appKeys; + QString path; + }; + + QStringList appPaths() const; + bool installPebbleApp(const QString &pbwFile); - QList allInstalledApps() const; +public slots: + void rescan(); + +private: + void scanApp(const QString &path); - QString getAppDir(const QUuid &uuid) const; +private: + QFileSystemWatcher *_watcher; + QHash _apps; + QHash _names; }; #endif // APPMANAGER_H diff --git a/daemon/daemon.cpp b/daemon/daemon.cpp index 80ae667..c9456c6 100644 --- a/daemon/daemon.cpp +++ b/daemon/daemon.cpp @@ -78,17 +78,11 @@ int main(int argc, char *argv[]) Log4Qt::Logger::logger(QLatin1String("Main Logger"))->info() << argv[0] << APP_VERSION; Settings settings; - watch::WatchConnector watch; - DBusConnector dbus; - VoiceCallManager voice(&settings); - NotificationManager notifications(&settings); - AppManager apps(&settings); - Manager manager(&watch, &dbus, &voice, ¬ifications, &apps, &settings); + Manager manager(&settings); + Q_UNUSED(manager); signal(SIGINT, signalhandler); signal(SIGTERM, signalhandler); - QObject::connect(&app, SIGNAL(aboutToQuit()), &watch, SLOT(endPhoneCall())); - QObject::connect(&app, SIGNAL(aboutToQuit()), &watch, SLOT(disconnect())); return app.exec(); } diff --git a/daemon/daemon.pro b/daemon/daemon.pro index 1799d18..768b50e 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -21,8 +21,8 @@ SOURCES += \ watchconnector.cpp \ dbusconnector.cpp \ dbusadaptor.cpp \ - watchcommands.cpp \ - appmanager.cpp + appmanager.cpp \ + musicmanager.cpp HEADERS += \ manager.h \ @@ -32,9 +32,10 @@ HEADERS += \ watchconnector.h \ dbusconnector.h \ dbusadaptor.h \ - watchcommands.h \ settings.h \ - appmanager.h + appmanager.h \ + musicmanager.h \ + unpacker.h OTHER_FILES += \ org.pebbled.xml \ diff --git a/daemon/dbusadaptor.cpp b/daemon/dbusadaptor.cpp index 3332551..7bbf623 100644 --- a/daemon/dbusadaptor.cpp +++ b/daemon/dbusadaptor.cpp @@ -75,10 +75,13 @@ void PebbledAdaptor::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"); +} diff --git a/daemon/dbusadaptor.h b/daemon/dbusadaptor.h index 715a41b..54a0963 100644 --- a/daemon/dbusadaptor.h +++ b/daemon/dbusadaptor.h @@ -46,6 +46,7 @@ class PebbledAdaptor: public QDBusAbstractAdaptor " \n" " \n" " \n" +" \n" " \n" "") public: @@ -70,6 +71,7 @@ public Q_SLOTS: // METHODS void ping(int val); void time(); void reconnect(); + void test(); Q_SIGNALS: // SIGNALS void connectedChanged(); void pebbleChanged(); diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 7761864..8a8acf4 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -5,9 +5,15 @@ #include #include -Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallManager *voice, NotificationManager *notifications, AppManager *apps, Settings *settings) : - QObject(0), watch(watch), dbus(dbus), voice(voice), notifications(notifications), apps(apps), - commands(new WatchCommands(watch, this)), settings(settings), notification(MNotification::DeviceEvent) +Manager::Manager(Settings *settings, QObject *parent) : + QObject(parent), settings(settings), + watch(new WatchConnector(this)), + dbus(new DBusConnector(this)), + voice(new VoiceCallManager(settings, this)), + notifications(new NotificationManager(settings, this)), + music(new MusicManager(watch, this)), + apps(new AppManager(this)), + notification(MNotification::DeviceEvent) { connect(settings, SIGNAL(valueChanged(QString)), SLOT(onSettingChanged(const QString&))); connect(settings, SIGNAL(valuesChanged()), SLOT(onSettingsChanged())); @@ -22,6 +28,24 @@ Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallMan numberFilter.setMatchFlags(QContactFilter::MatchPhoneNumber); connect(watch, SIGNAL(connectedChanged()), SLOT(onConnectedChanged())); + watch->setEndpointHandler(WatchConnector::watchPHONE_VERSION, + [this](const QByteArray& data) { + Q_UNUSED(data); + watch->sendPhoneVersion(); + return true; + }); + watch->setEndpointHandler(WatchConnector::watchPHONE_CONTROL, + [this](const QByteArray& data) { + if (data.at(0) == WatchConnector::callHANGUP) { + voice->hangupAll(); + } + return true; + }); + watch->setEndpointHandler(WatchConnector::watchDATA_LOGGING, + [this](const QByteArray& data) { + //logger()->debug() << data.toHex(); + return true; + }); connect(voice, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged())); connect(voice, SIGNAL(error(const QString &)), SLOT(onVoiceError(const QString &))); @@ -32,13 +56,10 @@ Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallMan 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 &))); - connect(watch, SIGNAL(messageDecoded(uint,QByteArray)), commands, SLOT(processMessage(uint,QByteArray))); - connect(commands, SIGNAL(hangup()), SLOT(hangupAll())); - PebbledProxy *proxy = new PebbledProxy(this); PebbledAdaptor *adaptor = new PebbledAdaptor(proxy); QDBusConnection session = QDBusConnection::sessionBus(); - session.registerObject("/", proxy); + session.registerObject("/org/pebbled", proxy); session.registerService("org.pebbled"); connect(dbus, SIGNAL(pebbleChanged()), adaptor, SIGNAL(pebbleChanged())); connect(watch, SIGNAL(connectedChanged()), adaptor, SIGNAL(connectedChanged())); @@ -52,7 +73,7 @@ Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallMan "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onMprisPropertiesChanged(QString,QMap,QStringList))); - connect(this, SIGNAL(mprisMetadataChanged(QVariantMap)), commands, SLOT(onMprisMetadataChanged(QVariantMap))); + connect(this, SIGNAL(mprisMetadataChanged(QVariantMap)), music, SLOT(onMprisMetadataChanged(QVariantMap))); // Set BT icon for notification notification.setImage("icon-system-bluetooth-device"); @@ -62,7 +83,10 @@ Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallMan connect(dbus, SIGNAL(pebbleChanged()), SLOT(onPebbleChanged())); dbus->findPebble(); } +} +Manager::~Manager() +{ } void Manager::onSettingChanged(const QString &key) @@ -247,13 +271,6 @@ void Manager::onEmailNotify(const QString &sender, const QString &data,const QSt watch->sendEmailNotification(sender, data, subject); } -void Manager::hangupAll() -{ - foreach (VoiceCallHandler* handler, voice->voiceCalls()) { - handler->hangup(); - } -} - void Manager::onMprisPropertiesChanged(QString interface, QMap changed, QStringList invalidated) { logger()->debug() << interface << changed << invalidated; @@ -369,3 +386,22 @@ void Manager::transliterateMessage(const QString &text) logger()->debug() << "String after transliteration:" << text; } } + +bool Manager::uploadApp(const QUuid &uuid, int slot) +{ + // TODO + return false; +} + +void Manager::test() +{ + logger()->debug() << "Starting test"; + + watch->getAppbankStatus([this](const QString &s) { + logger()->debug() << "Callback invoked" << s; + }); + + watch->getAppbankUuids([this](const QList &uuids) { + logger()->debug() << "Callback invoked. UUIDs:" << uuids.size(); + }); +} diff --git a/daemon/manager.h b/daemon/manager.h index 5946bf0..b210700 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -5,8 +5,8 @@ #include "dbusconnector.h" #include "voicecallmanager.h" #include "notificationmanager.h" +#include "musicmanager.h" #include "appmanager.h" -#include "watchcommands.h" #include "settings.h" #include @@ -35,16 +35,15 @@ class Manager : QBluetoothLocalDevice btDevice; - watch::WatchConnector *watch; + Settings *settings; + + WatchConnector *watch; DBusConnector *dbus; VoiceCallManager *voice; NotificationManager *notifications; + MusicManager *music; AppManager *apps; - WatchCommands *commands; - - Settings *settings; - MNotification notification; QContactManager *contacts; @@ -57,7 +56,8 @@ class Manager : QScopedPointer transliterator; public: - explicit Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallManager *voice, NotificationManager *notifications, AppManager *apps, Settings *settings); + explicit Manager(Settings *settings, QObject *parent = 0); + ~Manager(); Q_INVOKABLE QString findPersonByNumber(QString number); Q_INVOKABLE QString getCurrentProfile(); @@ -65,6 +65,8 @@ public: QVariantMap mprisMetadata; QVariantMap getMprisMetadata() { return mprisMetadata; } + Q_INVOKABLE bool uploadApp(const QUuid &uuid, int slot = -1); + protected: void transliterateMessage(const QString &text); @@ -72,10 +74,10 @@ signals: void mprisMetadataChanged(QVariantMap); public slots: - void hangupAll(); void applyProfile(); -protected slots: +private slots: + void test(); void onSettingChanged(const QString &key); void onSettingsChanged(); void onPebbleChanged(); @@ -114,6 +116,7 @@ public slots: 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(); } }; diff --git a/daemon/musicmanager.cpp b/daemon/musicmanager.cpp new file mode 100644 index 0000000..abea715 --- /dev/null +++ b/daemon/musicmanager.cpp @@ -0,0 +1,97 @@ +#include +#include "musicmanager.h" + +MusicManager::MusicManager(WatchConnector *watch, QObject *parent) + : QObject(parent), watch(watch) +{ + watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) { + musicControl(WatchConnector::MusicControl(data.at(0))); + return true; + }); +} + +void MusicManager::onMprisMetadataChanged(QVariantMap metadata) +{ + QString track = metadata.value("xesam:title").toString(); + QString album = metadata.value("xesam:album").toString(); + QString artist = metadata.value("xesam:artist").toString(); + logger()->debug() << __FUNCTION__ << track << album << artist; + watch->sendMusicNowPlaying(track, album, artist); +} + +void MusicManager::musicControl(WatchConnector::MusicControl operation) +{ + logger()->debug() << "Operation:" << operation; + + QString mpris = parent()->property("mpris").toString(); + if (mpris.isEmpty()) { + logger()->debug() << "No mpris interface active"; + return; + } + + QString method; + + switch(operation) { + case WatchConnector::musicPLAY_PAUSE: + method = "PlayPause"; + break; + case WatchConnector::musicPAUSE: + method = "Pause"; + break; + case WatchConnector::musicPLAY: + method = "Play"; + break; + case WatchConnector::musicNEXT: + method = "Next"; + break; + case WatchConnector::musicPREVIOUS: + method = "Previous"; + break; + case WatchConnector::musicVOLUME_UP: + case WatchConnector::musicVOLUME_DOWN: { + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusReply VolumeReply = bus.call( + QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") + << "org.mpris.MediaPlayer2.Player" << "Volume"); + if (VolumeReply.isValid()) { + double volume = VolumeReply.value().variant().toDouble(); + if (operation == WatchConnector::musicVOLUME_UP) { + volume += 0.1; + } + else { + volume -= 0.1; + } + logger()->debug() << "Setting volume" << volume; + QDBusError err = QDBusConnection::sessionBus().call( + QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set") + << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume))); + if (err.isValid()) { + logger()->error() << err.message(); + } + } else { + logger()->error() << VolumeReply.error().message(); + } + } + return; + case WatchConnector::musicGET_NOW_PLAYING: + onMprisMetadataChanged(parent()->property("mprisMetadata").toMap()); + return; + + case WatchConnector::musicSEND_NOW_PLAYING: + logger()->warn() << "Operation" << operation << "not supported"; + return; + } + + if (method.isEmpty()) { + logger()->error() << "Requested unsupported operation" << operation; + return; + } + + logger()->debug() << operation << "->" << method; + + QDBusError err = QDBusConnection::sessionBus().call( + QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method)); + if (err.isValid()) { + logger()->error() << err.message(); + } +} diff --git a/daemon/musicmanager.h b/daemon/musicmanager.h new file mode 100644 index 0000000..ca86ce3 --- /dev/null +++ b/daemon/musicmanager.h @@ -0,0 +1,25 @@ +#ifndef MUSICMANAGER_H +#define MUSICMANAGER_H + +#include +#include "watchconnector.h" + +class MusicManager : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + +public: + explicit MusicManager(WatchConnector *watch, QObject *parent = 0); + +private: + void musicControl(WatchConnector::MusicControl operation); + +private slots: + void onMprisMetadataChanged(QVariantMap metadata); + +private: + WatchConnector *watch; +}; + +#endif // MUSICMANAGER_H diff --git a/daemon/unpacker.h b/daemon/unpacker.h new file mode 100644 index 0000000..94908cb --- /dev/null +++ b/daemon/unpacker.h @@ -0,0 +1,87 @@ +#ifndef UNPACKER_H +#define UNPACKER_H + +#include +#include +#include +#include + +class Unpacker +{ +public: + Unpacker(const QByteArray &data); + + template + T read(); + + QString readFixedString(int n); + + QUuid readUuid(); + + void skip(int n); + + bool bad() const; + +private: + const uchar * p(); + bool checkBad(int n = 0); + + const QByteArray &_buf; + int _offset; + bool _bad; +}; + +inline Unpacker::Unpacker(const QByteArray &data) + : _buf(data), _offset(0), _bad(false) +{ +} + +template +inline T Unpacker::read() +{ + if (checkBad(sizeof(T))) return 0; + const uchar *u = p(); + _offset += sizeof(T); + return qFromBigEndian(u); +} + +inline QString Unpacker::readFixedString(int n) +{ + if (checkBad(n)) return QString(); + const char *u = &_buf.constData()[_offset]; + _offset += n; + return QString::fromUtf8(u, strnlen(u, n)); +} + +inline QUuid Unpacker::readUuid() +{ + if (checkBad(16)) return QString(); + _offset += 16; + return QUuid::fromRfc4122(_buf.mid(_offset - 16, 16)); +} + +inline void Unpacker::skip(int n) +{ + _offset += n; + checkBad(); +} + +inline bool Unpacker::bad() const +{ + return _bad; +} + +inline const uchar * Unpacker::p() +{ + return reinterpret_cast(&_buf.constData()[_offset]); +} + +inline bool Unpacker::checkBad(int n) +{ + if (_offset + n > _buf.size()) { + _bad = true; + } + return _bad; +} + +#endif // UNPACKER_H diff --git a/daemon/voicecallmanager.cpp b/daemon/voicecallmanager.cpp index ac03b51..9fd4339 100644 --- a/daemon/voicecallmanager.cpp +++ b/daemon/voicecallmanager.cpp @@ -148,6 +148,13 @@ void VoiceCallManager::dial(const QString &provider, const QString &msisdn) QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingCallFinished(QDBusPendingCallWatcher*))); } +void VoiceCallManager::hangupAll() +{ + foreach (VoiceCallHandler* handler, voiceCalls()) { + handler->hangup(); + } +} + void VoiceCallManager::silenceRingtone() { Q_D(const VoiceCallManager); diff --git a/daemon/voicecallmanager.h b/daemon/voicecallmanager.h index b7241ef..5c21269 100644 --- a/daemon/voicecallmanager.h +++ b/daemon/voicecallmanager.h @@ -80,6 +80,7 @@ Q_SIGNALS: public Q_SLOTS: void dial(const QString &providerId, const QString &msisdn); + void hangupAll(); void silenceRingtone(); diff --git a/daemon/watchcommands.cpp b/daemon/watchcommands.cpp deleted file mode 100644 index b16efb5..0000000 --- a/daemon/watchcommands.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "watchcommands.h" - -#include -#include -#include - -using namespace watch; - -WatchCommands::WatchCommands(WatchConnector *watch, QObject *parent) : - QObject(parent), watch(watch) -{} - -void WatchCommands::processMessage(uint endpoint, QByteArray data) -{ - logger()->debug() << __FUNCTION__ << endpoint << "/" << data.toHex() << data.length(); - switch (endpoint) { - case WatchConnector::watchPHONE_VERSION: - watch->sendPhoneVersion(); - break; - case WatchConnector::watchPHONE_CONTROL: - if (data.at(0) == WatchConnector::callHANGUP) { - emit hangup(); - } - break; - case WatchConnector::watchMUSIC_CONTROL: - musicControl(WatchConnector::MusicControl(data.at(0))); - break; - case WatchConnector::watchSYSTEM_MESSAGE: - logger()->info() << "Got SYSTEM_MESSAGE" << WatchConnector::SystemMessage(data.at(0)); - // TODO: handle systemBLUETOOTH_START_DISCOVERABLE/systemBLUETOOTH_END_DISCOVERABLE - break; - - default: - logger()->info() << __FUNCTION__ << "endpoint" << endpoint << "not supported yet"; - } -} - -void WatchCommands::onMprisMetadataChanged(QVariantMap metadata) -{ - QString track = metadata.value("xesam:title").toString(); - QString album = metadata.value("xesam:album").toString(); - QString artist = metadata.value("xesam:artist").toString(); - logger()->debug() << __FUNCTION__ << track << album << artist; - watch->sendMusicNowPlaying(track, album, artist); -} - -void WatchCommands::musicControl(WatchConnector::MusicControl operation) -{ - logger()->debug() << "Operation:" << operation; - - QString mpris = parent()->property("mpris").toString(); - if (mpris.isEmpty()) { - logger()->debug() << "No mpris interface active"; - return; - } - - QString method; - - switch(operation) { - case WatchConnector::musicPLAY_PAUSE: - method = "PlayPause"; - break; - case WatchConnector::musicPAUSE: - method = "Pause"; - break; - case WatchConnector::musicPLAY: - method = "Play"; - break; - case WatchConnector::musicNEXT: - method = "Next"; - break; - case WatchConnector::musicPREVIOUS: - method = "Previous"; - break; - case WatchConnector::musicVOLUME_UP: - case WatchConnector::musicVOLUME_DOWN: { - QDBusReply VolumeReply = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") - << "org.mpris.MediaPlayer2.Player" << "Volume"); - if (VolumeReply.isValid()) { - double volume = VolumeReply.value().variant().toDouble(); - if (operation == WatchConnector::musicVOLUME_UP) { - volume += 0.1; - } - else { - volume -= 0.1; - } - logger()->debug() << "Setting volume" << volume; - QDBusError err = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set") - << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume))); - if (err.isValid()) { - logger()->error() << err.message(); - } - } - else { - logger()->error() << VolumeReply.error().message(); - } - } - return; - case WatchConnector::musicGET_NOW_PLAYING: - onMprisMetadataChanged(parent()->property("mprisMetadata").toMap()); - return; - - case WatchConnector::musicSEND_NOW_PLAYING: - logger()->warn() << "Operation" << operation << "not supported"; - return; - } - - if (method.isEmpty()) { - logger()->error() << "Requested unsupported operation" << operation; - return; - } - - logger()->debug() << operation << "->" << method; - - QDBusError err = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method)); - if (err.isValid()) { - logger()->error() << err.message(); - } -} diff --git a/daemon/watchcommands.h b/daemon/watchcommands.h deleted file mode 100644 index 8626b7c..0000000 --- a/daemon/watchcommands.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef WATCHCOMMANDS_H -#define WATCHCOMMANDS_H - -#include "watchconnector.h" -#include - -#include - -class WatchCommands : public QObject -{ - Q_OBJECT - LOG4QT_DECLARE_QCLASS_LOGGER - - watch::WatchConnector *watch; - -public: - explicit WatchCommands(watch::WatchConnector *watch, QObject *parent = 0); - -signals: - void hangup(); - -public slots: - void processMessage(uint endpoint, QByteArray data); - -protected slots: - void onMprisMetadataChanged(QVariantMap metadata); - void musicControl(watch::WatchConnector::MusicControl operation); - -}; - -#endif // WATCHCOMMANDS_H diff --git a/daemon/watchconnector.cpp b/daemon/watchconnector.cpp index a240b04..61eeb67 100644 --- a/daemon/watchconnector.cpp +++ b/daemon/watchconnector.cpp @@ -1,11 +1,12 @@ -#include "watchconnector.h" -#include #include #include -using namespace watch; +#include "watchconnector.h" +#include "unpacker.h" + +static const int RECONNECT_TIMEOUT = 500; //ms -static int RECONNECT_TIMEOUT = 500; //ms +using std::function; WatchConnector::WatchConnector(QObject *parent) : QObject(parent), socket(nullptr), is_connected(false) @@ -83,49 +84,95 @@ void WatchConnector::handleWatch(const QString &name, const QString &address) QString WatchConnector::decodeEndpoint(uint val) { - QMetaEnum Endpoints = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("Endpoints")); + QMetaEnum Endpoints = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("Endpoint")); const char *endpoint = Endpoints.valueToKey(val); return endpoint ? QString(endpoint) : QString("watchUNKNOWN_%1").arg(val); } -void WatchConnector::decodeMsg(QByteArray data) +void WatchConnector::setEndpointHandler(uint endpoint, EndpointHandlerFunc func) { - //Sometimes pebble sends a "00", we ignore it without future action - if (data.length() == 1 && data.at(0) == 0) { - return; - } - - if (data.length() < 4) { - logger()->error() << "Can not decode message data length invalid: " << data.toHex(); - return; + if (func) { + handlers.insert(endpoint, func); + } else { + handlers.remove(endpoint); } +} - unsigned int datalen = 0; - int index = 0; - datalen = (data.at(index) << 8) + data.at(index+1); - index += 2; +void WatchConnector::clearEndpointHandler(uint endpoint) +{ + handlers.remove(endpoint); +} - unsigned int endpoint = 0; - endpoint = (data.at(index) << 8) + data.at(index+1); - index += 2; +bool WatchConnector::dispatchMessage(uint endpoint, const QByteArray &data) +{ + auto tmp_it = tmpHandlers.find(endpoint); + if (tmp_it != tmpHandlers.end()) { + QList& funcs = tmp_it.value(); + bool ok = false; + if (!funcs.empty()) { + if (funcs.first()(data)) { + ok = true; + funcs.removeFirst(); + } + } + if (funcs.empty()) { + tmpHandlers.erase(tmp_it); + } + if (ok) { + return true; + } + } - logger()->debug() << "Length:" << datalen << "Endpoint:" << decodeEndpoint(endpoint); - logger()->debug() << "Data:" << data.mid(index).toHex(); + auto it = handlers.find(endpoint); + if (it != handlers.end()) { + if (it.value() && it.value()(data)) { + return true; + } + } - emit messageDecoded(endpoint, data.mid(index, datalen)); + logger()->info() << "message to endpoint" << decodeEndpoint(endpoint) << "was not dispatched"; + emit messageReceived(endpoint, data); + return false; } void WatchConnector::onReadSocket() { - logger()->debug() << "read"; + static const int header_length = 4; + + logger()->debug() << "readyRead bytesAvailable =" << socket->bytesAvailable(); QBluetoothSocket *socket = qobject_cast(sender()); - if (!socket) return; + Q_ASSERT(socket && socket == this->socket); + + while (socket->bytesAvailable() >= header_length) { + // Do nothing if there is no message to read. + if (socket->bytesAvailable() < header_length) { + if (socket->bytesAvailable() > 0) { + logger()->debug() << "incomplete header in read buffer"; + } + return; + } + + uchar header[header_length]; + socket->peek(reinterpret_cast(header), header_length); + + quint16 message_length, endpoint; + message_length = qFromBigEndian(&header[0]); + endpoint = qFromBigEndian(&header[sizeof(quint16)]); + + // Now wait for the entire message + if (socket->bytesAvailable() < header_length + message_length) { + logger()->debug() << "incomplete msg body in read buffer"; + return; + } + + socket->read(header_length); // Skip the header - while (socket->bytesAvailable()) { - QByteArray line = socket->readAll(); - emit messageReceived(socket->peerName(), QString::fromUtf8(line.constData(), line.length())); - decodeMsg(line); + QByteArray data = socket->read(message_length); + + logger()->debug() << "received message of length" << message_length << "to endpoint" << decodeEndpoint(endpoint); + + dispatchMessage(endpoint, data); } } @@ -181,13 +228,11 @@ void WatchConnector::onError(QBluetoothSocket::SocketError error) void WatchConnector::sendData(const QByteArray &data) { - writeData = data; + writeData.append(data); if (socket == nullptr) { logger()->debug() << "No socket - reconnecting"; reconnect(); - return; - } - if (is_connected) { + } else if (is_connected) { logger()->debug() << "Writing" << data.length() << "bytes to socket"; socket->write(data); } @@ -195,13 +240,13 @@ void WatchConnector::sendData(const QByteArray &data) void WatchConnector::onBytesWritten(qint64 bytes) { - writeData = writeData.mid(bytes); + writeData.remove(0, bytes); logger()->debug() << "Socket written" << bytes << "bytes," << writeData.length() << "left"; } -void WatchConnector::sendMessage(uint endpoint, QByteArray data) +void WatchConnector::sendMessage(uint endpoint, const QByteArray &data) { - logger()->debug() << "Sending message"; + logger()->debug() << "sending message to endpoint" << decodeEndpoint(endpoint); QByteArray msg; // First send the length @@ -390,3 +435,95 @@ void WatchConnector::endPhoneCall(uint cookie) { phoneControl(callEND, cookie, QStringList()); } + +void WatchConnector::getAppbankStatus(const std::function& callback) +{ + sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_STATUS)); + + tmpHandlers[watchAPP_MANAGER].append([this, callback](const QByteArray &data) { + if (data.at(0) != appmgrGET_APPBANK_STATUS) { + return false; + } + logger()->debug() << "getAppbankStatus response" << data.toHex(); + + if (data.size() < 9) { + logger()->warn() << "invalid getAppbankStatus response"; + return true; + } + + Unpacker u(data); + + u.skip(sizeof(quint8)); + + unsigned int num_banks = u.read(); + unsigned int apps_installed = u.read(); + + logger()->debug() << num_banks << "/" << apps_installed; + + for (unsigned int i = 0; i < apps_installed; i++) { + unsigned int id = u.read(); + unsigned int index = u.read(); + QString name = u.readFixedString(32); + QString company = u.readFixedString(32); + unsigned int flags = u.read(); + unsigned short version = u.read(); + + logger()->debug() << id << index << name << company << flags << version; + + if (u.bad()) { + logger()->warn() << "short read"; + return true; + } + } + + logger()->debug() << "finished"; + + return true; + }); +} + +void WatchConnector::getAppbankUuids(const function &)>& callback) +{ + sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_UUIDS)); + + tmpHandlers[watchAPP_MANAGER].append([this, callback](const QByteArray &data) { + if (data.at(0) != appmgrGET_APPBANK_UUIDS) { + return false; + } + logger()->debug() << "getAppbankUuids response" << data.toHex(); + + if (data.size() < 5) { + logger()->warn() << "invalid getAppbankUuids response"; + return true; + } + + Unpacker u(data); + + u.skip(sizeof(quint8)); + + unsigned int apps_installed = u.read(); + + logger()->debug() << apps_installed; + + QList uuids; + + for (unsigned int i = 0; i < apps_installed; i++) { + QUuid uuid = u.readUuid(); + + logger()->debug() << uuid.toString(); + + if (u.bad()) { + logger()->warn() << "short read"; + return true; + } + + uuids.push_back(uuid); + } + + logger()->debug() << "finished"; + + callback(uuids); + + return true; + }); +} diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index b64e31b..01c3dac 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -30,6 +30,7 @@ #ifndef WATCHCONNECTOR_H #define WATCHCONNECTOR_H +#include #include #include #include @@ -39,25 +40,18 @@ #include #include -#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) -using namespace QtBluetooth; -#endif - -namespace watch -{ - class WatchConnector : public QObject { Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER - Q_ENUMS(Endpoints) + Q_ENUMS(Endpoint) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString connected READ isConnected NOTIFY connectedChanged) public: - enum Endpoints { + enum Endpoint { watchTIME = 11, watchVERSION = 16, watchPHONE_VERSION = 17, @@ -111,6 +105,11 @@ public: systemBLUETOOTH_START_DISCOVERABLE = 6, systemBLUETOOTH_END_DISCOVERABLE = 7 }; + enum AppManager { + appmgrGET_APPBANK_STATUS = 1, + appmgrGET_APPBANK_UUIDS = 5 + }; + enum { leadEMAIL = 0, leadSMS = 1, @@ -139,27 +138,41 @@ public: osLINUX = 4, osWINDOWS = 5 }; + enum { + DEFAULT_TIMEOUT_MSECS = 100 + }; + typedef std::function EndpointHandlerFunc; explicit WatchConnector(QObject *parent = 0); virtual ~WatchConnector(); - bool isConnected() const { return is_connected; } - QString name() const { return socket != nullptr ? socket->peerName() : ""; } - QString timeStamp(); - QString decodeEndpoint(uint val); + inline bool isConnected() const { return is_connected; } + inline QString name() const { return socket != nullptr ? socket->peerName() : ""; } + + void setEndpointHandler(uint endpoint, EndpointHandlerFunc func); + void clearEndpointHandler(uint endpoint); + + static QString timeStamp(); + static QString decodeEndpoint(uint val); + + void getAppbankStatus(const std::function& callback); + void getAppbankUuids(const std::function &uuids)>& callback); signals: - void messageReceived(QString peer, QString msg); - void messageDecoded(uint endpoint, QByteArray data); + void messageReceived(uint endpoint, const QByteArray &data); void nameChanged(); void connectedChanged(); public slots: - void sendData(const QByteArray &data); - void sendMessage(uint endpoint, QByteArray data); + void deviceConnect(const QString &name, const QString &address); + void disconnect(); + void reconnect(); + + void sendMessage(uint endpoint, const QByteArray &data); void ping(uint val); void time(); + void sendNotification(uint lead, QString sender, QString data, QString subject); void sendSMSNotification(QString sender, QString data); void sendEmailNotification(QString sender, QString data, QString subject); @@ -176,8 +189,7 @@ public slots: void startPhoneCall(uint cookie=0); void endPhoneCall(uint cookie=0); - void deviceConnect(const QString &name, const QString &address); - void disconnect(); +private slots: void deviceDiscovered(const QBluetoothDeviceInfo&); void handleWatch(const QString &name, const QString &address); void onReadSocket(); @@ -185,18 +197,19 @@ public slots: void onConnected(); void onDisconnected(); void onError(QBluetoothSocket::SocketError error); - void reconnect(); private: - void decodeMsg(QByteArray data); + void sendData(const QByteArray &data); + bool dispatchMessage(uint endpoint, const QByteArray &data); QPointer socket; + QHash> tmpHandlers; + QHash handlers; bool is_connected; QByteArray writeData; QTimer reconnectTimer; QString _last_name; QString _last_address; }; -} #endif // WATCHCONNECTOR_H diff --git a/log4qt-debug.conf b/log4qt-debug.conf index 974e593..cabd53a 100644 --- a/log4qt-debug.conf +++ b/log4qt-debug.conf @@ -1,4 +1,4 @@ -log4j.rootLogger=DEBUG, consolelog, syslog, filelog +log4j.rootLogger=DEBUG, consolelog, syslog #, filelog log4j.appender.consolelog=org.apache.log4j.ColorConsoleAppender log4j.appender.consolelog.layout=org.apache.log4j.SimpleTimeLayout @@ -11,5 +11,3 @@ log4j.appender.syslog.threshold=ERROR log4j.appender.filelog=org.apache.log4j.DailyRollingFileAppender log4j.appender.filelog.layout=org.apache.log4j.SimpleTimeLayout log4j.appender.filelog.file=$XDG_CACHE_HOME/pebble.log - -# log4j.logger.Qt=INFO diff --git a/log4qt-release.conf b/log4qt-release.conf index 393f493..996c311 100644 --- a/log4qt-release.conf +++ b/log4qt-release.conf @@ -1,4 +1,4 @@ -log4j.rootLogger=WARN, consolelog, syslog, filelog +log4j.rootLogger=WARN, consolelog, syslog #, filelog log4j.appender.consolelog=org.apache.log4j.ColorConsoleAppender log4j.appender.consolelog.layout=org.apache.log4j.SimpleTimeLayout @@ -11,5 +11,3 @@ log4j.appender.syslog.threshold=ERROR log4j.appender.filelog=org.apache.log4j.DailyRollingFileAppender log4j.appender.filelog.layout=org.apache.log4j.SimpleTimeLayout log4j.appender.filelog.file=$XDG_CACHE_HOME/pebble.log - -# log4j.logger.Qt=INFO -- cgit v1.2.3 From 4527ab9a4147a8f15bf8ca5613341df9d0029d0c Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 30 Nov 2014 18:49:07 +0100 Subject: add stub datalogmanager --- daemon/daemon.pro | 6 ++++-- daemon/datalogmanager.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ daemon/datalogmanager.h | 25 +++++++++++++++++++++++++ daemon/manager.cpp | 9 +++++---- daemon/manager.h | 2 ++ daemon/watchconnector.cpp | 7 +++++++ daemon/watchconnector.h | 13 ++++++++++++- 7 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 daemon/datalogmanager.cpp create mode 100644 daemon/datalogmanager.h (limited to 'daemon/manager.h') diff --git a/daemon/daemon.pro b/daemon/daemon.pro index 768b50e..d4d7dbf 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -22,7 +22,8 @@ SOURCES += \ dbusconnector.cpp \ dbusadaptor.cpp \ appmanager.cpp \ - musicmanager.cpp + musicmanager.cpp \ + datalogmanager.cpp HEADERS += \ manager.h \ @@ -35,7 +36,8 @@ HEADERS += \ settings.h \ appmanager.h \ musicmanager.h \ - unpacker.h + unpacker.h \ + datalogmanager.h OTHER_FILES += \ org.pebbled.xml \ diff --git a/daemon/datalogmanager.cpp b/daemon/datalogmanager.cpp new file mode 100644 index 0000000..8026e15 --- /dev/null +++ b/daemon/datalogmanager.cpp @@ -0,0 +1,42 @@ +#include "datalogmanager.h" +#include "unpacker.h" + +DataLogManager::DataLogManager(WatchConnector *watch, QObject *parent) : + QObject(parent), watch(watch) +{ + watch->setEndpointHandler(WatchConnector::watchDATA_LOGGING, [this](const QByteArray& data) { + if (data.size() < 2) { + logger()->warn() << "small data_logging packet"; + return false; + } + + const char command = data[0]; + const int session = data[1]; + + switch (command) { + case WatchConnector::datalogOPEN: + logger()->debug() << "open datalog session" << session; + return true; + case WatchConnector::datalogCLOSE: + logger()->debug() << "close datalog session" << session; + return true; + case WatchConnector::datalogTIMEOUT: + logger()->debug() << "timeout datalog session" << session; + return true; + case WatchConnector::datalogDATA: + handleDataCommand(session, data.mid(2)); + return true; + default: + return false; + } + }); +} + +void DataLogManager::handleDataCommand(int session, const QByteArray &data) +{ + Unpacker u(data); + + // TODO Seemingly related to analytics, so not important. + + logger()->debug() << "got datalog data" << session << data.size(); +} diff --git a/daemon/datalogmanager.h b/daemon/datalogmanager.h new file mode 100644 index 0000000..47fc948 --- /dev/null +++ b/daemon/datalogmanager.h @@ -0,0 +1,25 @@ +#ifndef DATALOGMANAGER_H +#define DATALOGMANAGER_H + +#include "watchconnector.h" + +class DataLogManager : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + +public: + explicit DataLogManager(WatchConnector *watch, QObject *parent = 0); + +signals: + +public slots: + +private: + void handleDataCommand(int session, const QByteArray &data); + +private: + WatchConnector *watch; +}; + +#endif // DATALOGMANAGER_H diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 8a8acf4..7a02c86 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -12,6 +12,7 @@ Manager::Manager(Settings *settings, QObject *parent) : voice(new VoiceCallManager(settings, this)), notifications(new NotificationManager(settings, this)), music(new MusicManager(watch, this)), + datalog(new DataLogManager(watch, this)), apps(new AppManager(this)), notification(MNotification::DeviceEvent) { @@ -35,15 +36,15 @@ Manager::Manager(Settings *settings, QObject *parent) : return true; }); watch->setEndpointHandler(WatchConnector::watchPHONE_CONTROL, - [this](const QByteArray& data) { + [this](const QByteArray &data) { if (data.at(0) == WatchConnector::callHANGUP) { voice->hangupAll(); } return true; }); - watch->setEndpointHandler(WatchConnector::watchDATA_LOGGING, - [this](const QByteArray& data) { - //logger()->debug() << data.toHex(); + watch->setEndpointHandler(WatchConnector::watchLAUNCHER, + [this](const QByteArray &data) { + logger()->debug() << "LAUNCHER msg:" << data.toHex(); return true; }); diff --git a/daemon/manager.h b/daemon/manager.h index b210700..8b2fd96 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -6,6 +6,7 @@ #include "voicecallmanager.h" #include "notificationmanager.h" #include "musicmanager.h" +#include "datalogmanager.h" #include "appmanager.h" #include "settings.h" @@ -42,6 +43,7 @@ class Manager : VoiceCallManager *voice; NotificationManager *notifications; MusicManager *music; + DataLogManager *datalog; AppManager *apps; MNotification notification; diff --git a/daemon/watchconnector.cpp b/daemon/watchconnector.cpp index 61eeb67..a3ea181 100644 --- a/daemon/watchconnector.cpp +++ b/daemon/watchconnector.cpp @@ -160,6 +160,13 @@ void WatchConnector::onReadSocket() message_length = qFromBigEndian(&header[0]); endpoint = qFromBigEndian(&header[sizeof(quint16)]); + if (message_length > 8 * 1024) { + // Protocol does not allow messages more than 8K long, seemingly. + logger()->warn() << "received message size too long: " << message_length; + socket->readAll(); // drop input buffer + return; + } + // Now wait for the entire message if (socket->bytesAvailable() < header_length + message_length) { logger()->debug() << "incomplete msg body in read buffer"; diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index 01c3dac..c5ec332 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -109,7 +109,18 @@ public: appmgrGET_APPBANK_STATUS = 1, appmgrGET_APPBANK_UUIDS = 5 }; - + enum AppMessage { + appmsgPUSH = 1, + appmsgREQUEST = 2, + appmsgACK = 0xFF, + appmsgNACK = 0x7F + }; + enum DataLogMessage { + datalogOPEN = 1, + datalogDATA = 2, + datalogCLOSE = 3, + datalogTIMEOUT = 7 + }; enum { leadEMAIL = 0, leadSMS = 1, -- cgit v1.2.3 From f7c8244641a4242f6a8c706bd918494b23e48075 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 30 Nov 2014 20:58:57 +0100 Subject: introduce the AppMsgManager and the JsKitManager will be used to handle application messages (push, etc) while the JSKitManager will run PebbleKit JS scripts. also add the ability to unpack PebbleDicts --- daemon/appmanager.cpp | 15 +++++++++ daemon/appmanager.h | 3 +- daemon/appmsgmanager.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++ daemon/appmsgmanager.h | 30 +++++++++++++++++ daemon/daemon.pro | 12 +++++-- daemon/jskitmanager.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++ daemon/jskitmanager.h | 41 ++++++++++++++++++++++ daemon/jskitmanager_p.h | 15 +++++++++ daemon/manager.cpp | 15 ++++----- daemon/manager.h | 8 +++-- daemon/unpacker.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ daemon/unpacker.h | 29 +++++++++------- daemon/watchconnector.h | 12 +++++++ 13 files changed, 395 insertions(+), 27 deletions(-) create mode 100644 daemon/appmsgmanager.cpp create mode 100644 daemon/appmsgmanager.h create mode 100644 daemon/jskitmanager.cpp create mode 100644 daemon/jskitmanager.h create mode 100644 daemon/jskitmanager_p.h create mode 100644 daemon/unpacker.cpp (limited to 'daemon/manager.h') diff --git a/daemon/appmanager.cpp b/daemon/appmanager.cpp index 34af3af..d06681e 100644 --- a/daemon/appmanager.cpp +++ b/daemon/appmanager.cpp @@ -29,6 +29,21 @@ QStringList AppManager::appPaths() const QStandardPaths::LocateDirectory); } +const AppManager::AppInfo & AppManager::info(const QUuid &uuid) const +{ + return _apps.value(uuid); +} + +const AppManager::AppInfo & AppManager::info(const QString &name) const +{ + QUuid uuid = _names.value(name); + if (!uuid.isNull()) { + return info(uuid); + } else { + return AppInfo(); + } +} + void AppManager::rescan() { QStringList watchedDirs = _watcher->directories(); diff --git a/daemon/appmanager.h b/daemon/appmanager.h index 5e150ab..dc2a979 100644 --- a/daemon/appmanager.h +++ b/daemon/appmanager.h @@ -30,7 +30,8 @@ public: QStringList appPaths() const; - bool installPebbleApp(const QString &pbwFile); + const AppInfo & info(const QUuid &uuid) const; + const AppInfo & info(const QString &shortName) const; public slots: void rescan(); diff --git a/daemon/appmsgmanager.cpp b/daemon/appmsgmanager.cpp new file mode 100644 index 0000000..9eb948e --- /dev/null +++ b/daemon/appmsgmanager.cpp @@ -0,0 +1,77 @@ +#include "appmsgmanager.h" +#include "unpacker.h" + +// TODO D-Bus server for non JS kit apps!!!! + +AppMsgManager::AppMsgManager(WatchConnector *watch, QObject *parent) + : QObject(parent), watch(watch) +{ + watch->setEndpointHandler(WatchConnector::watchLAUNCHER, + [this](const QByteArray &data) { + if (data.at(0) == WatchConnector::appmsgPUSH) { + logger()->debug() << "LAUNCHER is PUSHing" << data.toHex(); + Unpacker u(data); + u.skip(1); // skip data.at(0) which we just already checked above. + uint transaction = u.read(); + QUuid uuid = u.readUuid(); + WatchConnector::Dict dict = u.readDict(); + if (u.bad() || !dict.contains(1)) { + logger()->warn() << "Failed to parse LAUNCHER message"; + return true; + } + + switch (dict.value(1).toInt()) { + case WatchConnector::launcherSTARTED: + logger()->debug() << "App starting in watch:" << uuid; + this->watch->sendMessage(WatchConnector::watchLAUNCHER, + buildAckMessage(transaction)); + emit appStarted(uuid); + break; + case WatchConnector::launcherSTOPPED: + logger()->debug() << "App stopping in watch:" << uuid; + this->watch->sendMessage(WatchConnector::watchLAUNCHER, + buildAckMessage(transaction)); + emit appStopped(uuid); + break; + default: + logger()->warn() << "LAUNCHER pushed unknown message:" << uuid << dict; + this->watch->sendMessage(WatchConnector::watchLAUNCHER, + buildNackMessage(transaction)); + break; + } + } + + return true; + }); + + watch->setEndpointHandler(WatchConnector::watchAPPLICATION_MESSAGE, + [this](const QByteArray &data) { + switch (data.at(0)) { + case WatchConnector::appmsgPUSH: + break; + } + + return true; + }); +} + +void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data) +{ + // TODO +} + +QByteArray AppMsgManager::buildAckMessage(uint transaction) +{ + QByteArray ba(2, Qt::Uninitialized); + ba[0] = WatchConnector::appmsgACK; + ba[1] = transaction; + return ba; +} + +QByteArray AppMsgManager::buildNackMessage(uint transaction) +{ + QByteArray ba(2, Qt::Uninitialized); + ba[0] = WatchConnector::appmsgNACK; + ba[1] = transaction; + return ba; +} diff --git a/daemon/appmsgmanager.h b/daemon/appmsgmanager.h new file mode 100644 index 0000000..651d84e --- /dev/null +++ b/daemon/appmsgmanager.h @@ -0,0 +1,30 @@ +#ifndef APPMSGMANAGER_H +#define APPMSGMANAGER_H + +#include "watchconnector.h" + +class AppMsgManager : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + +public: + explicit AppMsgManager(WatchConnector *watch, QObject *parent); + +public slots: + void send(const QUuid &uuid, const QVariantMap &data); + +signals: + void appStarted(const QUuid &uuid); + void appStopped(const QUuid &uuid); + void dataReceived(const QUuid &uuid, const QVariantMap &data); + +private: + static QByteArray buildAckMessage(uint transaction); + static QByteArray buildNackMessage(uint transaction); + +private: + WatchConnector *watch; +}; + +#endif // APPMSGMANAGER_H diff --git a/daemon/daemon.pro b/daemon/daemon.pro index d4d7dbf..21c15c8 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -4,7 +4,7 @@ CONFIG += console CONFIG += link_pkgconfig QT -= gui -QT += bluetooth dbus contacts +QT += bluetooth dbus contacts qml PKGCONFIG += mlite5 icu-i18n CONFIG += c++11 @@ -23,7 +23,10 @@ SOURCES += \ dbusadaptor.cpp \ appmanager.cpp \ musicmanager.cpp \ - datalogmanager.cpp + datalogmanager.cpp \ + unpacker.cpp \ + appmsgmanager.cpp \ + jskitmanager.cpp HEADERS += \ manager.h \ @@ -37,7 +40,10 @@ HEADERS += \ appmanager.h \ musicmanager.h \ unpacker.h \ - datalogmanager.h + datalogmanager.h \ + appmsgmanager.h \ + jskitmanager.h \ + jskitmanager_p.h OTHER_FILES += \ org.pebbled.xml \ diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp new file mode 100644 index 0000000..698b22b --- /dev/null +++ b/daemon/jskitmanager.cpp @@ -0,0 +1,77 @@ +#include +#include +#include "jskitmanager.h" +#include "jskitmanager_p.h" + +JSKitPebble::JSKitPebble(JSKitManager *mgr) + : QObject(mgr) +{ +} + +JSKitPebble::~JSKitPebble() +{ +} + +JSKitManager::JSKitManager(AppManager *apps, AppMsgManager *appmsg, QObject *parent) : + QObject(parent), _apps(apps), _appmsg(appmsg), _engine(0) +{ + connect(_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted); + connect(_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped); +} + +JSKitManager::~JSKitManager() +{ + if (_engine) { + stopJsApp(); + } +} + +void JSKitManager::handleAppStarted(const QUuid &uuid) +{ + const auto &info = _apps->info(uuid); + if (!info.uuid.isNull() && info.isJSKit) { + logger()->debug() << "Preparing to start JSKit app" << info.uuid << info.shortName; + _curApp = info; + startJsApp(); + } +} + +void JSKitManager::handleAppStopped(const QUuid &uuid) +{ + if (!_curApp.uuid.isNull()) { + if (_curApp.uuid != uuid) { + logger()->warn() << "Closed app with invalid UUID"; + } + + _curApp = AppManager::AppInfo(); + } +} + +void JSKitManager::startJsApp() +{ + if (_engine) stopJsApp(); + _engine = new QJSEngine(this); + _jspebble = new JSKitPebble(this); + + QJSValue globalObj = _engine->globalObject(); + + globalObj.setProperty("Pebble", _engine->newQObject(_jspebble)); + + QJSValueIterator it(globalObj); + while (it.hasNext()) { + it.next(); + logger()->debug() << "JS property:" << it.name(); + } +} + +void JSKitManager::stopJsApp() +{ + if (!_engine) return; // Nothing to do! + + _engine->collectGarbage(); + + delete _engine; + _engine = 0; + delete _jspebble; + _jspebble = 0; +} diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h new file mode 100644 index 0000000..5e2880f --- /dev/null +++ b/daemon/jskitmanager.h @@ -0,0 +1,41 @@ +#ifndef JSKITMANAGER_H +#define JSKITMANAGER_H + +#include +#include "appmanager.h" +#include "appmsgmanager.h" + +class JSKitPebble; + +class JSKitManager : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + +public: + explicit JSKitManager(AppManager *apps, AppMsgManager *appmsg, QObject *parent = 0); + ~JSKitManager(); + +signals: + +public slots: + +private slots: + void handleAppStarted(const QUuid &uuid); + void handleAppStopped(const QUuid &uuid); + +private: + void startJsApp(); + void stopJsApp(); + +private: + friend class JSKitPebble; + + AppManager *_apps; + AppMsgManager *_appmsg; + AppManager::AppInfo _curApp; + QJSEngine *_engine; + QPointer _jspebble; +}; + +#endif // JSKITMANAGER_H diff --git a/daemon/jskitmanager_p.h b/daemon/jskitmanager_p.h new file mode 100644 index 0000000..690a0ec --- /dev/null +++ b/daemon/jskitmanager_p.h @@ -0,0 +1,15 @@ +#ifndef JSKITMANAGER_P_H +#define JSKITMANAGER_P_H + +#include "jskitmanager.h" + +class JSKitPebble : public QObject +{ + Q_OBJECT + +public: + explicit JSKitPebble(JSKitManager *mgr); + ~JSKitPebble(); +}; + +#endif // JSKITMANAGER_P_H diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 7a02c86..778fdc6 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -1,19 +1,21 @@ -#include "manager.h" -#include "dbusadaptor.h" - #include #include #include +#include "manager.h" +#include "dbusadaptor.h" + Manager::Manager(Settings *settings, QObject *parent) : QObject(parent), settings(settings), watch(new WatchConnector(this)), dbus(new DBusConnector(this)), + apps(new AppManager(this)), voice(new VoiceCallManager(settings, this)), notifications(new NotificationManager(settings, this)), music(new MusicManager(watch, this)), datalog(new DataLogManager(watch, this)), - apps(new AppManager(this)), + appmsg(new AppMsgManager(watch, this)), + js(new JSKitManager(apps, appmsg, this)), notification(MNotification::DeviceEvent) { connect(settings, SIGNAL(valueChanged(QString)), SLOT(onSettingChanged(const QString&))); @@ -42,11 +44,6 @@ Manager::Manager(Settings *settings, QObject *parent) : } return true; }); - watch->setEndpointHandler(WatchConnector::watchLAUNCHER, - [this](const QByteArray &data) { - logger()->debug() << "LAUNCHER msg:" << data.toHex(); - return true; - }); connect(voice, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged())); connect(voice, SIGNAL(error(const QString &)), SLOT(onVoiceError(const QString &))); diff --git a/daemon/manager.h b/daemon/manager.h index 8b2fd96..f1dd53e 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -7,6 +7,8 @@ #include "notificationmanager.h" #include "musicmanager.h" #include "datalogmanager.h" +#include "appmsgmanager.h" +#include "jskitmanager.h" #include "appmanager.h" #include "settings.h" @@ -40,11 +42,13 @@ class Manager : WatchConnector *watch; DBusConnector *dbus; + AppManager *apps; VoiceCallManager *voice; NotificationManager *notifications; MusicManager *music; DataLogManager *datalog; - AppManager *apps; + AppMsgManager *appmsg; + JSKitManager *js; MNotification notification; @@ -73,7 +77,7 @@ protected: void transliterateMessage(const QString &text); signals: - void mprisMetadataChanged(QVariantMap); + void mprisMetadataChanged(const QVariantMap &metadata); public slots: void applyProfile(); diff --git a/daemon/unpacker.cpp b/daemon/unpacker.cpp new file mode 100644 index 0000000..fc38020 --- /dev/null +++ b/daemon/unpacker.cpp @@ -0,0 +1,88 @@ +#include "unpacker.h" +#include "watchconnector.h" + +QByteArray Unpacker::readBytes(int n) +{ + if (checkBad(n)) return QByteArray(); + const char *u = &_buf.constData()[_offset]; + _offset += n; + return QByteArray(u, n); +} + +QString Unpacker::readFixedString(int n) +{ + if (checkBad(n)) return QString(); + const char *u = &_buf.constData()[_offset]; + _offset += n; + return QString::fromUtf8(u, strnlen(u, n)); +} + +QUuid Unpacker::readUuid() +{ + if (checkBad(16)) return QString(); + _offset += 16; + return QUuid::fromRfc4122(_buf.mid(_offset - 16, 16)); +} + +QMap Unpacker::readDict() +{ + QMap d; + if (checkBad(1)) return d; + + const int n = read(); + + for (int i = 0; i < n; i++) { + if (checkBad(4 + 1 + 2)) return d; + const int key = readLE(); // For some reason, this is little endian. + const int type = readLE(); + const int width = readLE(); + + switch (type) { + case WatchConnector::typeBYTES: + d.insert(key, QVariant::fromValue(readBytes(width))); + break; + case WatchConnector::typeSTRING: + d.insert(key, QVariant::fromValue(readFixedString(width))); + break; + case WatchConnector::typeUINT: + switch (width) { + case sizeof(quint8): + d.insert(key, QVariant::fromValue(readLE())); + break; + case sizeof(quint16): + d.insert(key, QVariant::fromValue(readLE())); + break; + case sizeof(quint32): + d.insert(key, QVariant::fromValue(readLE())); + break; + default: + _bad = true; + return d; + } + + break; + case WatchConnector::typeINT: + switch (width) { + case sizeof(qint8): + d.insert(key, QVariant::fromValue(readLE())); + break; + case sizeof(qint16): + d.insert(key, QVariant::fromValue(readLE())); + break; + case sizeof(qint32): + d.insert(key, QVariant::fromValue(readLE())); + break; + default: + _bad = true; + return d; + } + + break; + default: + _bad = true; + return d; + } + } + + return d; +} diff --git a/daemon/unpacker.h b/daemon/unpacker.h index 94908cb..000c3e8 100644 --- a/daemon/unpacker.h +++ b/daemon/unpacker.h @@ -5,19 +5,30 @@ #include #include #include +#include +#include class Unpacker { + LOG4QT_DECLARE_STATIC_LOGGER(logger, Unpacker) + public: Unpacker(const QByteArray &data); template T read(); + template + T readLE(); + + QByteArray readBytes(int n); + QString readFixedString(int n); QUuid readUuid(); + QMap readDict(); + void skip(int n); bool bad() const; @@ -45,19 +56,13 @@ inline T Unpacker::read() return qFromBigEndian(u); } -inline QString Unpacker::readFixedString(int n) -{ - if (checkBad(n)) return QString(); - const char *u = &_buf.constData()[_offset]; - _offset += n; - return QString::fromUtf8(u, strnlen(u, n)); -} - -inline QUuid Unpacker::readUuid() +template +inline T Unpacker::readLE() { - if (checkBad(16)) return QString(); - _offset += 16; - return QUuid::fromRfc4122(_buf.mid(_offset - 16, 16)); + if (checkBad(sizeof(T))) return 0; + const uchar *u = p(); + _offset += sizeof(T); + return qFromLittleEndian(u); } inline void Unpacker::skip(int n) diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index c5ec332..8a7d574 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -121,6 +121,10 @@ public: datalogCLOSE = 3, datalogTIMEOUT = 7 }; + enum { + launcherSTARTED = 1, + launcherSTOPPED = 0 + }; enum { leadEMAIL = 0, leadSMS = 1, @@ -153,6 +157,14 @@ public: DEFAULT_TIMEOUT_MSECS = 100 }; + typedef QMap Dict; + enum DictItemType { + typeBYTES, + typeSTRING, + typeUINT, + typeINT + }; + typedef std::function EndpointHandlerFunc; explicit WatchConnector(QObject *parent = 0); -- 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 'daemon/manager.h') 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 'daemon/manager.h') 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 843e8c2550f69de3b9dfc3ec5f13d2c3a5710896 Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 3 Dec 2014 00:00:42 +0100 Subject: implement more parts of the new D-Bus API --- daemon/appmsgmanager.cpp | 10 ++++++ daemon/appmsgmanager.h | 2 ++ daemon/jskitmanager.cpp | 5 +++ daemon/jskitmanager.h | 3 +- daemon/jskitobjects.cpp | 7 +---- daemon/manager.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++----- daemon/manager.h | 37 +++++++--------------- org.pebbled.Watch.xml | 9 ++++++ 8 files changed, 113 insertions(+), 41 deletions(-) (limited to 'daemon/manager.h') diff --git a/daemon/appmsgmanager.cpp b/daemon/appmsgmanager.cpp index b620078..afaabee 100644 --- a/daemon/appmsgmanager.cpp +++ b/daemon/appmsgmanager.cpp @@ -130,6 +130,16 @@ void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data) send(uuid, data, nullCallback, nullCallback); } +void AppMsgManager::launchApp(const QUuid &uuid) +{ + // TODO +} + +void AppMsgManager::closeApp(const QUuid &uuid) +{ + // TODO +} + WatchConnector::Dict AppMsgManager::mapAppKeys(const QUuid &uuid, const QVariantMap &data) { AppInfo info = apps->info(uuid); diff --git a/daemon/appmsgmanager.h b/daemon/appmsgmanager.h index ca1d484..bc9c82f 100644 --- a/daemon/appmsgmanager.h +++ b/daemon/appmsgmanager.h @@ -18,6 +18,8 @@ public: public slots: void send(const QUuid &uuid, const QVariantMap &data); + void launchApp(const QUuid &uuid); + void closeApp(const QUuid &uuid); signals: void appStarted(const QUuid &uuid); diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp index cfd860e..9efc5c8 100644 --- a/daemon/jskitmanager.cpp +++ b/daemon/jskitmanager.cpp @@ -24,6 +24,11 @@ QJSEngine * JSKitManager::engine() return _engine; } +bool JSKitManager::isJSKitAppRunning() const +{ + return _engine != 0; +} + void JSKitManager::showConfiguration() { if (_engine) { diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h index fd6040d..07ecec1 100644 --- a/daemon/jskitmanager.h +++ b/daemon/jskitmanager.h @@ -19,10 +19,11 @@ public: ~JSKitManager(); QJSEngine * engine(); + bool isJSKitAppRunning() const; signals: void appNotification(const QUuid &uuid, const QString &title, const QString &body); - void appOpenUrl(const QString &url); + void appOpenUrl(const QUrl &url); public slots: void showConfiguration(); diff --git a/daemon/jskitobjects.cpp b/daemon/jskitobjects.cpp index 9e4cfde..a0bc0ba 100644 --- a/daemon/jskitobjects.cpp +++ b/daemon/jskitobjects.cpp @@ -57,12 +57,7 @@ void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QSt 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 + emit _mgr->appOpenUrl(url); } QJSValue JSKitPebble::createXMLHttpRequest() diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 38b3948..0666de0 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -56,6 +56,8 @@ Manager::Manager(Settings *settings, QObject *parent) : connect(notifications, SIGNAL(facebookNotify(const QString &,const QString &)), SLOT(onFacebookNotify(const QString &,const QString &))); connect(appmsg, &AppMsgManager::messageReceived, this, &Manager::onAppMessage); + connect(appmsg, &AppMsgManager::appStarted, this, &Manager::onAppOpened); + connect(appmsg, &AppMsgManager::appStopped, this, &Manager::onAppClosed); QDBusConnection session = QDBusConnection::sessionBus(); new WatchAdaptor(proxy); @@ -389,19 +391,82 @@ void Manager::transliterateMessage(const QString &text) } } -void Manager::test() +void Manager::onAppMessage(const QUuid &uuid, const QVariantMap &data) { - logger()->debug() << "Starting test"; - - js->showConfiguration(); + emit proxy->AppMessage(uuid.toString(), data); } -void Manager::onAppMessage(const QUuid &uuid, const QVariantMap &data) +void Manager::onAppOpened(const QUuid &uuid) { - emit proxy->AppMessage(uuid.toString(), data); + currentAppUuid = uuid; + emit proxy->AppOpened(uuid.toString()); } -void Manager::onWebviewClosed(const QString &result) +void Manager::onAppClosed(const QUuid &uuid) { - js->handleWebviewClosed(result); + currentAppUuid = QUuid(); + emit proxy->AppClosed(uuid.toString()); +} + +bool PebbledProxy::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 PebbledProxy::StartAppConfiguration(const QString &uuid) { + 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 QString(); + } + + if (!manager()->js->isJSKitAppRunning()) { + sendErrorReply(msg.interface() + ".Error.JSNotActive", + "The requested app is not a PebbleKit JS application"); + return QString(); + } + + // After calling showConfiguration() on the script, + // it will (eventually!) return a URL to us via the appOpenUrl signal. + + // So we can't send the D-Bus reply right now. + setDelayedReply(true); + + // Set up a signal handler to catch the appOpenUrl signal. + QMetaObject::Connection c = connect(manager()->js, &JSKitManager::appOpenUrl, + [this,msg,c](const QUrl &url) { + // Workaround: due to a GCC bug we can't capture the uuid parameter, but we can extract + // it again from the original message arguments. + QString uuid = msg.arguments().at(0).toString(); + if (manager()->currentAppUuid != uuid) { + // App was changed while we were waiting for the script.. + QDBusMessage reply = msg.createErrorReply(msg.interface() + ".Error.AppNotRunning", + "The requested app is not currently opened in the watch"); + connection().send(reply); + } else { + QDBusMessage reply = msg.createReply(QVariant::fromValue(url.toString())); + connection().send(reply); + } + + disconnect(c); + }); + // TODO: JS script may fail, never call OpenURL, or something like that + // In those cases we may leak the above connection + // So we need to also set a timeout or similar. + + manager()->js->showConfiguration(); + + return QString(); // This return value should never be used. } diff --git a/daemon/manager.h b/daemon/manager.h index c12d3dc..f099aac 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -62,6 +62,8 @@ class Manager : public QObject, protected QDBusContext QString lastSeenMpris; QVariantMap mprisMetadata; + QUuid currentAppUuid; + QScopedPointer transliterator; public: @@ -84,8 +86,6 @@ public slots: void applyProfile(); private slots: - void test(); - void onWebviewClosed(const QString &result); void onSettingChanged(const QString &key); void onSettingsChanged(); void onPebbleChanged(); @@ -103,6 +103,8 @@ private slots: void setMprisMetadata(QVariantMap metadata); void onAppMessage(const QUuid &uuid, const QVariantMap &data); + void onAppOpened(const QUuid &uuid); + void onAppClosed(const QUuid &uuid); }; /** This class is what's actually exported over D-Bus, @@ -132,32 +134,13 @@ public slots: 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); + inline void LaunchApp(const QString &uuid) { manager()->appmsg->launchApp(uuid); } + inline void CloseApp(const QString &uuid) { manager()->appmsg->closeApp(uuid); } - // TODO - } + bool SendAppMessage(const QString &uuid, const QVariantMap &data); + QString StartAppConfiguration(const QString &uuid); - inline void SendAppConfiguration(const QString &uuid, const QString &data) { + void SendAppConfiguration(const QString &uuid, const QString &data) { // TODO } @@ -166,6 +149,8 @@ signals: void AddressChanged(); void ConnectedChanged(); void AppMessage(const QString &uuid, const QVariantMap &data); + void AppOpened(const QString &uuid); + void AppClosed(const QString &uuid); }; #endif // MANAGER_H diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml index 72aab6b..11f20b7 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -5,6 +5,7 @@ + @@ -15,6 +16,7 @@ + @@ -33,6 +35,13 @@ + + + + + + + -- 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 'daemon/manager.h') 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 'daemon/manager.h') 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 132562620764a8c0cc01dcc1574003beaa70272f Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 3 Dec 2014 01:24:25 +0100 Subject: fix small typo --- daemon/manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'daemon/manager.h') diff --git a/daemon/manager.h b/daemon/manager.h index 239c212..e84e982 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -131,7 +131,7 @@ public: inline QString AppUuid() const { return manager()->currentAppUuid.toString(); } public slots: - inline void Disconnected() { manager()->watch->disconnect(); } + inline void Disconnect() { manager()->watch->disconnect(); } inline void Reconnect() { manager()->watch->reconnect(); } inline void Ping(uint val) { manager()->watch->ping(val); } inline void SyncTime() { manager()->watch->time(); } -- cgit v1.2.3 From 0a92face6b035a26aad3d4d7ffa5a72b463e4c2a Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 6 Dec 2014 02:11:46 +0100 Subject: forward jskit notifications --- daemon/manager.cpp | 7 +++++++ daemon/manager.h | 1 + 2 files changed, 8 insertions(+) (limited to 'daemon/manager.h') diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 6fd47a4..b488432 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -59,6 +59,8 @@ Manager::Manager(Settings *settings, QObject *parent) : connect(appmsg, &AppMsgManager::appStarted, this, &Manager::onAppOpened); connect(appmsg, &AppMsgManager::appStopped, this, &Manager::onAppClosed); + connect(js, &JSKitManager::appNotification, this, &Manager::onAppNotification); + QDBusConnection session = QDBusConnection::sessionBus(); new WatchAdaptor(proxy); session.registerObject("/org/pebbled/Watch", proxy); @@ -391,6 +393,11 @@ void Manager::transliterateMessage(const QString &text) } } +void Manager::onAppNotification(const QUuid &uuid, const QString &title, const QString &body) +{ + watch->sendSMSNotification(title, body); +} + void Manager::onAppMessage(const QUuid &uuid, const QVariantMap &data) { emit proxy->AppMessage(uuid.toString(), data); diff --git a/daemon/manager.h b/daemon/manager.h index e84e982..18bd7bf 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -102,6 +102,7 @@ private slots: void setMprisMetadata(QDBusArgument metadata); void setMprisMetadata(QVariantMap metadata); + void onAppNotification(const QUuid &uuid, const QString &title, const QString &body); void onAppMessage(const QUuid &uuid, const QVariantMap &data); void onAppOpened(const QUuid &uuid); void onAppClosed(const QUuid &uuid); -- cgit v1.2.3 From 5499dc58d09f07081c41b8e4dead810a82137939 Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 6 Dec 2014 23:31:57 +0100 Subject: properly send acks for incoming appmsgs --- daemon/appmsgmanager.cpp | 32 +++++++++++++++++++++++++++++++- daemon/appmsgmanager.h | 7 ++++++- daemon/jskitmanager.cpp | 22 +++++++++++++++++++--- daemon/jskitmanager.h | 2 +- daemon/jskitobjects.cpp | 3 ++- daemon/manager.cpp | 7 +------ daemon/manager.h | 1 - 7 files changed, 60 insertions(+), 14 deletions(-) (limited to 'daemon/manager.h') diff --git a/daemon/appmsgmanager.cpp b/daemon/appmsgmanager.cpp index 312043a..f24b8d9 100644 --- a/daemon/appmsgmanager.cpp +++ b/daemon/appmsgmanager.cpp @@ -69,6 +69,16 @@ void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data, const std:: } } +void AppMsgManager::setMessageHandler(const QUuid &uuid, MessageHandlerFunc func) +{ + _handlers.insert(uuid, func); +} + +void AppMsgManager::clearMessageHandler(const QUuid &uuid) +{ + _handlers.remove(uuid); +} + uint AppMsgManager::lastTransactionId() const { return _lastTransactionId; @@ -236,6 +246,8 @@ void AppMsgManager::handlePushMessage(const QByteArray &data) if (!unpackPushMessage(data, &transaction, &uuid, &dict)) { logger()->warn() << "Failed to parse APP_MSG PUSH"; + watch->sendMessage(WatchConnector::watchAPPLICATION_MESSAGE, + buildNackMessage(transaction)); return; } @@ -244,7 +256,25 @@ void AppMsgManager::handlePushMessage(const QByteArray &data) QVariantMap msg = mapAppKeys(uuid, dict); logger()->debug() << "Mapped dict" << msg; - emit messageReceived(uuid, msg); + bool result; + + MessageHandlerFunc handler = _handlers.value(uuid); + if (handler) { + result = handler(msg); + } else { + // No handler? Let's just send an ACK. + result = false; + } + + if (result) { + logger()->debug() << "ACKing transaction" << transaction; + watch->sendMessage(WatchConnector::watchAPPLICATION_MESSAGE, + buildAckMessage(transaction)); + } else { + logger()->info() << "NACKing transaction" << transaction; + watch->sendMessage(WatchConnector::watchAPPLICATION_MESSAGE, + buildNackMessage(transaction)); + } } void AppMsgManager::handleAckMessage(const QByteArray &data, bool ack) diff --git a/daemon/appmsgmanager.h b/daemon/appmsgmanager.h index 9aaabd4..e52c544 100644 --- a/daemon/appmsgmanager.h +++ b/daemon/appmsgmanager.h @@ -19,6 +19,11 @@ public: void send(const QUuid &uuid, const QVariantMap &data, const std::function &ackCallback, const std::function &nackCallback); + + typedef std::function MessageHandlerFunc; + void setMessageHandler(const QUuid &uuid, MessageHandlerFunc func); + void clearMessageHandler(const QUuid &uuid); + uint lastTransactionId() const; uint nextTransactionId() const; @@ -30,7 +35,6 @@ public slots: signals: void appStarted(const QUuid &uuid); void appStopped(const QUuid &uuid); - void messageReceived(const QUuid &uuid, const QVariantMap &data); private: WatchConnector::Dict mapAppKeys(const QUuid &uuid, const QVariantMap &data); @@ -56,6 +60,7 @@ private slots: private: AppManager *apps; WatchConnector *watch; + QHash _handlers; quint8 _lastTransactionId; struct PendingTransaction { diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp index 6b291df..c73d32e 100644 --- a/daemon/jskitmanager.cpp +++ b/daemon/jskitmanager.cpp @@ -9,7 +9,6 @@ JSKitManager::JSKitManager(AppManager *apps, AppMsgManager *appmsg, QObject *par { connect(_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted); connect(_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped); - connect(_appmsg, &AppMsgManager::messageReceived, this, &JSKitManager::handleAppMessage); } JSKitManager::~JSKitManager() @@ -83,7 +82,7 @@ void JSKitManager::handleAppStopped(const QUuid &uuid) } } -void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &data) +void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &msg) { if (_curApp.uuid() == uuid) { logger()->debug() << "received a message for the current JSKit app"; @@ -94,7 +93,7 @@ void JSKitManager::handleAppMessage(const QUuid &uuid, const QVariantMap &data) } QJSValue eventObj = _engine->newObject(); - eventObj.setProperty("payload", _engine->toScriptValue(data)); + eventObj.setProperty("payload", _engine->toScriptValue(msg)); _jspebble->invokeCallbacks("appmessage", QJSValueList({eventObj})); } @@ -164,6 +163,19 @@ void JSKitManager::startJsApp() // Now load the actual script loadJsFile(_curApp.path() + "/pebble-js-app.js"); + // Setup the message callback + QUuid uuid = _curApp.uuid(); + _appmsg->setMessageHandler(uuid, [this, uuid](const QVariantMap &msg) { + QMetaObject::invokeMethod(this, "handleAppMessage", Qt::QueuedConnection, + Q_ARG(QUuid, uuid), + Q_ARG(QVariantMap, msg)); + + // Invoke the slot as a queued connection to give time for the ACK message + // to go through first. + + return true; + }); + // We try to invoke the callbacks even if script parsing resulted in error... _jspebble->invokeCallbacks("ready"); } @@ -174,6 +186,10 @@ void JSKitManager::stopJsApp() logger()->debug() << "stopping JS app"; + if (!_curApp.uuid().isNull()) { + _appmsg->clearMessageHandler(_curApp.uuid()); + } + _engine->collectGarbage(); _engine->deleteLater(); diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h index 4239f91..b42e338 100644 --- a/daemon/jskitmanager.h +++ b/daemon/jskitmanager.h @@ -35,7 +35,7 @@ public slots: private slots: void handleAppStarted(const QUuid &uuid); void handleAppStopped(const QUuid &uuid); - void handleAppMessage(const QUuid &uuid, const QVariantMap &data); + void handleAppMessage(const QUuid &uuid, const QVariantMap &msg); private: bool loadJsFile(const QString &filename); diff --git a/daemon/jskitobjects.cpp b/daemon/jskitobjects.cpp index e55f2d5..3386f16 100644 --- a/daemon/jskitobjects.cpp +++ b/daemon/jskitobjects.cpp @@ -519,7 +519,8 @@ void JSKitGeolocation::handleTimeout() for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) { if (it->timer.hasExpired(it->timeout)) { - logger()->info() << "positioning timeout for watch" << it->watchId; + logger()->info() << "positioning timeout for watch" << it->watchId + << ", watch is" << it->timer.elapsed() << "ms old, timeout is" << it->timeout; invokeCallback(it->errorCallback, obj); if (it->once) { diff --git a/daemon/manager.cpp b/daemon/manager.cpp index b488432..8a00373 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -55,7 +55,6 @@ 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 &))); - connect(appmsg, &AppMsgManager::messageReceived, this, &Manager::onAppMessage); connect(appmsg, &AppMsgManager::appStarted, this, &Manager::onAppOpened); connect(appmsg, &AppMsgManager::appStopped, this, &Manager::onAppClosed); @@ -395,14 +394,10 @@ void Manager::transliterateMessage(const QString &text) void Manager::onAppNotification(const QUuid &uuid, const QString &title, const QString &body) { + Q_UNUSED(uuid); watch->sendSMSNotification(title, body); } -void Manager::onAppMessage(const QUuid &uuid, const QVariantMap &data) -{ - emit proxy->AppMessage(uuid.toString(), data); -} - void Manager::onAppOpened(const QUuid &uuid) { currentAppUuid = uuid; diff --git a/daemon/manager.h b/daemon/manager.h index 18bd7bf..bf83f75 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -103,7 +103,6 @@ private slots: void setMprisMetadata(QVariantMap metadata); void onAppNotification(const QUuid &uuid, const QString &title, const QString &body); - void onAppMessage(const QUuid &uuid, const QVariantMap &data); void onAppOpened(const QUuid &uuid); void onAppClosed(const QUuid &uuid); }; -- cgit v1.2.3 From c7804f23412c14d6252bee6deb904d59ced835e2 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 7 Dec 2014 00:11:54 +0100 Subject: delete unused d-bus signal --- daemon/manager.h | 1 - org.pebbled.Watch.xml | 5 ----- 2 files changed, 6 deletions(-) (limited to 'daemon/manager.h') diff --git a/daemon/manager.h b/daemon/manager.h index bf83f75..9347289 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -148,7 +148,6 @@ signals: 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 759a6db..87a22b9 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -33,11 +33,6 @@ - - - - - -- cgit v1.2.3 From 49c20eb7e2933ae6e9e4337fc0fe9b49a39efde8 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 7 Dec 2014 01:31:51 +0100 Subject: add bankmanager and ability to unload apps --- daemon/bankmanager.cpp | 233 ++++++++++++++++++++++++++++++++++++++++++++++ daemon/bankmanager.h | 51 ++++++++++ daemon/daemon.pro | 6 +- daemon/manager.cpp | 12 +++ daemon/manager.h | 4 + daemon/watchconnector.cpp | 90 ------------------ daemon/watchconnector.h | 4 +- org.pebbled.Watch.xml | 5 + 8 files changed, 310 insertions(+), 95 deletions(-) create mode 100644 daemon/bankmanager.cpp create mode 100644 daemon/bankmanager.h (limited to 'daemon/manager.h') diff --git a/daemon/bankmanager.cpp b/daemon/bankmanager.cpp new file mode 100644 index 0000000..194ec77 --- /dev/null +++ b/daemon/bankmanager.cpp @@ -0,0 +1,233 @@ +#include "unpacker.h" +#include "packer.h" +#include "bankmanager.h" + +BankManager::BankManager(WatchConnector *watch, AppManager *apps, QObject *parent) : + QObject(parent), watch(watch), apps(apps) +{ + connect(watch, &WatchConnector::connectedChanged, this, &BankManager::handleWatchConnected); +} + +int BankManager::numSlots() const +{ + return _slots.size(); +} + +bool BankManager::uploadApp(const QUuid &uuid, int slot) +{ + AppInfo info = apps->info(uuid); + if (info.uuid() != uuid) { + logger()->warn() << "uuid" << uuid << "is not installed"; + return false; + } + if (slot == -1) { + slot = findUnusedSlot(); + if (slot == -1) { + logger()->warn() << "no free slots!"; + return false; + } + } + if (slot < 0 || slot > _slots.size()) { + logger()->warn() << "invalid slot index"; + return false; + } + + // TODO + + return false; +} + +bool BankManager::unloadApp(int slot) +{ + if (slot < 0 || slot > _slots.size()) { + logger()->warn() << "invalid slot index"; + return false; + } + if (!_slots[slot].used) { + logger()->warn() << "slot is empty"; + return false; + } + + logger()->debug() << "going to unload app" << _slots[slot].name << "in slot" << slot; + + int installId = _slots[slot].id; + + QByteArray msg; + msg.reserve(2 * sizeof(quint32)); + Packer p(&msg); + p.write(WatchConnector::appmgrREMOVE_APP); + p.write(installId); + p.write(slot); + + watch->sendMessage(WatchConnector::watchAPP_MANAGER, msg, + [this](const QByteArray &data) { + Unpacker u(data); + if (u.read() != WatchConnector::appmgrREMOVE_APP) { + return false; + } + + uint result = u.read(); + switch (result) { + case 1: /* Success */ + logger()->debug() << "sucessfully unloaded app"; + break; + default: + logger()->warn() << "could not unload app. result code:" << result; + break; + } + + QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + + return true; + }); + + return true; // Operation in progress +} + +void BankManager::refresh() +{ + logger()->debug() << "refreshing bank status"; + + watch->sendMessage(WatchConnector::watchAPP_MANAGER, + QByteArray(1, WatchConnector::appmgrGET_APPBANK_STATUS), + [this](const QByteArray &data) { + if (data.at(0) != WatchConnector::appmgrGET_APPBANK_STATUS) { + return false; + } + + if (data.size() < 9) { + logger()->warn() << "invalid getAppbankStatus response"; + return true; + } + + Unpacker u(data); + + u.skip(sizeof(quint8)); + + unsigned int num_banks = u.read(); + unsigned int apps_installed = u.read(); + + logger()->debug() << "Bank status:" << apps_installed << "/" << num_banks; + + _slots.resize(num_banks); + for (unsigned int i = 0; i < num_banks; i++) { + _slots[i].used = false; + _slots[i].id = 0; + _slots[i].name.clear(); + _slots[i].company.clear(); + _slots[i].flags = 0; + _slots[i].version = 0; + _slots[i].uuid = QUuid(); + } + + for (unsigned int i = 0; i < apps_installed; i++) { + unsigned int id = u.read(); + int index = u.read(); + QString name = u.readFixedString(32); + QString company = u.readFixedString(32); + unsigned int flags = u.read(); + unsigned short version = u.read(); + + if (index < 0 || index >= _slots.size()) { + logger()->warn() << "Invalid slot index" << index; + continue; + } + + if (u.bad()) { + logger()->warn() << "short read"; + return true; + } + + _slots[index].used = true; + _slots[index].id = id; + _slots[index].name = name; + _slots[index].company = company; + _slots[index].flags = flags; + _slots[index].version = version; + + AppInfo info = apps->info(name); + QUuid uuid = info.uuid(); + _slots[index].uuid = uuid; + + logger()->debug() << index << id << name << company << flags << version << uuid; + } + + emit this->slotsChanged(); + + return true; + }); +} + +int BankManager::findUnusedSlot() const +{ + for (int i = 0; i < _slots.size(); ++i) { + if (!_slots[i].used) { + return i; + } + } + + return -1; +} + +void BankManager::handleWatchConnected() +{ + if (watch->isConnected()) { + refresh(); + } +} + +#if 0 + +void WatchConnector::getAppbankStatus(const std::function& callback) +{ + sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_STATUS), + [this, callback](const QByteArray &data) { + + }); +} + +void WatchConnector::getAppbankUuids(const function &)>& callback) +{ + sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_UUIDS), + [this, callback](const QByteArray &data) { + if (data.at(0) != appmgrGET_APPBANK_UUIDS) { + return false; + } + logger()->debug() << "getAppbankUuids response" << data.toHex(); + + if (data.size() < 5) { + logger()->warn() << "invalid getAppbankUuids response"; + return true; + } + + Unpacker u(data); + + u.skip(sizeof(quint8)); + + unsigned int apps_installed = u.read(); + + logger()->debug() << apps_installed; + + QList uuids; + + for (unsigned int i = 0; i < apps_installed; i++) { + QUuid uuid = u.readUuid(); + + logger()->debug() << uuid.toString(); + + if (u.bad()) { + logger()->warn() << "short read"; + return true; + } + + uuids.push_back(uuid); + } + + logger()->debug() << "finished"; + + callback(uuids); + + return true; + }); +} +#endif diff --git a/daemon/bankmanager.h b/daemon/bankmanager.h new file mode 100644 index 0000000..28729b9 --- /dev/null +++ b/daemon/bankmanager.h @@ -0,0 +1,51 @@ +#ifndef BANKMANAGER_H +#define BANKMANAGER_H + +#include "watchconnector.h" +#include "appmanager.h" + +class BankManager : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + +public: + explicit BankManager(WatchConnector *watch, AppManager *apps, QObject *parent = 0); + + int numSlots() const; + + +signals: + void slotsChanged(); + +public slots: + bool uploadApp(const QUuid &uuid, int slot = -1); + bool unloadApp(int slot); + + void refresh(); + +private: + int findUnusedSlot() const; + + +private slots: + void handleWatchConnected(); + +private: + WatchConnector *watch; + AppManager *apps; + + struct SlotInfo { + bool used; + quint32 id; + QString name; + QString company; + quint32 flags; + quint16 version; + QUuid uuid; + }; + + QVector _slots; +}; + +#endif // BANKMANAGER_H diff --git a/daemon/daemon.pro b/daemon/daemon.pro index 81570c7..21ebffa 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -28,7 +28,8 @@ SOURCES += \ jskitmanager.cpp \ appinfo.cpp \ jskitobjects.cpp \ - packer.cpp + packer.cpp \ + bankmanager.cpp HEADERS += \ manager.h \ @@ -46,7 +47,8 @@ HEADERS += \ jskitmanager.h \ appinfo.h \ jskitobjects.h \ - packer.h + packer.h \ + bankmanager.h OTHER_FILES += \ ../log4qt-debug.conf \ diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 8a00373..27bb870 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -11,6 +11,7 @@ Manager::Manager(Settings *settings, QObject *parent) : watch(new WatchConnector(this)), dbus(new DBusConnector(this)), apps(new AppManager(this)), + bank(new BankManager(watch, apps, this)), voice(new VoiceCallManager(settings, this)), notifications(new NotificationManager(settings, this)), music(new MusicManager(watch, this)), @@ -498,3 +499,14 @@ void PebbledProxy::SendAppConfigurationData(const QString &uuid, const QString & manager()->js->handleWebviewClosed(data); } + +void PebbledProxy::UnloadApp(uint slot) +{ + Q_ASSERT(calledFromDBus()); + const QDBusMessage msg = message(); + + if (!manager()->bank->unloadApp(slot)) { + sendErrorReply(msg.interface() + ".Error.CannotUnload", + "Cannot unload application"); + } +} diff --git a/daemon/manager.h b/daemon/manager.h index 9347289..0e55190 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -10,6 +10,7 @@ #include "appmsgmanager.h" #include "jskitmanager.h" #include "appmanager.h" +#include "bankmanager.h" #include "settings.h" #include @@ -45,6 +46,7 @@ class Manager : public QObject, protected QDBusContext WatchConnector *watch; DBusConnector *dbus; AppManager *apps; + BankManager *bank; VoiceCallManager *voice; NotificationManager *notifications; MusicManager *music; @@ -143,6 +145,8 @@ public slots: QString StartAppConfiguration(const QString &uuid); void SendAppConfigurationData(const QString &uuid, const QString &data); + void UnloadApp(uint slot); + signals: void NameChanged(); void AddressChanged(); diff --git a/daemon/watchconnector.cpp b/daemon/watchconnector.cpp index baec52c..21f5ad5 100644 --- a/daemon/watchconnector.cpp +++ b/daemon/watchconnector.cpp @@ -451,93 +451,3 @@ void WatchConnector::endPhoneCall(uint cookie) { phoneControl(callEND, cookie, QStringList()); } - -void WatchConnector::getAppbankStatus(const std::function& callback) -{ - sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_STATUS), - [this, callback](const QByteArray &data) { - if (data.at(0) != appmgrGET_APPBANK_STATUS) { - return false; - } - logger()->debug() << "getAppbankStatus response" << data.toHex(); - - if (data.size() < 9) { - logger()->warn() << "invalid getAppbankStatus response"; - return true; - } - - Unpacker u(data); - - u.skip(sizeof(quint8)); - - unsigned int num_banks = u.read(); - unsigned int apps_installed = u.read(); - - logger()->debug() << num_banks << "/" << apps_installed; - - for (unsigned int i = 0; i < apps_installed; i++) { - unsigned int id = u.read(); - unsigned int index = u.read(); - QString name = u.readFixedString(32); - QString company = u.readFixedString(32); - unsigned int flags = u.read(); - unsigned short version = u.read(); - - logger()->debug() << id << index << name << company << flags << version; - - if (u.bad()) { - logger()->warn() << "short read"; - return true; - } - } - - logger()->debug() << "finished"; - - return true; - }); -} - -void WatchConnector::getAppbankUuids(const function &)>& callback) -{ - sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_UUIDS), - [this, callback](const QByteArray &data) { - if (data.at(0) != appmgrGET_APPBANK_UUIDS) { - return false; - } - logger()->debug() << "getAppbankUuids response" << data.toHex(); - - if (data.size() < 5) { - logger()->warn() << "invalid getAppbankUuids response"; - return true; - } - - Unpacker u(data); - - u.skip(sizeof(quint8)); - - unsigned int apps_installed = u.read(); - - logger()->debug() << apps_installed; - - QList uuids; - - for (unsigned int i = 0; i < apps_installed; i++) { - QUuid uuid = u.readUuid(); - - logger()->debug() << uuid.toString(); - - if (u.bad()) { - logger()->warn() << "short read"; - return true; - } - - uuids.push_back(uuid); - } - - logger()->debug() << "finished"; - - callback(uuids); - - return true; - }); -} diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index 6c7fdd4..a5fe1ea 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -107,6 +107,7 @@ public: }; enum AppManager { appmgrGET_APPBANK_STATUS = 1, + appmgrREMOVE_APP = 2, appmgrGET_APPBANK_UUIDS = 5 }; enum AppMessage { @@ -179,9 +180,6 @@ public: static QString timeStamp(); static QString decodeEndpoint(uint val); - void getAppbankStatus(const std::function& callback); - void getAppbankUuids(const std::function &uuids)>& callback); - signals: void messageReceived(uint endpoint, const QByteArray &data); void nameChanged(); diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml index 87a22b9..ce71248 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -49,5 +49,10 @@ + + + + + -- cgit v1.2.3 From a60c1cb3c4afd6dfd305115ec4c52e993172fa7d Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 7 Dec 2014 23:39:29 +0100 Subject: ability to upload apps --- daemon/bankmanager.cpp | 134 +++++++++++++++++++++--- daemon/bankmanager.h | 14 ++- daemon/daemon.pro | 8 +- daemon/manager.cpp | 14 ++- daemon/manager.h | 3 + daemon/stm32crc.cpp | 119 +++++++++++++++++++++ daemon/stm32crc.h | 24 +++++ daemon/uploadmanager.cpp | 266 +++++++++++++++++++++++++++++++++++++++++++++++ daemon/uploadmanager.h | 65 ++++++++++++ daemon/watchconnector.h | 16 ++- org.pebbled.Watch.xml | 4 + 11 files changed, 643 insertions(+), 24 deletions(-) create mode 100644 daemon/stm32crc.cpp create mode 100644 daemon/stm32crc.h create mode 100644 daemon/uploadmanager.cpp create mode 100644 daemon/uploadmanager.h (limited to 'daemon/manager.h') diff --git a/daemon/bankmanager.cpp b/daemon/bankmanager.cpp index 194ec77..fe5dc21 100644 --- a/daemon/bankmanager.cpp +++ b/daemon/bankmanager.cpp @@ -1,11 +1,19 @@ +#include +#include #include "unpacker.h" #include "packer.h" #include "bankmanager.h" -BankManager::BankManager(WatchConnector *watch, AppManager *apps, QObject *parent) : - QObject(parent), watch(watch), apps(apps) +BankManager::BankManager(WatchConnector *watch, UploadManager *upload, AppManager *apps, QObject *parent) : + QObject(parent), watch(watch), upload(upload), apps(apps), _refresh(new QTimer(this)) { - connect(watch, &WatchConnector::connectedChanged, this, &BankManager::handleWatchConnected); + connect(watch, &WatchConnector::connectedChanged, + this, &BankManager::handleWatchConnected); + + _refresh->setInterval(0); + _refresh->setSingleShot(true); + connect(_refresh, &QTimer::timeout, + this, &BankManager::refresh); } int BankManager::numSlots() const @@ -32,9 +40,82 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot) return false; } - // TODO + QDir appDir(info.path()); + + logger()->debug() << "about to install app from" << appDir.absolutePath() << "into slot" << slot; + + QFile *binaryFile = new QFile(appDir.absoluteFilePath("pebble-app.bin"), this); + if (!binaryFile->open(QIODevice::ReadOnly)) { + logger()->warn() << "failed to open" << binaryFile->fileName() << ":" << binaryFile->errorString(); + delete binaryFile; + return false; + } + + logger()->debug() << "binary file size is" << binaryFile->size(); + + QFile *resourceFile = 0; + if (appDir.exists("app_resources.pbpack")) { + resourceFile = new QFile(appDir.absoluteFilePath("app_resources.pbpack"), this); + if (!resourceFile->open(QIODevice::ReadOnly)) { + logger()->warn() << "failed to open" << resourceFile->fileName() << ":" << resourceFile->errorString(); + delete resourceFile; + return false; + } + } + + // Mark the slot as used, but without any app, just in case. + _slots[slot].used = true; + _slots[slot].name.clear(); + _slots[slot].uuid = QUuid(); + + upload->upload(WatchConnector::uploadBINARY, slot, binaryFile, -1, + [this, binaryFile, resourceFile, slot]() { + logger()->debug() << "app binary upload succesful"; + delete binaryFile; + + // Proceed to upload the resource file + if (resourceFile) { + upload->upload(WatchConnector::uploadRESOURCES, slot, resourceFile, -1, + [this, resourceFile, slot]() { + logger()->debug() << "app resources upload succesful"; + delete resourceFile; + + // Upload succesful + // Tell the watch to reload the slot + refreshWatchApp(slot, [this]() { + logger()->debug() << "app refresh succesful"; + _refresh->start(); + }, [this](int code) { + logger()->warn() << "app refresh failed" << code; + _refresh->start(); + }); + }, [this, resourceFile](int code) { + logger()->warn() << "app resources upload failed" << code; + delete resourceFile; + + _refresh->start(); + }); + + } else { + // No resource file + // Tell the watch to reload the slot + refreshWatchApp(slot, [this]() { + logger()->debug() << "app refresh succesful"; + _refresh->start(); + }, [this](int code) { + logger()->warn() << "app refresh failed" << code; + _refresh->start(); + }); + } + }, [this, binaryFile, resourceFile](int code) { + logger()->warn() << "app binary upload failed" << code; + delete binaryFile; + delete resourceFile; + + _refresh->start(); + }); - return false; + return true; } bool BankManager::unloadApp(int slot) @@ -76,7 +157,7 @@ bool BankManager::unloadApp(int slot) break; } - QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + _refresh->start(); return true; }); @@ -169,24 +250,43 @@ int BankManager::findUnusedSlot() const return -1; } +void BankManager::refreshWatchApp(int slot, std::function successCallback, std::function errorCallback) +{ + QByteArray msg; + Packer p(&msg); + p.write(WatchConnector::appmgrREFRESH_APP); + p.write(slot); + + watch->sendMessage(WatchConnector::watchAPP_MANAGER, msg, + [this, successCallback, errorCallback](const QByteArray &data) { + Unpacker u(data); + if (u.read() != WatchConnector::appmgrREFRESH_APP) { + return false; + } + int code = u.read(); + if (code == Success) { + if (successCallback) { + successCallback(); + } + } else { + if (errorCallback) { + errorCallback(code); + } + } + + return true; + }); +} + void BankManager::handleWatchConnected() { if (watch->isConnected()) { - refresh(); + _refresh->start(); } } #if 0 - -void WatchConnector::getAppbankStatus(const std::function& callback) -{ - sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_STATUS), - [this, callback](const QByteArray &data) { - - }); -} - -void WatchConnector::getAppbankUuids(const function &)>& callback) +void BankManager::getAppbankUuids(const function &)>& callback) { sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_UUIDS), [this, callback](const QByteArray &data) { diff --git a/daemon/bankmanager.h b/daemon/bankmanager.h index 28729b9..6abedc8 100644 --- a/daemon/bankmanager.h +++ b/daemon/bankmanager.h @@ -2,6 +2,7 @@ #define BANKMANAGER_H #include "watchconnector.h" +#include "uploadmanager.h" #include "appmanager.h" class BankManager : public QObject @@ -10,11 +11,10 @@ class BankManager : public QObject LOG4QT_DECLARE_QCLASS_LOGGER public: - explicit BankManager(WatchConnector *watch, AppManager *apps, QObject *parent = 0); + explicit BankManager(WatchConnector *watch, UploadManager *upload, AppManager *apps, QObject *parent = 0); int numSlots() const; - signals: void slotsChanged(); @@ -26,6 +26,7 @@ public slots: private: int findUnusedSlot() const; + void refreshWatchApp(int slot, std::function successCallback, std::function errorCallback); private slots: @@ -33,8 +34,16 @@ private slots: private: WatchConnector *watch; + UploadManager *upload; AppManager *apps; + enum ResultCodes { + Success = 1, + BankInUse = 2, + InvalidCommand = 3, + GeneralFailure = 4 + }; + struct SlotInfo { bool used; quint32 id; @@ -46,6 +55,7 @@ private: }; QVector _slots; + QTimer *_refresh; }; #endif // BANKMANAGER_H diff --git a/daemon/daemon.pro b/daemon/daemon.pro index 21ebffa..6eea288 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -29,7 +29,9 @@ SOURCES += \ appinfo.cpp \ jskitobjects.cpp \ packer.cpp \ - bankmanager.cpp + bankmanager.cpp \ + uploadmanager.cpp \ + stm32crc.cpp HEADERS += \ manager.h \ @@ -48,7 +50,9 @@ HEADERS += \ appinfo.h \ jskitobjects.h \ packer.h \ - bankmanager.h + bankmanager.h \ + uploadmanager.h \ + stm32crc.h OTHER_FILES += \ ../log4qt-debug.conf \ diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 27bb870..136d7f3 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -10,8 +10,9 @@ Manager::Manager(Settings *settings, QObject *parent) : proxy(new PebbledProxy(this)), watch(new WatchConnector(this)), dbus(new DBusConnector(this)), + upload(new UploadManager(watch, this)), apps(new AppManager(this)), - bank(new BankManager(watch, apps, this)), + bank(new BankManager(watch, upload, apps, this)), voice(new VoiceCallManager(settings, this)), notifications(new NotificationManager(settings, this)), music(new MusicManager(watch, this)), @@ -510,3 +511,14 @@ void PebbledProxy::UnloadApp(uint slot) "Cannot unload application"); } } + +void PebbledProxy::UploadApp(const QString &uuid, uint slot) +{ + Q_ASSERT(calledFromDBus()); + const QDBusMessage msg = message(); + + if (!manager()->bank->uploadApp(QUuid(uuid), slot)) { + sendErrorReply(msg.interface() + ".Error.CannotUpload", + "Cannot upload application"); + } +} diff --git a/daemon/manager.h b/daemon/manager.h index 0e55190..f27da98 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -3,6 +3,7 @@ #include "watchconnector.h" #include "dbusconnector.h" +#include "uploadmanager.h" #include "voicecallmanager.h" #include "notificationmanager.h" #include "musicmanager.h" @@ -45,6 +46,7 @@ class Manager : public QObject, protected QDBusContext WatchConnector *watch; DBusConnector *dbus; + UploadManager *upload; AppManager *apps; BankManager *bank; VoiceCallManager *voice; @@ -146,6 +148,7 @@ public slots: void SendAppConfigurationData(const QString &uuid, const QString &data); void UnloadApp(uint slot); + void UploadApp(const QString &uuid, uint slot); signals: void NameChanged(); diff --git a/daemon/stm32crc.cpp b/daemon/stm32crc.cpp new file mode 100644 index 0000000..dd09f38 --- /dev/null +++ b/daemon/stm32crc.cpp @@ -0,0 +1,119 @@ +#include "stm32crc.h" + +/** Precomputed CRC polynomial + * Generated by pycrc v0.8.2, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 32 + * Poly = 0x04c11db7 + * XorIn = 0xffffffff + * ReflectIn = False + * XorOut = 0xffffffff + * ReflectOut = False + * Algorithm = table-driven + * Modified to use STM32-like word size + *****************************************************************************/ +static const quint32 crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +Stm32Crc::Stm32Crc() +{ + reset(); +} + +void Stm32Crc::reset() +{ + crc = 0xFFFFFFFFU; +} + +void Stm32Crc::addData(const char *data, int length) +{ + const int word_len = sizeof(quint32); + int i = 0; + + Q_ASSERT(length % word_len == 0); + Q_ASSERT(quintptr(data) % word_len == 0); + + for (; i < (length/word_len)*word_len; i+=word_len) { + const quint32 *word = reinterpret_cast(&data[i]); + + crc ^= *word; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + } +} + +void Stm32Crc::addData(const QByteArray &data) +{ + addData(data.constData(), data.length()); +} + +quint32 Stm32Crc::result() const +{ + return crc; +} diff --git a/daemon/stm32crc.h b/daemon/stm32crc.h new file mode 100644 index 0000000..1361de3 --- /dev/null +++ b/daemon/stm32crc.h @@ -0,0 +1,24 @@ +#ifndef STM32CRC_H +#define STM32CRC_H + +#include + +class Stm32Crc +{ +public: + Stm32Crc(); + + void reset(); + + // Data size must be multiple of 4, data must be aligned to 4. + + void addData(const char *data, int length); + void addData(const QByteArray &data); + + quint32 result() const; + +private: + quint32 crc; +}; + +#endif // STM32CRC_H diff --git a/daemon/uploadmanager.cpp b/daemon/uploadmanager.cpp new file mode 100644 index 0000000..89d70f7 --- /dev/null +++ b/daemon/uploadmanager.cpp @@ -0,0 +1,266 @@ +#include "uploadmanager.h" +#include "unpacker.h" +#include "packer.h" +#include "stm32crc.h" + +static const int CHUNK_SIZE = 2000; +using std::function; + +UploadManager::UploadManager(WatchConnector *watch, QObject *parent) : + QObject(parent), watch(watch), _lastUploadId(0), _state(StateNotStarted) +{ + watch->setEndpointHandler(WatchConnector::watchPUTBYTES, + [this](const QByteArray &msg) { + if (_pending.empty()) { + logger()->warn() << "putbytes message, but queue is empty!"; + return false; + } + handleMessage(msg); + return true; + }); +} + +uint UploadManager::upload(WatchConnector::UploadType type, int index, QIODevice *device, int size, + function successCallback, function errorCallback) +{ + PendingUpload upload; + upload.id = ++_lastUploadId; + upload.type = type; + upload.index = index; + upload.device = device; + if (size < 0) { + upload.remaining = device->size(); + } else { + upload.remaining = size; + } + upload.successCallback = successCallback; + upload.errorCallback = errorCallback; + + if (upload.remaining <= 0) { + logger()->warn() << "upload is empty"; + if (errorCallback) { + errorCallback(-1); + return -1; + } + } + + _pending.enqueue(upload); + + if (_pending.size() == 1) { + startNextUpload(); + } + + return upload.id; +} + +void UploadManager::cancel(uint id, int code) +{ + if (_pending.empty()) { + logger()->warn() << "cannot cancel, empty queue"; + return; + } + + if (id == _pending.head().id) { + PendingUpload upload = _pending.dequeue(); + logger()->debug() << "aborting current upload" << id << "(code:" << code << ")"; + + if (_state != StateNotStarted && _state != StateWaitForToken && _state != StateComplete) { + QByteArray msg; + Packer p(&msg); + p.write(WatchConnector::putbytesABORT); + p.write(_token); + + logger()->debug() << "sending abort for upload" << id; + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + } + + _state = StateNotStarted; + _token = 0; + + if (upload.errorCallback) { + upload.errorCallback(code); + } + + if (!_pending.empty()) { + startNextUpload(); + } + } else { + for (int i = 1; i < _pending.size(); ++i) { + if (_pending[i].id == id) { + logger()->debug() << "cancelling upload" << id << "(code:" << code << ")"; + if (_pending[i].errorCallback) { + _pending[i].errorCallback(code); + } + _pending.removeAt(i); + return; + } + } + logger()->warn() << "cannot cancel, id" << id << "not found"; + } +} + +void UploadManager::startNextUpload() +{ + Q_ASSERT(!_pending.empty()); + Q_ASSERT(_state == StateNotStarted); + + PendingUpload &upload = _pending.head(); + QByteArray msg; + Packer p(&msg); + p.write(WatchConnector::putbytesINIT); + p.write(upload.remaining); + p.write(upload.type); + p.write(upload.index); + + _state = StateWaitForToken; + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); +} + +void UploadManager::handleMessage(const QByteArray &msg) +{ + Q_ASSERT(!_pending.empty()); + PendingUpload &upload = _pending.head(); + + logger()->debug() << "get message" << msg.toHex(); + + Unpacker u(msg); + int status = u.read(); + + if (u.bad() || status != 1) { + logger()->warn() << "upload" << upload.id << "got error code=" << status; + cancel(upload.id, status); + return; + } + + quint32 recv_token = u.read(); + + if (u.bad()) { + logger()->warn() << "upload" << upload.id << ": could not read the token"; + cancel(upload.id, -1); + return; + } + + if (_state != StateNotStarted && _state != StateWaitForToken && _state != StateComplete) { + if (recv_token != _token) { + logger()->warn() << "upload" << upload.id << ": invalid token"; + cancel(upload.id, -1); + return; + } + } + + switch (_state) { + case StateNotStarted: + logger()->warn() << "got packet when upload is not started"; + break; + case StateWaitForToken: + logger()->debug() << "token received"; + _token = recv_token; + _state = StateInProgress; + + /* fallthrough */ + case StateInProgress: + logger()->debug() << "moving to the next chunk"; + if (upload.remaining > 0) { + if (!uploadNextChunk(upload)) { + cancel(upload.id, -1); + return; + } + } else { + logger()->debug() << "no additional chunks, commit"; + _state = StateCommit; + if (!commit(upload)) { + cancel(upload.id, -1); + return; + } + } + break; + case StateCommit: + logger()->debug() << "commited succesfully"; + _state = StateComplete; + if (!complete(upload)) { + cancel(upload.id, -1); + return; + } + break; + case StateComplete: + logger()->debug() << "upload" << upload.id << "succesful, invoking callback"; + if (upload.successCallback) { + upload.successCallback(); + } + _pending.dequeue(); + _token = 0; + _state = StateNotStarted; + if (!_pending.empty()) { + startNextUpload(); + } + break; + } +} + +bool UploadManager::uploadNextChunk(PendingUpload &upload) +{ + QByteArray chunk = upload.device->read(qMin(upload.remaining, CHUNK_SIZE)); + + if (upload.remaining < CHUNK_SIZE && chunk.size() < upload.remaining) { + // Short read! + logger()->warn() << "short read during upload" << upload.id; + return false; + } + + Q_ASSERT(!chunk.isEmpty()); + Q_ASSERT(_state = StateInProgress); + + QByteArray msg; + Packer p(&msg); + p.write(WatchConnector::putbytesSEND); + p.write(_token); + p.write(chunk.size()); + msg.append(chunk); + + logger()->debug() << "sending a chunk of" << chunk.size() << "bytes"; + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + + upload.remaining -= chunk.size(); + upload.crc.addData(chunk); + + logger()->debug() << "remaining" << upload.remaining << "bytes"; + + return true; +} + +bool UploadManager::commit(PendingUpload &upload) +{ + Q_ASSERT(_state == StateCommit); + Q_ASSERT(upload.remaining == 0); + + QByteArray msg; + Packer p(&msg); + p.write(WatchConnector::putbytesCOMMIT); + p.write(_token); + p.write(upload.crc.result()); + + logger()->debug() << "commiting upload" << upload.id + << "with crc" << qPrintable(QString("0x%1").arg(upload.crc.result(), 0, 16)); + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + + return true; +} + +bool UploadManager::complete(PendingUpload &upload) +{ + Q_ASSERT(_state == StateComplete); + + QByteArray msg; + Packer p(&msg); + p.write(WatchConnector::putbytesCOMPLETE); + p.write(_token); + + logger()->debug() << "completing upload" << upload.id; + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + + return true; +} diff --git a/daemon/uploadmanager.h b/daemon/uploadmanager.h new file mode 100644 index 0000000..1d42237 --- /dev/null +++ b/daemon/uploadmanager.h @@ -0,0 +1,65 @@ +#ifndef UPLOADMANAGER_H +#define UPLOADMANAGER_H + +#include +#include +#include "watchconnector.h" +#include "stm32crc.h" + +class UploadManager : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + +public: + explicit UploadManager(WatchConnector *watch, QObject *parent = 0); + + typedef std::function Callback; + + uint upload(WatchConnector::UploadType type, int index, QIODevice *device, int size = -1, + std::function successCallback = std::function(), + std::function errorCallback = std::function()); + void cancel(uint id, int code = 0); + +signals: + +public slots: + + +private: + enum State { + StateNotStarted, + StateWaitForToken, + StateInProgress, + StateCommit, + StateComplete + }; + + struct PendingUpload { + uint id; + + WatchConnector::UploadType type; + int index; + QIODevice *device; + int remaining; + Stm32Crc crc; + + std::function successCallback; + std::function errorCallback; + }; + + void startNextUpload(); + void handleMessage(const QByteArray &msg); + bool uploadNextChunk(PendingUpload &upload); + bool commit(PendingUpload &upload); + bool complete(PendingUpload &upload); + +private: + WatchConnector *watch; + QQueue _pending; + uint _lastUploadId; + State _state; + quint32 _token; +}; + +#endif // UPLOADMANAGER_H diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index a5fe1ea..1aaf39d 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -108,6 +108,7 @@ public: enum AppManager { appmgrGET_APPBANK_STATUS = 1, appmgrREMOVE_APP = 2, + appmgrREFRESH_APP = 3, appmgrGET_APPBANK_UUIDS = 5 }; enum AppMessage { @@ -154,8 +155,19 @@ public: osLINUX = 4, osWINDOWS = 5 }; - enum { - DEFAULT_TIMEOUT_MSECS = 100 + enum UploadType { + uploadFIRMWARE = 1, + uploadRECOVERY = 2, + uploadSYS_RESOURCES = 3, + uploadRESOURCES = 4, + uploadBINARY = 5 + }; + enum PutBytesCommand { + putbytesINIT = 1, + putbytesSEND = 2, + putbytesCOMMIT = 3, + putbytesABORT = 4, + putbytesCOMPLETE = 5 }; typedef QMap Dict; diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml index ce71248..2c5202f 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -54,5 +54,9 @@ + + + + -- cgit v1.2.3 From 4280a9bda38046f702a4151d7b831a3bf46ef169 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 8 Dec 2014 00:16:06 +0100 Subject: add d-bus API to list slot contents and fix other API issues --- daemon/bankmanager.cpp | 14 ++++++++++++++ daemon/bankmanager.h | 4 +++- daemon/manager.cpp | 25 +++++++++++++++++++++++-- daemon/manager.h | 8 ++++++-- daemon/uploadmanager.cpp | 5 +++-- daemon/watchconnector.cpp | 16 +++++++++------- daemon/watchconnector.h | 1 - org.pebbled.Watch.xml | 6 ++++-- 8 files changed, 62 insertions(+), 17 deletions(-) (limited to 'daemon/manager.h') diff --git a/daemon/bankmanager.cpp b/daemon/bankmanager.cpp index fe5dc21..8636e95 100644 --- a/daemon/bankmanager.cpp +++ b/daemon/bankmanager.cpp @@ -21,6 +21,16 @@ int BankManager::numSlots() const return _slots.size(); } +bool BankManager::isUsed(int slot) const +{ + return _slots.at(slot).used; +} + +QUuid BankManager::appAt(int slot) const +{ + return _slots.at(slot).uuid; +} + bool BankManager::uploadApp(const QUuid &uuid, int slot) { AppInfo info = apps->info(uuid); @@ -39,6 +49,10 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot) logger()->warn() << "invalid slot index"; return false; } + if (_slots[slot].used) { + logger()->warn() << "slot in use"; + return false; + } QDir appDir(info.path()); diff --git a/daemon/bankmanager.h b/daemon/bankmanager.h index 6abedc8..871db6b 100644 --- a/daemon/bankmanager.h +++ b/daemon/bankmanager.h @@ -15,6 +15,9 @@ public: int numSlots() const; + bool isUsed(int slot) const; + QUuid appAt(int slot) const; + signals: void slotsChanged(); @@ -28,7 +31,6 @@ private: int findUnusedSlot() const; void refreshWatchApp(int slot, std::function successCallback, std::function errorCallback); - private slots: void handleWatchConnected(); diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 136d7f3..469e92b 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -70,6 +70,7 @@ Manager::Manager(Settings *settings, QObject *parent) : connect(dbus, &DBusConnector::pebbleChanged, proxy, &PebbledProxy::NameChanged); connect(dbus, &DBusConnector::pebbleChanged, proxy, &PebbledProxy::AddressChanged); connect(watch, &WatchConnector::connectedChanged, proxy, &PebbledProxy::ConnectedChanged); + connect(bank, &BankManager::slotsChanged, proxy, &PebbledProxy::AppSlotsChanged); QString currentProfile = getCurrentProfile(); defaultProfile = currentProfile.isEmpty() ? "ambience" : currentProfile; @@ -414,6 +415,26 @@ void Manager::onAppClosed(const QUuid &uuid) emit proxy->AppUuidChanged(); } +QStringList PebbledProxy::AppSlots() const +{ + const int num_slots = manager()->bank->numSlots(); + QStringList l; + l.reserve(num_slots); + + for (int i = 0; i < num_slots; ++i) { + if (manager()->bank->isUsed(i)) { + QUuid uuid = manager()->bank->appAt(i); + l.append(uuid.toString()); + } else { + l.append(QString()); + } + } + + Q_ASSERT(l.size() == num_slots); + + return l; +} + bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data) { Q_ASSERT(calledFromDBus()); @@ -501,7 +522,7 @@ void PebbledProxy::SendAppConfigurationData(const QString &uuid, const QString & manager()->js->handleWebviewClosed(data); } -void PebbledProxy::UnloadApp(uint slot) +void PebbledProxy::UnloadApp(int slot) { Q_ASSERT(calledFromDBus()); const QDBusMessage msg = message(); @@ -512,7 +533,7 @@ void PebbledProxy::UnloadApp(uint slot) } } -void PebbledProxy::UploadApp(const QString &uuid, uint slot) +void PebbledProxy::UploadApp(const QString &uuid, int slot) { Q_ASSERT(calledFromDBus()); const QDBusMessage msg = message(); diff --git a/daemon/manager.h b/daemon/manager.h index f27da98..0588705 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -122,6 +122,7 @@ class PebbledProxy : public QObject, protected QDBusContext Q_PROPERTY(QString Address READ Address NOTIFY AddressChanged) Q_PROPERTY(bool Connected READ Connected NOTIFY ConnectedChanged) Q_PROPERTY(QString AppUuid READ AppUuid NOTIFY AppUuidChanged) + Q_PROPERTY(QStringList AppSlots READ AppSlots NOTIFY AppSlotsChanged) inline Manager* manager() const { return static_cast(parent()); } inline QVariantMap pebble() const { return manager()->dbus->pebble(); } @@ -134,6 +135,8 @@ public: inline bool Connected() const { return manager()->watch->isConnected(); } inline QString AppUuid() const { return manager()->currentAppUuid.toString(); } + QStringList AppSlots() const; + public slots: inline void Disconnect() { manager()->watch->disconnect(); } inline void Reconnect() { manager()->watch->reconnect(); } @@ -147,14 +150,15 @@ public slots: QString StartAppConfiguration(const QString &uuid); void SendAppConfigurationData(const QString &uuid, const QString &data); - void UnloadApp(uint slot); - void UploadApp(const QString &uuid, uint slot); + void UnloadApp(int slot); + void UploadApp(const QString &uuid, int slot); signals: void NameChanged(); void AddressChanged(); void ConnectedChanged(); void AppUuidChanged(); + void AppSlotsChanged(); void AppOpened(const QString &uuid); void AppClosed(const QString &uuid); }; diff --git a/daemon/uploadmanager.cpp b/daemon/uploadmanager.cpp index 89d70f7..ccbf12a 100644 --- a/daemon/uploadmanager.cpp +++ b/daemon/uploadmanager.cpp @@ -122,8 +122,6 @@ void UploadManager::handleMessage(const QByteArray &msg) Q_ASSERT(!_pending.empty()); PendingUpload &upload = _pending.head(); - logger()->debug() << "get message" << msg.toHex(); - Unpacker u(msg); int status = u.read(); @@ -195,6 +193,9 @@ void UploadManager::handleMessage(const QByteArray &msg) startNextUpload(); } break; + default: + logger()->warn() << "received message in wrong state"; + break; } } diff --git a/daemon/watchconnector.cpp b/daemon/watchconnector.cpp index 21f5ad5..e66ec0f 100644 --- a/daemon/watchconnector.cpp +++ b/daemon/watchconnector.cpp @@ -5,6 +5,7 @@ #include "unpacker.h" static const int RECONNECT_TIMEOUT = 500; //ms +static const bool PROTOCOL_DEBUG = false; using std::function; @@ -135,7 +136,7 @@ bool WatchConnector::dispatchMessage(uint endpoint, const QByteArray &data) } logger()->info() << "message to endpoint" << decodeEndpoint(endpoint) << "was not dispatched"; - emit messageReceived(endpoint, data); + logger()->debug() << data.toHex(); return false; } @@ -182,6 +183,7 @@ void WatchConnector::onReadSocket() QByteArray data = socket->read(message_length); logger()->debug() << "received message of length" << message_length << "to endpoint" << decodeEndpoint(endpoint); + if (PROTOCOL_DEBUG) logger()->debug() << data.toHex(); dispatchMessage(endpoint, data); } @@ -225,7 +227,7 @@ void WatchConnector::onDisconnected() reconnectTimer.setInterval(reconnectTimer.interval() + RECONNECT_TIMEOUT); } reconnectTimer.start(); - logger()->debug() << "Will reconnect in" << reconnectTimer.interval() << "ms"; + logger()->debug() << "will reconnect in" << reconnectTimer.interval() << "ms"; } void WatchConnector::onError(QBluetoothSocket::SocketError error) @@ -233,7 +235,7 @@ void WatchConnector::onError(QBluetoothSocket::SocketError error) if (error == QBluetoothSocket::UnknownSocketError) { logger()->info() << error << socket->errorString(); } else { - logger()->error() << "Error connecting Pebble:" << error << socket->errorString(); + logger()->error() << "error connecting Pebble:" << error << socket->errorString(); } } @@ -241,11 +243,11 @@ void WatchConnector::sendData(const QByteArray &data) { writeData.append(data); if (socket == nullptr) { - logger()->debug() << "No socket - reconnecting"; + logger()->debug() << "no socket - reconnecting"; reconnect(); } else if (is_connected) { - logger()->debug() << "Writing" << data.length() << "bytes to socket"; - logger()->debug() << data.toHex(); + logger()->debug() << "writing" << data.length() << "bytes to socket"; + if (PROTOCOL_DEBUG) logger()->debug() << data.toHex(); socket->write(data); } } @@ -253,7 +255,7 @@ void WatchConnector::sendData(const QByteArray &data) void WatchConnector::onBytesWritten(qint64 bytes) { writeData.remove(0, bytes); - logger()->debug() << "Socket written" << bytes << "bytes," << writeData.length() << "left"; + logger()->debug() << "socket written" << bytes << "bytes," << writeData.length() << "left"; } void WatchConnector::sendMessage(uint endpoint, const QByteArray &data, const EndpointHandlerFunc &callback) diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index 1aaf39d..2e89b8c 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -193,7 +193,6 @@ public: static QString decodeEndpoint(uint val); signals: - void messageReceived(uint endpoint, const QByteArray &data); void nameChanged(); void connectedChanged(); diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml index 2c5202f..e076d6c 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -51,12 +51,14 @@ + + - + - + -- cgit v1.2.3 From 132c349727bf8c0a0ce6dd5725e48e5a060d69bb Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 8 Dec 2014 19:51:53 +0100 Subject: fix a crash within the lambda that captures appOpenUrl --- daemon/jskitmanager.h | 7 +++---- daemon/manager.cpp | 26 ++++++++++++++++++-------- daemon/manager.h | 4 +++- 3 files changed, 24 insertions(+), 13 deletions(-) (limited to 'daemon/manager.h') diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h index b42e338..7ffc0ad 100644 --- a/daemon/jskitmanager.h +++ b/daemon/jskitmanager.h @@ -24,14 +24,13 @@ public: static QString describeError(QJSValue error); + void showConfiguration(); + void handleWebviewClosed(const QString &result); + signals: void appNotification(const QUuid &uuid, const QString &title, const QString &body); void appOpenUrl(const QUrl &url); -public slots: - void showConfiguration(); - void handleWebviewClosed(const QString &result); - private slots: void handleAppStarted(const QUuid &uuid); void handleAppStopped(const QUuid &uuid); diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 469e92b..dcf9c16 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -454,14 +454,17 @@ QString PebbledProxy::StartAppConfiguration(const QString &uuid) { Q_ASSERT(calledFromDBus()); const QDBusMessage msg = message(); + QDBusConnection conn = connection(); if (manager()->currentAppUuid != uuid) { + logger()->warn() << "Called StartAppConfiguration but the uuid" << uuid << "is not running"; sendErrorReply(msg.interface() + ".Error.AppNotRunning", "The requested app is not currently opened in the watch"); return QString(); } if (!manager()->js->isJSKitAppRunning()) { + logger()->warn() << "Called StartAppConfiguration but the uuid" << uuid << "is not a JS app"; sendErrorReply(msg.interface() + ".Error.JSNotActive", "The requested app is not a PebbleKit JS application"); return QString(); @@ -474,25 +477,32 @@ QString PebbledProxy::StartAppConfiguration(const QString &uuid) setDelayedReply(true); // Set up a signal handler to catch the appOpenUrl signal. - QMetaObject::Connection c = connect(manager()->js, &JSKitManager::appOpenUrl, - [this,msg,c](const QUrl &url) { - // Workaround: due to a GCC bug we can't capture the uuid parameter, but we can extract + QMetaObject::Connection *c = new QMetaObject::Connection; + *c = connect(manager()->js, &JSKitManager::appOpenUrl, + this, [this,conn,msg,c](const QUrl &url) { + // Workaround: due to a GCC crash we can't capture the uuid parameter, but we can extract // it again from the original message arguments. - QString uuid = msg.arguments().at(0).toString(); + // Suspect GCC bug# is 59195, 61233, or 61321. + // TODO Possibly fixed in 4.9.0 + const QString uuid = msg.arguments().at(0).toString(); + if (manager()->currentAppUuid != uuid) { // App was changed while we were waiting for the script.. QDBusMessage reply = msg.createErrorReply(msg.interface() + ".Error.AppNotRunning", "The requested app is not currently opened in the watch"); - connection().send(reply); + conn.send(reply); } else { QDBusMessage reply = msg.createReply(QVariant::fromValue(url.toString())); - connection().send(reply); + conn.send(reply); } - disconnect(c); + disconnect(*c); + delete c; }); + // TODO: JS script may fail, never call OpenURL, or something like that - // In those cases we may leak the above connection + // In those cases we WILL leak the above connection. + // (at least until the next appOpenURL event comes in) // So we need to also set a timeout or similar. manager()->js->showConfiguration(); diff --git a/daemon/manager.h b/daemon/manager.h index 0588705..ad0428b 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -114,10 +114,12 @@ private slots: /** 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 +// Some of the methods are marked inline so that they may be inlined inside qt_metacall class PebbledProxy : public QObject, protected QDBusContext { Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + Q_PROPERTY(QString Name READ Name NOTIFY NameChanged) Q_PROPERTY(QString Address READ Address NOTIFY AddressChanged) Q_PROPERTY(bool Connected READ Connected NOTIFY ConnectedChanged) -- cgit v1.2.3 From 617c632f245c44151f0e17917f9e158403c444c6 Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 12 Dec 2014 02:33:03 +0100 Subject: move the mpris tracking into musicmanager --- daemon/dbusconnector.cpp | 24 ------- daemon/dbusconnector.h | 11 +--- daemon/manager.cpp | 69 -------------------- daemon/manager.h | 15 ----- daemon/musicmanager.cpp | 159 +++++++++++++++++++++++++++++++++++++++-------- daemon/musicmanager.h | 14 ++++- 6 files changed, 148 insertions(+), 144 deletions(-) (limited to 'daemon/manager.h') diff --git a/daemon/dbusconnector.cpp b/daemon/dbusconnector.cpp index 8bde322..1f3ffc2 100644 --- a/daemon/dbusconnector.cpp +++ b/daemon/dbusconnector.cpp @@ -6,7 +6,6 @@ #include #include #include -#include //dbus-send --system --dest=org.bluez --print-reply / org.bluez.Manager.ListAdapters //dbus-send --system --dest=org.bluez --print-reply $path org.bluez.Adapter.GetProperties @@ -16,17 +15,6 @@ DBusConnector::DBusConnector(QObject *parent) : QObject(parent) { - QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); - - QDBusReply serviceNames = interface->registeredServiceNames(); - if (serviceNames.isValid()) { - dbusServices = serviceNames.value(); - } - else { - logger()->error() << serviceNames.error().message(); - } - connect(interface, SIGNAL(serviceRegistered(const QString &)), SLOT(onServiceRegistered(const QString &))); - connect(interface, SIGNAL(serviceUnregistered(const QString &)), SLOT(onServiceUnregistered(const QString &))); } bool DBusConnector::findPebble() @@ -82,15 +70,3 @@ bool DBusConnector::findPebble() return false; } - -void DBusConnector::onServiceRegistered(const QString &name) -{ - logger()->debug() << "DBus service online:" << name; - if (!dbusServices.contains(name)) dbusServices.append(name); -} - -void DBusConnector::onServiceUnregistered(const QString &name) -{ - logger()->debug() << "DBus service offline:" << name; - if (dbusServices.contains(name)) dbusServices.removeAll(name); -} diff --git a/daemon/dbusconnector.h b/daemon/dbusconnector.h index c24bb9b..7ed3d56 100644 --- a/daemon/dbusconnector.h +++ b/daemon/dbusconnector.h @@ -6,33 +6,26 @@ #include #include +// TODO Remove this. + class DBusConnector : public QObject { Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER Q_PROPERTY(QVariantMap pebble READ pebble NOTIFY pebbleChanged) - Q_PROPERTY(QStringList services READ services NOTIFY servicesChanged) - QVariantMap pebbleProps; - QStringList dbusServices; public: explicit DBusConnector(QObject *parent = 0); QVariantMap pebble() const { return pebbleProps; } - QStringList services() const { return dbusServices; } signals: void pebbleChanged(); - void servicesChanged(); public slots: bool findPebble(); - -protected slots: - void onServiceRegistered(const QString &); - void onServiceUnregistered(const QString &); }; #endif // DBUSCONNECTOR_H diff --git a/daemon/manager.cpp b/daemon/manager.cpp index dcf9c16..73b80e5 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -76,13 +76,6 @@ Manager::Manager(Settings *settings, QObject *parent) : defaultProfile = currentProfile.isEmpty() ? "ambience" : currentProfile; connect(watch, SIGNAL(connectedChanged()), SLOT(applyProfile())); - // Music Control interface - session.connect("", "/org/mpris/MediaPlayer2", - "org.freedesktop.DBus.Properties", "PropertiesChanged", - this, SLOT(onMprisPropertiesChanged(QString,QMap,QStringList))); - - connect(this, SIGNAL(mprisMetadataChanged(QVariantMap)), music, SLOT(onMprisMetadataChanged(QVariantMap))); - // Set BT icon for notification notification.setImage("icon-system-bluetooth-device"); @@ -131,22 +124,6 @@ void Manager::onConnectedChanged() if (!notification.publish()) { logger()->debug() << "Failed publishing notification"; } - - if (watch->isConnected()) { - QString mpris = this->mpris(); - if (not mpris.isEmpty()) { - QDBusReply Metadata = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") - << "org.mpris.MediaPlayer2.Player" << "Metadata"); - if (Metadata.isValid()) { - setMprisMetadata(Metadata.value().variant().value()); - } - else { - logger()->error() << Metadata.error().message(); - setMprisMetadata(QVariantMap()); - } - } - } } void Manager::onActiveVoiceCallChanged() @@ -279,52 +256,6 @@ void Manager::onEmailNotify(const QString &sender, const QString &data,const QSt watch->sendEmailNotification(sender, data, subject); } -void Manager::onMprisPropertiesChanged(QString interface, QMap changed, QStringList invalidated) -{ - logger()->debug() << interface << changed << invalidated; - - if (changed.contains("Metadata")) { - setMprisMetadata(changed.value("Metadata").value()); - } - - if (changed.contains("PlaybackStatus")) { - QString PlaybackStatus = changed.value("PlaybackStatus").toString(); - if (PlaybackStatus == "Stopped") { - setMprisMetadata(QVariantMap()); - } - } - - lastSeenMpris = message().service(); - logger()->debug() << "lastSeenMpris:" << lastSeenMpris; -} - -QString Manager::mpris() const -{ - const QStringList &services = dbus->services(); - if (not lastSeenMpris.isEmpty() && services.contains(lastSeenMpris)) - return lastSeenMpris; - - foreach (QString service, services) - if (service.startsWith("org.mpris.MediaPlayer2.")) - return service; - - return QString(); -} - -void Manager::setMprisMetadata(QDBusArgument metadata) -{ - if (metadata.currentType() == QDBusArgument::MapType) { - metadata >> mprisMetadata; - emit mprisMetadataChanged(mprisMetadata); - } -} - -void Manager::setMprisMetadata(QVariantMap metadata) -{ - mprisMetadata = metadata; - emit mprisMetadataChanged(mprisMetadata); -} - QString Manager::getCurrentProfile() const { QDBusReply profile = QDBusConnection::sessionBus().call( diff --git a/daemon/manager.h b/daemon/manager.h index ad0428b..c191b0c 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -35,9 +35,6 @@ class Manager : public QObject, protected QDBusContext friend class PebbledProxy; - Q_PROPERTY(QString mpris READ mpris) - Q_PROPERTY(QVariantMap mprisMetadata READ getMprisMetadata WRITE setMprisMetadata NOTIFY mprisMetadataChanged) - QBluetoothLocalDevice btDevice; Settings *settings; @@ -63,9 +60,6 @@ class Manager : public QObject, protected QDBusContext QString defaultProfile; - QString lastSeenMpris; - QVariantMap mprisMetadata; - QUuid currentAppUuid; QScopedPointer transliterator; @@ -76,16 +70,10 @@ public: QString findPersonByNumber(QString number); QString getCurrentProfile() const; - QString mpris() const; - - inline QVariantMap getMprisMetadata() const { return mprisMetadata; } protected: void transliterateMessage(const QString &text); -signals: - void mprisMetadataChanged(const QVariantMap &metadata); - public slots: void applyProfile(); @@ -102,9 +90,6 @@ private slots: void onTwitterNotify(const QString &sender, const QString &data); void onFacebookNotify(const QString &sender, const QString &data); void onEmailNotify(const QString &sender, const QString &data,const QString &subject); - void onMprisPropertiesChanged(QString,QMap,QStringList); - void setMprisMetadata(QDBusArgument metadata); - void setMprisMetadata(QVariantMap metadata); void onAppNotification(const QUuid &uuid, const QString &title, const QString &body); void onAppOpened(const QUuid &uuid); diff --git a/daemon/musicmanager.cpp b/daemon/musicmanager.cpp index abea715..e018e4c 100644 --- a/daemon/musicmanager.cpp +++ b/daemon/musicmanager.cpp @@ -4,28 +4,45 @@ MusicManager::MusicManager(WatchConnector *watch, QObject *parent) : QObject(parent), watch(watch) { + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusConnectionInterface *bus_iface = bus.interface(); + + // Listen for MPRIS signals from every player + bus.connect("", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged", + this, SLOT(handleMprisPropertiesChanged(QString,QMap,QStringList))); + + // Listen for D-Bus name registered signals to see if a MPRIS service comes up + connect(bus_iface, &QDBusConnectionInterface::serviceRegistered, + this, &MusicManager::handleServiceRegistered); + connect(bus_iface, &QDBusConnectionInterface::serviceUnregistered, + this, &MusicManager::handleServiceUnregistered); + connect(bus_iface, &QDBusConnectionInterface::serviceOwnerChanged, + this, &MusicManager::handleServiceOwnerChanged); + + // But also try to find an already active MPRIS service + const QStringList &services = bus_iface->registeredServiceNames(); + foreach (QString service, services) { + if (service.startsWith("org.mpris.MediaPlayer2.")) { + switchToService(service); + break; + } + } + + // Set up watch endpoint handler watch->setEndpointHandler(WatchConnector::watchMUSIC_CONTROL, [this](const QByteArray& data) { musicControl(WatchConnector::MusicControl(data.at(0))); return true; }); -} - -void MusicManager::onMprisMetadataChanged(QVariantMap metadata) -{ - QString track = metadata.value("xesam:title").toString(); - QString album = metadata.value("xesam:album").toString(); - QString artist = metadata.value("xesam:artist").toString(); - logger()->debug() << __FUNCTION__ << track << album << artist; - watch->sendMusicNowPlaying(track, album, artist); + connect(watch, &WatchConnector::connectedChanged, + this, &MusicManager::handleWatchConnected); } void MusicManager::musicControl(WatchConnector::MusicControl operation) { - logger()->debug() << "Operation:" << operation; + logger()->debug() << "operation from watch:" << operation; - QString mpris = parent()->property("mpris").toString(); - if (mpris.isEmpty()) { - logger()->debug() << "No mpris interface active"; + if (_curService.isEmpty()) { + logger()->info() << "No mpris interface active"; return; } @@ -50,11 +67,11 @@ void MusicManager::musicControl(WatchConnector::MusicControl operation) case WatchConnector::musicVOLUME_UP: case WatchConnector::musicVOLUME_DOWN: { QDBusConnection bus = QDBusConnection::sessionBus(); - QDBusReply VolumeReply = bus.call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") - << "org.mpris.MediaPlayer2.Player" << "Volume"); - if (VolumeReply.isValid()) { - double volume = VolumeReply.value().variant().toDouble(); + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + call << "org.mpris.MediaPlayer2.Player" << "Volume"; + QDBusReply volumeReply = bus.call(call); + if (volumeReply.isValid()) { + double volume = volumeReply.value().variant().toDouble(); if (operation == WatchConnector::musicVOLUME_UP) { volume += 0.1; } @@ -62,22 +79,25 @@ void MusicManager::musicControl(WatchConnector::MusicControl operation) volume -= 0.1; } logger()->debug() << "Setting volume" << volume; - QDBusError err = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set") - << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume))); + + call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Set"); + call << "org.mpris.MediaPlayer2.Player" << "Volume" << QVariant::fromValue(QDBusVariant(volume)); + + QDBusError err = QDBusConnection::sessionBus().call(call); if (err.isValid()) { logger()->error() << err.message(); } } else { - logger()->error() << VolumeReply.error().message(); + logger()->error() << volumeReply.error().message(); } } return; case WatchConnector::musicGET_NOW_PLAYING: - onMprisMetadataChanged(parent()->property("mprisMetadata").toMap()); + setMprisMetadata(_curMetadata); return; case WatchConnector::musicSEND_NOW_PLAYING: + default: logger()->warn() << "Operation" << operation << "not supported"; return; } @@ -89,9 +109,98 @@ void MusicManager::musicControl(WatchConnector::MusicControl operation) logger()->debug() << operation << "->" << method; - QDBusError err = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method)); + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method); + QDBusError err = QDBusConnection::sessionBus().call(call); if (err.isValid()) { logger()->error() << err.message(); } } + +void MusicManager::switchToService(const QString &service) +{ + if (_curService != service) { + logger()->debug() << "switching to mpris service" << service; + _curService = service; + } +} + +void MusicManager::setMprisMetadata(const QVariantMap &metadata) +{ + _curMetadata = metadata; + QString track = metadata.value("xesam:title").toString(); + QString album = metadata.value("xesam:album").toString(); + QString artist = metadata.value("xesam:artist").toString(); + logger()->debug() << "new mpris metadata:" << track << album << artist; + + if (watch->isConnected()) { + watch->sendMusicNowPlaying(track, album, artist); + } +} + +void MusicManager::handleServiceRegistered(const QString &service) +{ + if (service.startsWith("org.mpris.MediaPlayer2.")) { + if (_curService.isEmpty()) { + switchToService(service); + } + } +} + +void MusicManager::handleServiceUnregistered(const QString &service) +{ + if (service == _curService) { + // Oops! + setMprisMetadata(QVariantMap()); + switchToService(QString()); + } +} + +void MusicManager::handleServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) +{ + Q_UNUSED(oldOwner); + if (newOwner.isEmpty()) { + handleServiceUnregistered(name); + } else { + handleServiceRegistered(name); + } +} + +void MusicManager::handleMprisPropertiesChanged(const QString &interface, const QMap &changed, const QStringList &invalidated) +{ + Q_ASSERT(calledFromDBus()); + Q_UNUSED(interface); + Q_UNUSED(invalidated); + + if (changed.contains("Metadata")) { + QVariantMap metadata = qdbus_cast(changed.value("Metadata").value()); + logger()->debug() << "received new metadata" << metadata; + setMprisMetadata(metadata); + } + + if (changed.contains("PlaybackStatus")) { + QString status = changed.value("PlaybackStatus").toString(); + if (status == "Stopped") { + setMprisMetadata(QVariantMap()); + } + } + + switchToService(message().service()); +} + +void MusicManager::handleWatchConnected() +{ + if (watch->isConnected()) { + if (!_curService.isEmpty()) { + QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + call << "org.mpris.MediaPlayer2.Player" << "Metadata"; + QDBusReply metadata = QDBusConnection::sessionBus().call(call); + if (metadata.isValid()) { + setMprisMetadata(qdbus_cast(metadata.value().variant().value())); + // + } else { + logger()->error() << metadata.error().message(); + setMprisMetadata(QVariantMap()); + } + } + } +} diff --git a/daemon/musicmanager.h b/daemon/musicmanager.h index ca86ce3..88c46c3 100644 --- a/daemon/musicmanager.h +++ b/daemon/musicmanager.h @@ -2,9 +2,10 @@ #define MUSICMANAGER_H #include +#include #include "watchconnector.h" -class MusicManager : public QObject +class MusicManager : public QObject, protected QDBusContext { Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER @@ -14,12 +15,21 @@ public: private: void musicControl(WatchConnector::MusicControl operation); + void switchToService(const QString &service); + void setMprisMetadata(const QVariantMap &data); private slots: - void onMprisMetadataChanged(QVariantMap metadata); + void handleServiceRegistered(const QString &service); + void handleServiceUnregistered(const QString &service); + void handleServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void handleMprisPropertiesChanged(const QString &interface, const QMap &changed, const QStringList &invalidated); + void handleWatchConnected(); private: WatchConnector *watch; + + QVariantMap _curMetadata; + QString _curService; }; #endif // MUSICMANAGER_H -- cgit v1.2.3 From 492a861a47c5165cf62a051303d4b45e5d5630cd Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 12 Dec 2014 23:32:46 +0100 Subject: query all apps from d-bus --- daemon/appmanager.cpp | 6 ++++++ daemon/appmanager.h | 4 ++++ daemon/manager.cpp | 20 ++++++++++++++++++++ daemon/manager.h | 4 ++++ org.pebbled.Watch.xml | 6 ++++++ 5 files changed, 40 insertions(+) (limited to 'daemon/manager.h') diff --git a/daemon/appmanager.cpp b/daemon/appmanager.cpp index 2520ba6..10f2e3e 100644 --- a/daemon/appmanager.cpp +++ b/daemon/appmanager.cpp @@ -30,6 +30,11 @@ QStringList AppManager::appPaths() const QStandardPaths::LocateDirectory); } +QList AppManager::appUuids() const +{ + return _apps.keys(); +} + AppInfo AppManager::info(const QUuid &uuid) const { return _apps.value(uuid); @@ -71,6 +76,7 @@ void AppManager::rescan() } logger()->debug() << "now watching" << _watcher->directories() << _watcher->files(); + emit appsChanged(); } void AppManager::scanApp(const QString &path) diff --git a/daemon/appmanager.h b/daemon/appmanager.h index 7458e19..1725c14 100644 --- a/daemon/appmanager.h +++ b/daemon/appmanager.h @@ -17,6 +17,7 @@ public: explicit AppManager(QObject *parent = 0); QStringList appPaths() const; + QList appUuids() const; AppInfo info(const QUuid &uuid) const; AppInfo info(const QString &shortName) const; @@ -24,6 +25,9 @@ public: public slots: void rescan(); +signals: + void appsChanged(); + private: void scanApp(const QString &path); diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 73b80e5..25908e4 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -366,6 +366,26 @@ QStringList PebbledProxy::AppSlots() const return l; } +QVariantList PebbledProxy::AllApps() const +{ + QList uuids = manager()->apps->appUuids(); + QVariantList l; + + foreach (const QUuid &uuid, uuids) { + const AppInfo &info = manager()->apps->info(uuid); + QVariantMap m; + m.insert("uuid", QVariant::fromValue(uuid.toString())); + m.insert("short-name", QVariant::fromValue(info.shortName())); + m.insert("long-name", QVariant::fromValue(info.longName())); + m.insert("company-name", QVariant::fromValue(info.companyName())); + m.insert("version-label", QVariant::fromValue(info.versionLabel())); + m.insert("is-watchface", QVariant::fromValue(info.isWatchface())); + l.append(QVariant::fromValue(m)); + } + + return l; +} + bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data) { Q_ASSERT(calledFromDBus()); diff --git a/daemon/manager.h b/daemon/manager.h index c191b0c..efe9b82 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -110,6 +110,7 @@ class PebbledProxy : public QObject, protected QDBusContext Q_PROPERTY(bool Connected READ Connected NOTIFY ConnectedChanged) Q_PROPERTY(QString AppUuid READ AppUuid NOTIFY AppUuidChanged) Q_PROPERTY(QStringList AppSlots READ AppSlots NOTIFY AppSlotsChanged) + Q_PROPERTY(QVariantList AllApps READ AllApps NOTIFY AllAppsChanged) inline Manager* manager() const { return static_cast(parent()); } inline QVariantMap pebble() const { return manager()->dbus->pebble(); } @@ -124,6 +125,8 @@ public: QStringList AppSlots() const; + QVariantList AllApps() const; + public slots: inline void Disconnect() { manager()->watch->disconnect(); } inline void Reconnect() { manager()->watch->reconnect(); } @@ -146,6 +149,7 @@ signals: void ConnectedChanged(); void AppUuidChanged(); void AppSlotsChanged(); + void AllAppsChanged(); void AppOpened(const QString &uuid); void AppClosed(const QString &uuid); }; diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml index e076d6c..6336d04 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -60,5 +60,11 @@ + + + + + + -- cgit v1.2.3