summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-12-05 22:56:27 +0100
committerJavier <dev.git@javispedro.com>2014-12-05 22:56:27 +0100
commite96c2ee30342b5a198025c3dd0c51bf43688d9ee (patch)
treed8f7e22795054958c5644673f063ad50146521b0 /daemon
parent8722bee52922f8c6707103795fdf69f9ed7d0240 (diff)
partial implementation of geolocation
Diffstat (limited to 'daemon')
-rw-r--r--daemon/jskitmanager.cpp10
-rw-r--r--daemon/jskitmanager.h2
-rw-r--r--daemon/jskitobjects.cpp172
-rw-r--r--daemon/jskitobjects.h20
4 files changed, 175 insertions, 29 deletions
diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp
index 70ea4bd..9c739fc 100644
--- a/daemon/jskitmanager.cpp
+++ b/daemon/jskitmanager.cpp
@@ -29,6 +29,14 @@ bool JSKitManager::isJSKitAppRunning() const
return _engine != 0;
}
+QString JSKitManager::describeError(QJSValue error)
+{
+ return QString("%1:%2: %3")
+ .arg(error.property("fileName").toString())
+ .arg(error.property("lineNumber").toInt())
+ .arg(error.toString());
+}
+
void JSKitManager::showConfiguration()
{
if (_engine) {
@@ -134,7 +142,7 @@ void JSKitManager::startJsApp()
QJSValue result = _engine->evaluate(script, scriptFile.fileName());
if (result.isError()) {
- logger()->warn() << "error while evaluating JSKit script:" << result.toString();
+ logger()->warn() << "error while evaluating JSKit script:" << describeError(result);
}
logger()->debug() << "JS script evaluated";
diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h
index 1f842b7..873489b 100644
--- a/daemon/jskitmanager.h
+++ b/daemon/jskitmanager.h
@@ -22,6 +22,8 @@ public:
QJSEngine * engine();
bool isJSKitAppRunning() const;
+ static QString describeError(QJSValue error);
+
signals:
void appNotification(const QUuid &uuid, const QString &title, const QString &body);
void appOpenUrl(const QUrl &url);
diff --git a/daemon/jskitobjects.cpp b/daemon/jskitobjects.cpp
index 3b4584b..b3820b5 100644
--- a/daemon/jskitobjects.cpp
+++ b/daemon/jskitobjects.cpp
@@ -3,6 +3,7 @@
#include <QUrl>
#include <QBuffer>
#include <QDir>
+#include <limits>
#include "jskitobjects.h"
JSKitPebble::JSKitPebble(const AppInfo &info, JSKitManager *mgr)
@@ -45,7 +46,7 @@ void JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSV
QJSValue result = callbackForAck.call();
if (result.isError()) {
logger()->warn() << "error while invoking ACK callback" << callbackForAck.toString() << ":"
- << result.toString();
+ << JSKitManager::describeError(result);
}
} else {
logger()->debug() << "Ack callback not callable";
@@ -56,7 +57,7 @@ void JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSV
QJSValue result = callbackForNack.call();
if (result.isError()) {
logger()->warn() << "error while invoking NACK callback" << callbackForNack.toString() << ":"
- << result.toString();
+ << JSKitManager::describeError(result);
}
} else {
logger()->debug() << "Nack callback not callable";
@@ -93,7 +94,7 @@ void JSKitPebble::invokeCallbacks(const QString &type, const QJSValueList &args)
QJSValue result = it->call(args);
if (result.isError()) {
logger()->warn() << "error while invoking callback" << type << it->toString() << ":"
- << result.toString();
+ << JSKitManager::describeError(result);
}
}
}
@@ -287,12 +288,11 @@ void JSKitXMLHttpRequest::handleReplyFinished()
emit statusChanged();
emit responseTextChanged();
-
if (_onload.isCallable()) {
logger()->debug() << "going to call onload handler:" << _onload.toString();
QJSValue result = _onload.callWithInstance(_mgr->engine()->newQObject(this));
if (result.isError()) {
- logger()->warn() << "JS error on onload handler:" << result.toString();
+ logger()->warn() << "JS error on onload handler:" << JSKitManager::describeError(result);
}
} else {
logger()->debug() << "No onload set";
@@ -312,7 +312,7 @@ void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code)
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();
+ logger()->warn() << "JS error on onerror handler:" << JSKitManager::describeError(result);
}
}
}
@@ -320,7 +320,6 @@ void JSKitXMLHttpRequest::handleReplyError(QNetworkReply::NetworkError code)
JSKitGeolocation::JSKitGeolocation(JSKitManager *mgr)
: QObject(mgr), _mgr(mgr), _source(0), _lastWatchId(0)
{
-
}
void JSKitGeolocation::getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
@@ -331,28 +330,59 @@ void JSKitGeolocation::getCurrentPosition(const QJSValue &successCallback, const
int JSKitGeolocation::watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options)
{
- logger()->debug() << Q_FUNC_INFO;
return setupWatcher(successCallback, errorCallback, options, false);
}
void JSKitGeolocation::clearWatch(int watchId)
{
- logger()->debug() << Q_FUNC_INFO;
+ removeWatcher(watchId);
}
void JSKitGeolocation::handleError(QGeoPositionInfoSource::Error error)
{
- logger()->debug() << Q_FUNC_INFO;
+ logger()->warn() << "positioning error: " << error;
+ // TODO
}
void JSKitGeolocation::handlePosition(const QGeoPositionInfo &pos)
{
logger()->debug() << Q_FUNC_INFO;
+ if (_watches.empty()) {
+ logger()->warn() << "got position update but no one is watching";
+ }
+
+ QJSValue obj = buildPositionObject(pos);
+
+ for (auto it = _watches.begin(); it != _watches.end(); /*no adv*/) {
+ invokeCallback(it->successCallback, obj);
+
+ if (it->once) {
+ it = _watches.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ if (_watches.empty()) {
+ _source->stopUpdates();
+ }
}
void JSKitGeolocation::handleTimeout()
{
logger()->debug() << Q_FUNC_INFO;
+ // TODO
+}
+
+uint JSKitGeolocation::minimumTimeout() const
+{
+ uint minimum = std::numeric_limits<uint>::max();
+ Q_FOREACH(const Watcher &watcher, _watches) {
+ if (!watcher.once) {
+ minimum = qMin<uint>(watcher.timeout, minimum);
+ }
+ }
+ return minimum;
}
int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once)
@@ -362,10 +392,13 @@ int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSVal
watcher.errorCallback = errorCallback;
watcher.highAccuracy = options.value("enableHighAccuracy").toBool();
watcher.timeout = options.value("timeout", 0xFFFFFFFFU).toUInt();
- watcher.maximumAge = options.value("maximumAge", 0).toUInt();
watcher.once = once;
watcher.watchId = ++_lastWatchId;
+ uint maximumAge = options.value("maximumAge", 0).toUInt();
+
+ logger()->debug() << "setting up watcher, gps=" << watcher.highAccuracy << "timeout=" << watcher.timeout << "maximumAge=" << maximumAge << "once=" << once;
+
if (!_source) {
_source = QGeoPositionInfoSource::createDefaultSource(this);
connect(_source, static_cast<void (QGeoPositionInfoSource::*)(QGeoPositionInfoSource::Error)>(&QGeoPositionInfoSource::error),
@@ -376,14 +409,17 @@ int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSVal
this, &JSKitGeolocation::handleTimeout);
}
- if (once && watcher.maximumAge > 0) {
- QDateTime threshold = QDateTime::currentDateTime().addMSecs(-watcher.maximumAge);
+ if (maximumAge > 0) {
+ QDateTime threshold = QDateTime::currentDateTime().addMSecs(-qint64(maximumAge));
QGeoPositionInfo pos = _source->lastKnownPosition(watcher.highAccuracy);
+ logger()->debug() << "got pos timestamp" << pos.timestamp() << " but we want" << threshold;
if (pos.isValid() && pos.timestamp() >= threshold) {
- invokeSuccessCallback(watcher, pos);
- return -1;
- } else if (watcher.timeout == 0) {
- invokeErrorCallback(watcher);
+ invokeCallback(watcher.successCallback, buildPositionObject(pos));
+ if (once) {
+ return -1;
+ }
+ } else if (watcher.timeout == 0 && once) {
+ invokeCallback(watcher.errorCallback, buildPositionErrorObject(TIMEOUT, "no cached position"));
return -1;
}
}
@@ -391,21 +427,111 @@ int JSKitGeolocation::setupWatcher(const QJSValue &successCallback, const QJSVal
if (once) {
_source->requestUpdate(watcher.timeout);
} else {
- // TODO _source->setInterval to the minimum of all watches
+ uint timeout = minimumTimeout();
+ logger()->debug() << "setting location update interval to" << timeout;
+ _source->setUpdateInterval(timeout);
+ logger()->debug() << "starting location updates";
_source->startUpdates();
}
+ _watches.append(watcher);
+
+ logger()->debug() << "added new watch" << watcher.watchId;
+
return watcher.watchId;
}
-void JSKitGeolocation::invokeSuccessCallback(Watcher &watcher, const QGeoPositionInfo &pos)
+void JSKitGeolocation::removeWatcher(int watchId)
{
- // TODO
+ Watcher watcher;
+
+ logger()->debug() << "removing watchId" << watcher.watchId;
+
+ for (int i = 0; i < _watches.size(); i++) {
+ if (_watches[i].watchId == watchId) {
+ watcher = _watches.takeAt(i);
+ break;
+ }
+ }
+
+ if (watcher.watchId != watchId) {
+ logger()->warn() << "watchId not found";
+ return;
+ }
+
+ if (_watches.empty()) {
+ logger()->debug() << "stopping updates";
+ _source->stopUpdates();
+ } else {
+ uint timeout = minimumTimeout();
+ logger()->debug() << "setting location update interval to" << timeout;
+ _source->setUpdateInterval(timeout);
+ }
}
-void JSKitGeolocation::invokeErrorCallback(Watcher &watcher)
+QJSValue JSKitGeolocation::buildPositionObject(const QGeoPositionInfo &pos)
{
- if (watcher.errorCallback.isCallable()) {
- watcher.errorCallback.call(); // TODO this, eventArgs
+ QJSEngine *engine = _mgr->engine();
+ QJSValue obj = engine->newObject();
+ QJSValue coords = engine->newObject();
+ QJSValue timestamp = engine->toScriptValue<quint64>(pos.timestamp().toMSecsSinceEpoch());
+
+ coords.setProperty("latitude", engine->toScriptValue(pos.coordinate().latitude()));
+ coords.setProperty("longitude", engine->toScriptValue(pos.coordinate().longitude()));
+ if (pos.coordinate().type() == QGeoCoordinate::Coordinate3D) {
+ coords.setProperty("altitude", engine->toScriptValue(pos.coordinate().altitude()));
+ } else {
+ coords.setProperty("altitude", engine->toScriptValue<void*>(0));
+ }
+
+ coords.setProperty("accuracy", engine->toScriptValue(pos.attribute(QGeoPositionInfo::HorizontalAccuracy)));
+
+ if (pos.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
+ coords.setProperty("altitudeAccuracy", engine->toScriptValue(pos.attribute(QGeoPositionInfo::VerticalAccuracy)));
+ } else {
+ coords.setProperty("altitudeAccuracy", engine->toScriptValue<void*>(0));
+ }
+
+ if (pos.hasAttribute(QGeoPositionInfo::Direction)) {
+ coords.setProperty("heading", engine->toScriptValue(pos.attribute(QGeoPositionInfo::Direction)));
+ } else {
+ coords.setProperty("heading", engine->toScriptValue<void*>(0));
+ }
+
+ if (pos.hasAttribute(QGeoPositionInfo::GroundSpeed)) {
+ coords.setProperty("speed", engine->toScriptValue(pos.attribute(QGeoPositionInfo::GroundSpeed)));
+ } else {
+ coords.setProperty("speed", engine->toScriptValue<void*>(0));
+ }
+
+ obj.setProperty("coords", coords);
+ obj.setProperty("timestamp", timestamp);
+
+ logger()->debug() << obj.toString();
+
+ return obj;
+}
+
+QJSValue JSKitGeolocation::buildPositionErrorObject(PositionError error, const QString &message)
+{
+ QJSEngine *engine = _mgr->engine();
+ QJSValue obj = engine->newObject();
+
+ obj.setProperty("code", engine->toScriptValue<unsigned short>(error));
+ obj.setProperty("message", engine->toScriptValue(message));
+
+ return obj;
+}
+
+void JSKitGeolocation::invokeCallback(QJSValue callback, QJSValue event)
+{
+ if (callback.isCallable()) {
+ logger()->debug() << "invoking callback" << callback.toString();
+ QJSValue result = callback.call(QJSValueList({event}));
+ if (result.isError()) {
+ logger()->warn() << "while invoking callback: " << JSKitManager::describeError(result);
+ }
+ } else {
+ logger()->warn() << "callback is not callable";
}
}
diff --git a/daemon/jskitobjects.h b/daemon/jskitobjects.h
index 9c9b84e..0dccc05 100644
--- a/daemon/jskitobjects.h
+++ b/daemon/jskitobjects.h
@@ -98,9 +98,9 @@ public:
DONE = 4
};
- Q_INVOKABLE void open(const QString &method, const QString &url, bool async);
+ Q_INVOKABLE void open(const QString &method, const QString &url, bool async = false);
Q_INVOKABLE void setRequestHeader(const QString &header, const QString &value);
- Q_INVOKABLE void send(const QString &body);
+ Q_INVOKABLE void send(const QString &body = QString());
Q_INVOKABLE void abort();
QJSValue onload() const;
@@ -138,6 +138,7 @@ private:
class JSKitGeolocation : public QObject
{
Q_OBJECT
+ Q_ENUMS(PositionError)
LOG4QT_DECLARE_QCLASS_LOGGER
struct Watcher;
@@ -145,6 +146,12 @@ class JSKitGeolocation : public QObject
public:
explicit JSKitGeolocation(JSKitManager *mgr);
+ enum PositionError {
+ PERMISSION_DENIED = 1,
+ POSITION_UNAVAILABLE = 2,
+ TIMEOUT = 3
+ };
+
Q_INVOKABLE void getCurrentPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
Q_INVOKABLE int watchPosition(const QJSValue &successCallback, const QJSValue &errorCallback = QJSValue(), const QVariantMap &options = QVariantMap());
Q_INVOKABLE void clearWatch(int watchId);
@@ -155,9 +162,13 @@ private slots:
void handleTimeout();
private:
+ uint minimumTimeout() const;
int setupWatcher(const QJSValue &successCallback, const QJSValue &errorCallback, const QVariantMap &options, bool once);
- void invokeSuccessCallback(Watcher &watcher, const QGeoPositionInfo &pos);
- void invokeErrorCallback(Watcher &watcher);
+ void removeWatcher(int watchId);
+ QJSValue buildPositionObject(const QGeoPositionInfo &pos);
+ QJSValue buildPositionErrorObject(PositionError error, const QString &message = QString());
+ QJSValue buildPositionErrorObject(const QGeoPositionInfoSource::Error error);
+ void invokeCallback(QJSValue callback, QJSValue event);
private:
JSKitManager *_mgr;
@@ -170,7 +181,6 @@ private:
bool once;
bool highAccuracy;
uint timeout;
- uint maximumAge;
};
QList<Watcher> _watches;