diff options
Diffstat (limited to 'rockworkd/libpebble/jskit/jskitpebble.cpp')
| -rw-r--r-- | rockworkd/libpebble/jskit/jskitpebble.cpp | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/rockworkd/libpebble/jskit/jskitpebble.cpp b/rockworkd/libpebble/jskit/jskitpebble.cpp new file mode 100644 index 0000000..a300aef --- /dev/null +++ b/rockworkd/libpebble/jskit/jskitpebble.cpp @@ -0,0 +1,355 @@ +#include <QUrl> +#include <QCryptographicHash> +#include <QSettings> + +#include "jskitpebble.h" +#include "jskitxmlhttprequest.h" + +static const char *token_salt = "0feeb7416d3c4546a19b04bccd8419b1"; + +JSKitPebble::JSKitPebble(const AppInfo &info, JSKitManager *mgr, QObject *parent) : + QObject(parent), + l(metaObject()->className()), + m_appInfo(info), + m_mgr(mgr) +{ +} + +void JSKitPebble::addEventListener(const QString &type, QJSValue function) +{ + m_listeners[type].append(function); +} + +void JSKitPebble::removeEventListener(const QString &type, QJSValue function) +{ + if (!m_listeners.contains(type)) return; + + QList<QJSValue> &callbacks = m_listeners[type]; + for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ) { + if (it->strictlyEquals(function)) { + it = callbacks.erase(it); + } else { + ++it; + } + } + + if (callbacks.empty()) { + m_listeners.remove(type); + } +} + +void JSKitPebble::showSimpleNotificationOnPebble(const QString &title, const QString &body) +{ + qCDebug(l) << "showSimpleNotificationOnPebble" << title << body; + emit m_mgr->appNotification(m_appInfo.uuid(), title, body); +} + +uint JSKitPebble::sendAppMessage(QJSValue message, QJSValue callbackForAck, QJSValue callbackForNack) +{ + QVariantMap data = message.toVariant().toMap(); + QPointer<JSKitPebble> pebbObj = this; + uint transactionId = m_mgr->m_appmsg->nextTransactionId(); + + qCDebug(l) << "sendAppMessage" << data; + + m_mgr->m_appmsg->send( + m_appInfo.uuid(), + data, + [this, pebbObj, transactionId, callbackForAck]() mutable { + if (pebbObj.isNull()) return; + + if (callbackForAck.isCallable()) { + QJSValue event = pebbObj->buildAckEventObject(transactionId); + QJSValue result = callbackForAck.call(QJSValueList({event})); + + if (result.isError()) { + qCWarning(l) << "error while invoking ACK callback" + << callbackForAck.toString() << ":" + << JSKitManager::describeError(result); + } + } + }, + [this, pebbObj, transactionId, callbackForNack]() mutable { + if (pebbObj.isNull()) return; + + if (callbackForNack.isCallable()) { + QJSValue event = pebbObj->buildAckEventObject(transactionId, "NACK from watch"); + QJSValue result = callbackForNack.call(QJSValueList({event})); + + if (result.isError()) { + qCWarning(l) << "error while invoking NACK callback" + << callbackForNack.toString() << ":" + << JSKitManager::describeError(result); + } + } + } + ); + + return transactionId; +} + +void JSKitPebble::getTimelineToken(QJSValue successCallback, QJSValue failureCallback) +{ + //TODO actually implement this + qCDebug(l) << "call to unsupported method Pebble.getTimelineToken"; + Q_UNUSED(successCallback); + + if (failureCallback.isCallable()) { + failureCallback.call(); + } +} + +void JSKitPebble::timelineSubscribe(const QString &topic, QJSValue successCallback, QJSValue failureCallback) +{ + //TODO actually implement this + qCDebug(l) << "call to unsupported method Pebble.timelineSubscribe"; + Q_UNUSED(topic); + Q_UNUSED(successCallback); + + if (failureCallback.isCallable()) { + failureCallback.call(); + } +} + +void JSKitPebble::timelineUnsubscribe(const QString &topic, QJSValue successCallback, QJSValue failureCallback) +{ + //TODO actually implement this + qCDebug(l) << "call to unsupported method Pebble.timelineUnsubscribe"; + Q_UNUSED(topic); + Q_UNUSED(successCallback); + + if (failureCallback.isCallable()) { + failureCallback.call(); + } +} + +void JSKitPebble::timelineSubscriptions(QJSValue successCallback, QJSValue failureCallback) +{ + //TODO actually implement this + qCDebug(l) << "call to unsupported method Pebble.timelineSubscriptions"; + Q_UNUSED(successCallback); + + if (failureCallback.isCallable()) { + failureCallback.call(); + } +} + + +QString JSKitPebble::getAccountToken() const +{ + // We do not have any account system, so we just fake something up. + QCryptographicHash hasher(QCryptographicHash::Md5); + + hasher.addData(token_salt, strlen(token_salt)); + hasher.addData(m_appInfo.uuid().toByteArray()); + + QSettings settings; + QString token = settings.value("accountToken").toString(); + + if (token.isEmpty()) { + token = QUuid::createUuid().toString(); + qCDebug(l) << "created new account token" << token; + settings.setValue("accountToken", token); + } + + hasher.addData(token.toLatin1()); + + QString hash = hasher.result().toHex(); + qCDebug(l) << "returning account token" << hash; + + return hash; +} + +QString JSKitPebble::getWatchToken() const +{ + QCryptographicHash hasher(QCryptographicHash::Md5); + + hasher.addData(token_salt, strlen(token_salt)); + hasher.addData(m_appInfo.uuid().toByteArray()); + hasher.addData(m_mgr->m_pebble->serialNumber().toLatin1()); + + QString hash = hasher.result().toHex(); + qCDebug(l) << "returning watch token" << hash; + + return hash; +} + +QJSValue JSKitPebble::getActiveWatchInfo() const +{ + QJSValue watchInfo = m_mgr->m_engine->newObject(); + + switch (m_mgr->m_pebble->hardwarePlatform()) { + case HardwarePlatformBasalt: + watchInfo.setProperty("platform", "basalt"); + break; + + case HardwarePlatformChalk: + watchInfo.setProperty("platform", "chalk"); + break; + + default: + watchInfo.setProperty("platform", "aplite"); + break; + } + + switch (m_mgr->m_pebble->model()) { + case ModelTintinWhite: + watchInfo.setProperty("model", "pebble_white"); + break; + + case ModelTintinRed: + watchInfo.setProperty("model", "pebble_red"); + break; + + case ModelTintinOrange: + watchInfo.setProperty("model", "pebble_orange"); + break; + + case ModelTintinGrey: + watchInfo.setProperty("model", "pebble_grey"); + break; + + case ModelBiancaSilver: + watchInfo.setProperty("model", "pebble_steel_silver"); + break; + + case ModelBiancaBlack: + watchInfo.setProperty("model", "pebble_steel_black"); + break; + + case ModelTintinBlue: + watchInfo.setProperty("model", "pebble_blue"); + break; + + case ModelTintinGreen: + watchInfo.setProperty("model", "pebble_green"); + break; + + case ModelTintinPink: + watchInfo.setProperty("model", "pebble_pink"); + break; + + case ModelSnowyWhite: + watchInfo.setProperty("model", "pebble_time_white"); + break; + + case ModelSnowyBlack: + watchInfo.setProperty("model", "pebble_time_black"); + break; + + case ModelSnowyRed: + watchInfo.setProperty("model", "pebble_time_read"); + break; + + case ModelBobbySilver: + watchInfo.setProperty("model", "pebble_time_steel_silver"); + break; + + case ModelBobbyBlack: + watchInfo.setProperty("model", "pebble_time_steel_black"); + break; + + case ModelBobbyGold: + watchInfo.setProperty("model", "pebble_time_steel_gold"); + break; + + case ModelSpalding14Silver: + watchInfo.setProperty("model", "pebble_time_round_silver_14mm"); + break; + + case ModelSpalding14Black: + watchInfo.setProperty("model", "pebble_time_round_black_14mm"); + break; + + case ModelSpalding20Silver: + watchInfo.setProperty("model", "pebble_time_round_silver_20mm"); + break; + + case ModelSpalding20Black: + watchInfo.setProperty("model", "pebble_time_round_black_20mm"); + break; + + case ModelSpalding14RoseGold: + watchInfo.setProperty("model", "pebble_time_round_rose_gold_14mm"); + break; + + default: + watchInfo.setProperty("model", "pebble_black"); + break; + } + + watchInfo.setProperty("language", m_mgr->m_pebble->language()); + + QJSValue firmware = m_mgr->m_engine->newObject(); + QString version = m_mgr->m_pebble->softwareVersion().remove("v"); + QStringList versionParts = version.split("."); + + if (versionParts.count() >= 1) { + firmware.setProperty("major", versionParts[0].toInt()); + } + + if (versionParts.count() >= 2) { + firmware.setProperty("minor", versionParts[1].toInt()); + } + + if (versionParts.count() >= 3) { + if (versionParts[2].contains("-")) { + QStringList patchParts = version.split("-"); + firmware.setProperty("patch", patchParts[0].toInt()); + firmware.setProperty("suffix", patchParts[1]); + } else { + firmware.setProperty("patch", versionParts[2].toInt()); + firmware.setProperty("suffix", ""); + } + } + + watchInfo.setProperty("firmware", firmware); + return watchInfo; +} + +void JSKitPebble::openURL(const QUrl &url) +{ + emit m_mgr->openURL(m_appInfo.uuid().toString(), url.toString()); +} + +QJSValue JSKitPebble::createXMLHttpRequest() +{ + JSKitXMLHttpRequest *xhr = new JSKitXMLHttpRequest(m_mgr->engine()); + // Should be deleted by JS engine. + return m_mgr->engine()->newQObject(xhr); +} + +QJSValue JSKitPebble::buildAckEventObject(uint transaction, const QString &message) const +{ + QJSEngine *engine = m_mgr->engine(); + QJSValue eventObj = engine->newObject(); + QJSValue dataObj = engine->newObject(); + + dataObj.setProperty("transactionId", engine->toScriptValue(transaction)); + eventObj.setProperty("data", dataObj); + + if (!message.isEmpty()) { + QJSValue errorObj = engine->newObject(); + + errorObj.setProperty("message", engine->toScriptValue(message)); + eventObj.setProperty("error", errorObj); + } + + return eventObj; +} + +void JSKitPebble::invokeCallbacks(const QString &type, const QJSValueList &args) +{ + if (!m_listeners.contains(type)) return; + QList<QJSValue> &callbacks = m_listeners[type]; + + for (QList<QJSValue>::iterator it = callbacks.begin(); it != callbacks.end(); ++it) { + qCDebug(l) << "invoking callback" << type << it->toString(); + QJSValue result = it->call(args); + if (result.isError()) { + qCWarning(l) << "error while invoking callback" + << type << it->toString() << ":" + << JSKitManager::describeError(result); + } + } +} |
