From 621bfaed91d6ebf839738baf817618a1b1c58531 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 12 Oct 2020 14:59:15 +0200 Subject: [PATCH] More work on NFC --- .../controlviews/devicecontrolapplication.cpp | 190 +++++++++--- .../controlviews/devicecontrolapplication.h | 10 +- libnymea-app/types/actiontypes.cpp | 3 +- nymea-app/nfchelper.cpp | 163 ++++++++--- nymea-app/nfchelper.h | 47 ++- nymea-app/nymea-app.pro | 3 +- nymea-app/ui/images/nfc.svg | 10 +- nymea-app/ui/magic/WriteNfcTagPage.qml | 272 ++++++++++-------- .../guh/nymeaapp/NymeaAppControlService.java | 3 +- .../nymeaapp/NymeaAppControlsActivity.java | 23 +- .../nymeaapp/NymeaAppServiceConnection.java | 5 +- 11 files changed, 487 insertions(+), 242 deletions(-) diff --git a/androidservice/controlviews/devicecontrolapplication.cpp b/androidservice/controlviews/devicecontrolapplication.cpp index 39a724a1..6e0560a5 100644 --- a/androidservice/controlviews/devicecontrolapplication.cpp +++ b/androidservice/controlviews/devicecontrolapplication.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine) @@ -28,75 +29,180 @@ DeviceControlApplication::DeviceControlApplication(int argc, char *argv[]) : QAp setApplicationName("nymea-app"); setOrganizationName("nymea"); - QNearFieldManager *manager = new QNearFieldManager(this); - int ret = manager->registerNdefMessageHandler(this, SLOT(handleNdefMessage(QNdefMessage,QNearFieldTarget*))); - qDebug() << "*** NFC registered" << ret; - - QString nymeaId = QtAndroid::androidActivity().callObjectMethod("nymeaId").toString(); - QString thingId = QtAndroid::androidActivity().callObjectMethod("thingId").toString(); - QSettings settings; m_discovery = new NymeaDiscovery(this); AWSClient::instance()->setConfig(settings.value("cloudEnvironment").toString()); m_discovery->setAwsClient(AWSClient::instance()); - NymeaHost *host = m_discovery->nymeaHosts()->find(nymeaId); - - if (nymeaId.isEmpty() && !host) { - qWarning() << "No such nymea host:" << nymeaId; - // TODO: We could wait here until the discovery finds it... But it really should be cached already... - exit(1); - } m_engine = new Engine(this); - m_engine->jsonRpcClient()->connectToHost(host); - - qDebug() << "Connecting to:" << host; - - qDebug() << "Creating QML view"; m_qmlEngine = new QQmlApplicationEngine(this); - registerQmlTypes(); - qmlRegisterSingletonType("Nymea", 1, 0, "PlatformHelper", platformHelperProvider); qmlRegisterSingletonType(QUrl("qrc:///ui/utils/NymeaUtils.qml"), "Nymea", 1, 0, "NymeaUtils" ); qmlRegisterType("Nymea", 1, 0, "NfcHelper"); - StyleController styleController; - m_qmlEngine->rootContext()->setContextProperty("styleController", &styleController); + StyleController *styleController = new StyleController(this); + m_qmlEngine->rootContext()->setContextProperty("styleController", styleController); m_qmlEngine->rootContext()->setContextProperty("engine", m_engine); m_qmlEngine->rootContext()->setContextProperty("_engine", m_engine); - m_qmlEngine->rootContext()->setContextProperty("controlledThingId", thingId); + m_qmlEngine->rootContext()->setContextProperty("controlledThingId", ""); // Unknown at this point m_qmlEngine->load(QUrl(QLatin1String("qrc:/Main.qml"))); -} -void DeviceControlApplication::handleNdefMessage(QNdefMessage message, QNearFieldTarget *target) -{ - qDebug() << "************* NFC message!" << message.toByteArray() << target; - foreach (const QNdefRecord &record, message) { - QNdefNfcUriRecord uriRecord(record); - qDebug() << "record" << uriRecord.uri(); - QUrl url = uriRecord.uri(); - QUuid nymeaId = QUuid(url.host().split('.').first()); - QUuid thingId = QUuid(url.host().split('.').last()); - QList> queryItems = QUrlQuery(url.query()).queryItems(); - for (int i = 0; i < queryItems.count(); i++) { - QUuid stateTypeId = queryItems.at(i).first; - QVariant value = queryItems.at(i).second; + jboolean startedByNfc = QtAndroid::androidActivity().callMethod("startedByNfc", "()Z"); + if (startedByNfc) { + qDebug() << "**** Started by NFC"; + qDebug() << "Registering NFC handler and waiting for message."; - } + QNearFieldManager *manager = new QNearFieldManager(this); + manager->registerNdefMessageHandler(this, SLOT(handleNdefMessage(QNdefMessage,QNearFieldTarget*))); - NymeaHost *host = m_discovery->nymeaHosts()->find(nymeaId); - m_engine->jsonRpcClient()->connectToHost(host); + } else { + qDebug() << "*** Started by other intent"; + qDebug() << "Expecing nymeaId and thingId in intent extras."; + QString nymeaId = QtAndroid::androidActivity().callObjectMethod("nymeaId").toString(); + QString thingId = QtAndroid::androidActivity().callObjectMethod("thingId").toString(); + + connectToNymea(nymeaId); m_qmlEngine->rootContext()->setContextProperty("controlledThingId", thingId); } } -void DeviceControlApplication::createView() +void DeviceControlApplication::handleNdefMessage(QNdefMessage message, QNearFieldTarget *target) { + qDebug() << "************* NFC message!" << message.toByteArray(); + if (message.count() < 1) { + qWarning() << "NFC message doesn't contain any records..."; + return; + } + // NOTE: At this point we're only supporting one NDEF record per message + QNdefRecord record = message.first(); + QNdefNfcUriRecord uriRecord(record); + QUrl url = uriRecord.uri(); + if (url.scheme() != "nymea") { + qWarning() << "NDEF URI record scheme is not \"nymea://\""; + return; + } + + QUuid nymeaId = QUuid(url.host()); + if (nymeaId.isNull()) { + qWarning() << "Invalid nymea UUID in NDEF record."; + return; + } + + QUuid thingId = QUuid(QUrlQuery(url).queryItemValue("t")); + if (thingId.isNull()) { + qWarning() << "Invalid thing in NDEF record"; + return; + } + + m_pendingNfcAction = url; + + connectToNymea(nymeaId); + m_qmlEngine->rootContext()->setContextProperty("controlledThingId", thingId); + + connect(m_engine->thingManager(), &DeviceManager::fetchingDataChanged, [this](){ + if (m_engine->jsonRpcClient()->connected() && !m_engine->thingManager()->fetchingData()) { + qDebug() << "Ready to process commands"; + runNfcAction(); + } + }); +} + +void DeviceControlApplication::connectToNymea(const QUuid &nymeaId) +{ + NymeaHost *host = m_discovery->nymeaHosts()->find(nymeaId); + if (!host) { + qWarning() << "No such nymea host:" << nymeaId; + // TODO: We could wait here until the discovery finds it... But it really should be cached already... + exit(1); + } + qDebug() << "Connecting to:" << host->name(); + m_engine->jsonRpcClient()->connectToHost(host); +} + +void DeviceControlApplication::runNfcAction() +{ + if (!m_pendingNfcAction.isEmpty()) { + qDebug() << "NFC action:" << m_pendingNfcAction; + } + QUrl url = m_pendingNfcAction; + m_pendingNfcAction.clear(); + + if (url.scheme() != "nymea") { + qWarning() << "NDEF URI record scheme is not \"nymea://\" in" << url.toString(); + return; + } + + QUuid nymeaId = QUuid(url.host()); + if (nymeaId.isNull()) { + qWarning() << "Invalid nymea UUID" << url.host() << "in NDEF record" << url.toString(); + return; + } + + QUuid thingId = QUuid(QUrlQuery(url).queryItemValue("t")); + Device *thing = m_engine->thingManager()->things()->getThing(thingId); + if (!thing) { + qDebug() << "Thing" << thingId.toString() << "from" << url.toString() << "doesn't exist on nymea host" << nymeaId.toString(); + return; + } + + QList> queryItems = QUrlQuery(url.query()).queryItems(); + for (int i = 0; i < queryItems.count(); i++) { + QString entryName = queryItems.at(i).first; + if (entryName == "t") { + continue; + } + if (!entryName.startsWith("a")) { + qDebug() << "Only actions are supported. Skipping query item" << entryName; + continue; + } + + QString actionString = queryItems.at(i).second; + QStringList parts = actionString.split("#"); + if (parts.count() == 0) { + qDebug() << "Invalid action definition:" << actionString; + continue; + } + + QString actionTypeName = parts.at(0); + ActionType *actionType = thing->thingClass()->actionTypes()->findByName(actionTypeName); + if (!actionType) { + qWarning() << "Invalid action name" << actionType << "in url:" << url.toString(); + continue; + } + + QHash paramsInUri; + if (parts.count() > 1) { + QString paramsString = parts.at(1); + foreach (const QString ¶mString, paramsString.split("+")) { + QStringList parts = paramString.split(":"); + if (parts.count() != 2) { + qWarning() << "Invalid param format" << paramString << "in url:" << url.toString(); + continue; + } + paramsInUri.insert(parts.at(0), parts.at(1)); + } + } + + QVariantList params; + for (int j = 0; j < actionType->paramTypes()->rowCount(); j++) { + ParamType *paramType = actionType->paramTypes()->get(j); + QVariantMap param; + param.insert("paramTypeId", paramType->id()); + if (paramsInUri.contains(paramType->name())) { + param.insert("value", paramsInUri.value(paramType->name())); + } else { + param.insert("value", paramType->defaultValue()); + } + params.append(param); + } + + m_engine->thingManager()->executeAction(thingId, actionType->id(), params); + } } diff --git a/androidservice/controlviews/devicecontrolapplication.h b/androidservice/controlviews/devicecontrolapplication.h index aa557ee8..edd6985c 100644 --- a/androidservice/controlviews/devicecontrolapplication.h +++ b/androidservice/controlviews/devicecontrolapplication.h @@ -5,7 +5,9 @@ #include #include #include +#include +#include "types/ruleactions.h" #include "connection/discovery/nymeadiscovery.h" #include "engine.h" @@ -18,12 +20,18 @@ public: private slots: void handleNdefMessage(QNdefMessage message,QNearFieldTarget* target); - void createView(); + void connectToNymea(const QUuid &nymeaId); + + void runNfcAction(); private: NymeaDiscovery *m_discovery = nullptr; Engine *m_engine = nullptr; QQmlApplicationEngine *m_qmlEngine = nullptr; + + QUrl m_pendingNfcAction; + + }; #endif // DEVICECONTROLAPPLICATION_H diff --git a/libnymea-app/types/actiontypes.cpp b/libnymea-app/types/actiontypes.cpp index 7d17e648..14972811 100644 --- a/libnymea-app/types/actiontypes.cpp +++ b/libnymea-app/types/actiontypes.cpp @@ -49,11 +49,12 @@ ActionType *ActionTypes::get(int index) const ActionType *ActionTypes::getActionType(const QUuid &actionTypeId) const { foreach (ActionType *actionType, m_actionTypes) { + qDebug() << "checking:" << actionType->id(); if (actionType->id() == actionTypeId) { return actionType; } } - return 0; + return nullptr; } int ActionTypes::rowCount(const QModelIndex &parent) const diff --git a/nymea-app/nfchelper.cpp b/nymea-app/nfchelper.cpp index a93929aa..6e7fa893 100644 --- a/nymea-app/nfchelper.cpp +++ b/nymea-app/nfchelper.cpp @@ -1,6 +1,9 @@ #include "nfchelper.h" #include "types/deviceclass.h" #include "types/statetype.h" +#include "types/ruleaction.h" +#include "types/ruleactionparams.h" +#include "types/ruleactionparam.h" #include #include @@ -9,36 +12,126 @@ #include #include -NfcHelper::NfcHelper(QObject *parent) : QObject(parent) +NfcHelper::NfcHelper(QObject *parent): + QObject(parent), + m_manager(new QNearFieldManager(this)), + m_actions(new RuleActions(this)) { - m_manager = new QNearFieldManager(this); + connect(m_manager, &QNearFieldManager::targetDetected, this, &NfcHelper::targetDetected); connect(m_manager, &QNearFieldManager::targetLost, this, &NfcHelper::targetLost); + + connect(m_actions, &RuleActions::countChanged, this, &NfcHelper::updateContent); + + m_manager->startTargetDetection(); + } -bool NfcHelper::busy() const +NfcHelper::~NfcHelper() { - return m_busy; + m_manager->stopTargetDetection(); } -void NfcHelper::writeThingStates(Engine *engine, Device *thing) +Engine *NfcHelper::engine() const { - if (m_busy) { - return; + return m_engine; +} + +void NfcHelper::setEngine(Engine *engine) +{ + if (m_engine != engine) { + m_engine = engine; + emit engineChanged(); + updateContent(); } +} + +Device *NfcHelper::thing() const +{ + return m_thing; +} + +void NfcHelper::setThing(Device *thing) +{ + if (m_thing != thing) { + m_thing = thing; + emit thingChanged(); + updateContent(); + } +} + +RuleActions *NfcHelper::actions() const +{ + return m_actions; +} + +int NfcHelper::messageSize() const +{ + return m_currentMessage.toByteArray().size(); + int ret = 0; + for (int i = 0; i < m_currentMessage.size(); i++) { + ret += m_currentMessage.at(i).payload().size(); + } + return ret; +} + +NfcHelper::TagStatus NfcHelper::status() const +{ + return m_status; +} + +void NfcHelper::updateContent() +{ + qDebug() << "Updating" << m_engine << m_thing; + + // Creating an URI type record with this format: + // nymea:// + // ? t= + // & a[0]= + // & a[1]=#: + // & a[2]=#:+: + // & ... + + // NOTE: We're using actionType and paramType *name* instead of the ID because NFC tags are + // small and normally names are shorter than ids so we save some space. + + // NOTE: param values are percentage encoded to prevent messing with the parsing if they + // contain + or : QUrl url; url.setScheme("nymea"); - url.setHost(engine->jsonRpcClient()->currentHost()->uuid().toString().remove(QRegExp("[{}]")) + "." + thing->id().toString().remove(QRegExp("[{}]"))); + if (!m_engine || !m_thing) { + return; + } + url.setHost(m_engine->jsonRpcClient()->currentHost()->uuid().toString().remove(QRegExp("[{}]"))); + QUrlQuery query; - for (int i = 0; i < thing->thingClass()->stateTypes()->rowCount(); i++) { - StateType *stateType = thing->thingClass()->stateTypes()->get(i); - ActionType *actionType = thing->thingClass()->actionTypes()->getActionType(stateType->id()); - if (!actionType) { - continue; // Read only state + + query.addQueryItem("t", m_thing->id().toString().remove(QRegExp("[{}]"))); + + for (int i = 0; i < m_actions->rowCount(); i++) { + RuleAction *action = m_actions->get(i); + QStringList params; + ActionType *at = m_thing->thingClass()->actionTypes()->getActionType(action->actionTypeId()); + if (!at) { + qWarning() << "ActionType not found in thing" << action->actionTypeId(); + continue; } - QVariant currentValue = thing->states()->getState(stateType->id())->value(); - query.addQueryItem(stateType->id().toString().remove(QRegExp("[{}]")), currentValue.toString()); + + for (int j = 0; j < action->ruleActionParams()->rowCount(); j++) { + RuleActionParam *param = action->ruleActionParams()->get(j); + ParamType *pt = at->paramTypes()->getParamType(param->paramTypeId()); + if (!pt) { + qWarning() << "ParamType not found in thing"; + continue; + } + params.append(pt->name() + ":" + param->value().toByteArray().toPercentEncoding()); + } + QString actionString = at->name(); + if (params.length() > 0) { + actionString += "#" + params.join("+"); + } + query.addQueryItem(QString("a[%1]").arg(i), actionString); } url.setQuery(query); qDebug() << "writing message" << url; @@ -49,38 +142,40 @@ void NfcHelper::writeThingStates(Engine *engine, Device *thing) message.append(record); m_currentMessage = message; - m_manager->startTargetDetection(); - m_busy = true; - emit busyChanged(); + emit messageSizeChanged(); + } void NfcHelper::targetDetected(QNearFieldTarget *target) { - connect(target, &QNearFieldTarget::ndefMessagesWritten, this, &NfcHelper::ndefMessageWritten); - connect(target, &QNearFieldTarget::error, this, &NfcHelper::targetError); - + QDateTime startTime = QDateTime::currentDateTime(); + qDebug() << "target detected"; + connect(target, &QNearFieldTarget::error, this, [=](QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id){ + qDebug() << "Tag error:" << error; + m_status = TagStatusFailed; + emit statusChanged(); + }); + connect(target, &QNearFieldTarget::ndefMessagesWritten, this, [=](){ + qDebug() << "Tag written in" << startTime.msecsTo(QDateTime::currentDateTime()); + m_status = TagStatusWritten; + emit statusChanged(); + }); QNearFieldTarget::RequestId m_request = target->writeNdefMessages(QList() << m_currentMessage); if (!m_request.isValid()) { qDebug() << "Error writing tag"; - //targetError(QNearFieldTarget::NdefWriteError, m_request); + m_status = TagStatusFailed; + emit statusChanged(); } + + m_status = TagStatusWriting; + emit statusChanged(); } void NfcHelper::targetLost(QNearFieldTarget *target) { qDebug() << "Target lost" << target; + m_status = TagStatusWaiting; + emit statusChanged(); } -void NfcHelper::ndefMessageWritten() -{ - qDebug() << "NDEF message written"; - m_manager->stopTargetDetection(); - m_busy = false; - emit busyChanged(); -} - -void NfcHelper::targetError() -{ - qDebug() << "Target error"; -} diff --git a/nymea-app/nfchelper.h b/nymea-app/nfchelper.h index a2df5a3d..51ef05be 100644 --- a/nymea-app/nfchelper.h +++ b/nymea-app/nfchelper.h @@ -7,33 +7,62 @@ #include "types/device.h" #include "engine.h" +#include "types/ruleactions.h" class NfcHelper : public QObject { Q_OBJECT - Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) + Q_PROPERTY(Engine *engine READ engine WRITE setEngine NOTIFY engineChanged) + Q_PROPERTY(Device *thing READ thing WRITE setThing NOTIFY thingChanged) + Q_PROPERTY(RuleActions *actions READ actions CONSTANT) + Q_PROPERTY(int messageSize READ messageSize NOTIFY messageSizeChanged) + Q_PROPERTY(TagStatus status READ status NOTIFY statusChanged) + public: + enum TagStatus { + TagStatusWaiting, + TagStatusWriting, + TagStatusWritten, + TagStatusFailed + }; + Q_ENUM(TagStatus) + explicit NfcHelper(QObject *parent = nullptr); + ~NfcHelper(); - bool busy() const; + Engine *engine() const; + void setEngine(Engine *engine); -public slots: - void writeThingStates(Engine *engine, Device *thing); + Device *thing() const; + void setThing(Device *thing); + + RuleActions *actions() const; + + int messageSize() const; + + TagStatus status() const; signals: - void busyChanged(); + void engineChanged(); + void thingChanged(); + + void messageSizeChanged(); + void statusChanged(); private slots: + void updateContent(); + void targetDetected(QNearFieldTarget *target); void targetLost(QNearFieldTarget *target); - void ndefMessageWritten(); - void targetError(); - private: QNearFieldManager *m_manager = nullptr; - bool m_busy = false; + Engine *m_engine = nullptr; + Device *m_thing = nullptr; + RuleActions* m_actions; + + TagStatus m_status = TagStatusWaiting; QNdefMessage m_currentMessage; diff --git a/nymea-app/nymea-app.pro b/nymea-app/nymea-app.pro index e847d96d..6d444f68 100644 --- a/nymea-app/nymea-app.pro +++ b/nymea-app/nymea-app.pro @@ -165,10 +165,9 @@ BR=$$BRANDING target.path = /usr/bin INSTALLS += target -ANDROID_ABIS = armeabi-v7a - contains(ANDROID_TARGET_ARCH,) { ANDROID_ABIS = \ armeabi-v7a \ arm64-v8a } + diff --git a/nymea-app/ui/images/nfc.svg b/nymea-app/ui/images/nfc.svg index dea348bd..6176e99e 100644 --- a/nymea-app/ui/images/nfc.svg +++ b/nymea-app/ui/images/nfc.svg @@ -21,7 +21,7 @@ @@ -38,9 +38,9 @@ inkscape:window-height="873" id="namedview7" showgrid="true" - inkscape:zoom="2.1413287" - inkscape:cx="32.892399" - inkscape:cy="42.615368" + inkscape:zoom="2.4135194" + inkscape:cx="42.404861" + inkscape:cy="39.439781" inkscape:window-x="60" inkscape:window-y="27" inkscape:window-maximized="1" @@ -89,6 +89,6 @@ diff --git a/nymea-app/ui/magic/WriteNfcTagPage.qml b/nymea-app/ui/magic/WriteNfcTagPage.qml index d5cfe4a1..f31e7611 100644 --- a/nymea-app/ui/magic/WriteNfcTagPage.qml +++ b/nymea-app/ui/magic/WriteNfcTagPage.qml @@ -42,13 +42,16 @@ Page { header: NymeaHeader { text: qsTr("Write NFC tag") onBackPressed: { - root.backPressed(); + pageStack.pop() } } NfcHelper { id: nfcHelper + engine: _engine + thing: root.thing + } // nfcHelper.writeThingStates(engine, root.thing) @@ -56,140 +59,163 @@ Page { anchors.fill: parent columns: app.landscape ? 2 : 1 - Item { - Layout.preferredWidth: Math.min(root.width / parent.columns, root.height) - Layout.preferredHeight: app.iconSize * 8 - - SequentialAnimation { - loops: Animation.Infinite - running: true - - PropertyAction { target: phoneIcon; property: "anchors.horizontalCenterOffset"; value: app.iconSize * 2 } - PropertyAction { target: phoneIcon; property: "scale"; value: 1.3 } - NumberAnimation { - target: phoneIcon - property: "opacity" - duration: 500 - to: 1 - } - - PauseAnimation { duration: 500 } - ParallelAnimation { - NumberAnimation { - target: phoneIcon - property: "anchors.horizontalCenterOffset" - from: app.iconSize * 2 - to: -app.iconSize * 2 - duration: 1500 - easing.type: Easing.OutQuad - } - - NumberAnimation { - target: phoneIcon - property: "scale" - duration: 1500 - from: 1.3 - to: 1 - easing.type: Easing.InOutQuad - } - } - - ParallelAnimation { - loops: 2 - NumberAnimation { - duration: 250 - target: vibrateCircle - property: "scale" - from: .8 - to: 1.5 - } - NumberAnimation { - duration: 250 - target: vibrateCircle - property: "opacity" - from: 1 - to: 0 - } - } - PauseAnimation { - duration: 500 - } - - NumberAnimation { - target: phoneIcon - property: "opacity" - duration: 500 - to: 0 - } - } - - - ColorIcon { - id: nfcIcon - name: "../images/nfc.svg" - height: app.iconSize * 2 - width: app.iconSize * 2 - anchors.centerIn: parent - anchors.horizontalCenterOffset: - app.iconSize * 2 - } - - Item { - id: phoneIcon - height: app.iconSize * 5 - width: app.iconSize * 5 - scale: 1.5 - anchors.centerIn: parent - anchors.horizontalCenterOffset: app.iconSize * 2 - - Rectangle { - id: vibrateCircle - anchors.centerIn: parent - anchors.fill: parent - radius: width / 2 - color: "transparent" -// border.color: nfcIcon.keyColor - border.color: app.accentColor - border.width: 2 - scale: .8 - opacity: 0 - } - - Rectangle { - anchors.fill: parent - anchors.leftMargin: phoneIcon.width * .21 - anchors.rightMargin: phoneIcon.width * .21 - anchors.topMargin: phoneIcon.height * .1 - anchors.bottomMargin: phoneIcon.height * .1 - color: app.backgroundColor - } - - ColorIcon { - name: "../images/smartphone.svg" - anchors.fill: parent - } - } - - } - ColumnLayout { + Layout.preferredWidth: parent.width / parent.columns + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins Label { Layout.fillWidth: true - Layout.leftMargin: app.margins; Layout.rightMargin: app.margins - text: qsTr("Select the wanted states and tap an NFC tag to store them. When tapping this tag later, they will be restored.").arg(root.thing.name) + text: { + switch (nfcHelper.status) { + case NfcHelper.TagStatusWaiting: + return qsTr("Tap an NFC tag to link it to %1.").arg(root.thing.name) + case NfcHelper.TagStatusWriting: + return qsTr("Writing NFC tag...") + case NfcHelper.TagStatusWritten: + return qsTr("NFC tag linked to %1.").arg(root.thing.name) + case NfcHelper.TagStatusFailed: + return qsTr("Failed linking the NFC tag to %1.").arg(root.thing.name) + } + } + horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap - font.pixelSize: app.smallFont } + Label { + Layout.fillWidth: true + text: qsTr("Required tag size: %1 bytes").arg(nfcHelper.messageSize) + font.pixelSize: app.smallFont + horizontalAlignment: Text.AlignHCenter + enabled: false + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: app.iconSize * 8 + + SequentialAnimation { + loops: Animation.Infinite + running: true + + PropertyAction { target: phoneIcon; property: "anchors.horizontalCenterOffset"; value: app.iconSize * 2 } + PropertyAction { target: phoneIcon; property: "scale"; value: 1.3 } + NumberAnimation { target: phoneIcon; property: "opacity"; duration: 500; to: 1 } + PauseAnimation { duration: 500 } + ParallelAnimation { + NumberAnimation { target: phoneIcon; property: "anchors.horizontalCenterOffset"; from: app.iconSize * 2; to: -app.iconSize * 2; duration: 1500; easing.type: Easing.OutQuad } + NumberAnimation { target: phoneIcon; property: "scale"; duration: 1500; from: 1.3; to: 1; easing.type: Easing.InOutQuad } + } + PauseAnimation { duration: 500 } + NumberAnimation { target: phoneIcon; property: "opacity"; duration: 500; to: 0 } + PauseAnimation { duration: 500 } + } + + + ColorIcon { + id: nfcIcon + name: "../images/nfc.svg" + height: app.iconSize * 2 + width: app.iconSize * 2 + anchors.centerIn: parent + anchors.horizontalCenterOffset: - app.iconSize * 2 + visible: nfcHelper.status == NfcHelper.TagStatusWaiting + } + + Item { + id: phoneIcon + height: app.iconSize * 5 + width: app.iconSize * 5 + scale: 1.5 + anchors.centerIn: parent + anchors.horizontalCenterOffset: app.iconSize * 2 + visible: nfcHelper.status == NfcHelper.TagStatusWaiting + + Rectangle { + anchors.fill: parent + anchors.leftMargin: phoneIcon.width * .21 + anchors.rightMargin: phoneIcon.width * .21 + anchors.topMargin: phoneIcon.height * .1 + anchors.bottomMargin: phoneIcon.height * .1 + color: app.backgroundColor + } + + ColorIcon { + name: "../images/smartphone.svg" + anchors.fill: parent + } + } + + Rectangle { + id: tick + anchors.centerIn: parent + height: app.iconSize * 6 + width: app.iconSize * 6 + radius: width / 2 + color: app.backgroundColor + border.width: 4 + border.color: app.foregroundColor + opacity: nfcHelper.status == NfcHelper.TagStatusWaiting ? 0 : 1 + Behavior on opacity { NumberAnimation { duration: 300 } } + + property bool shown: nfcHelper.status == NfcHelper.TagStatusWritten || nfcHelper.status == NfcHelper.TagStatusFailed + + BusyIndicator { + anchors.fill: parent + running: visible + visible: nfcHelper.status == NfcHelper.TagStatusWriting + } + + Item { + anchors.fill: parent + anchors.rightMargin: tick.shown ? 0 : parent.width + Behavior on anchors.rightMargin { NumberAnimation { duration: 500 } } + clip: true + + ColorIcon { + x: (tick.width - width) / 2 + y: (tick.height - height) / 2 + height: app.iconSize * 4 + width: app.iconSize * 4 + name: nfcHelper.status == NfcHelper.TagStatusFailed ? "../images/close.svg" : "../images/tick.svg" + color: nfcHelper.status == NfcHelper.TagStatusFailed ? "red" : "green" + } + } + } + } + } + + + ColumnLayout { + Layout.preferredWidth: parent.width / parent.columns + ListView { Layout.fillWidth: true Layout.fillHeight: true - model: root.thingClass.stateTypes + model: nfcHelper.actions clip: true - delegate: CheckDelegate { + delegate: RuleActionDelegate { + ruleAction: nfcHelper.actions.get(index) width: parent.width - text: model.displayName - checked: true + onRemoveRuleAction: nfcHelper.actions.removeRuleAction(index) + } + } + + Button { + text: qsTr("Add action") + Layout.fillWidth: true + Layout.margins: app.margins + onClicked: { + var action = nfcHelper.actions.createNewRuleAction() + action.thingId = root.thing.id + var page = pageStack.push("SelectRuleActionPage.qml", {ruleAction: action}); + page.done.connect(function() { + nfcHelper.actions.addRuleAction(action); + pageStack.pop(); + }) + page.backPressed.connect(function() { + action.destroy() + pageStack.pop(); + }) } } } diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java index b35cef9c..b3febd4f 100644 --- a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java @@ -61,9 +61,8 @@ public class NymeaAppControlService extends ControlsProviderService { } } @Override public void onUpdate(UUID nymeaId, UUID thingId) { - Log.d(TAG, "onUpdate()"); if (m_updatePublisher != null && m_activeControlIds.contains(thingId.toString())) { - Log.d(TAG, "Updating publisher for thing: " + thingId); +// Log.d(TAG, "Updating publisher for thing: " + thingId); m_updatePublisher.onNext(thingToControl(nymeaId, thingId)); // m_updatePublisher.onComplete(); } diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java index 466f66e0..3b94d25e 100644 --- a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java @@ -29,11 +29,13 @@ public class NymeaAppControlsActivity extends org.qtproject.qt5.android.bindings Log.d(TAG, "Resuming..."); } - @Override public void onDestroy() { Log.d(TAG, "Destroying..."); } + public boolean startedByNfc() { + return NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction()); + } public String nymeaId() { @@ -50,23 +52,4 @@ public class NymeaAppControlsActivity extends org.qtproject.qt5.android.bindings Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); v.vibrate(duration); } - - @Override - protected void onNewIntent(Intent intent) { - Log.d(TAG, "*************** New intent"); - super.onNewIntent(intent); - if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { - Parcelable[] rawMessages = - intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); - if (rawMessages != null) { - NdefMessage[] messages = new NdefMessage[rawMessages.length]; - for (int i = 0; i < rawMessages.length; i++) { - messages[i] = (NdefMessage) rawMessages[i]; - Log.d(TAG, messages[i].toString()); - } - // Process the messages array. - } - } - } - } diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java index aa0cb9da..95f1b608 100644 --- a/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java @@ -154,7 +154,6 @@ public class NymeaAppServiceConnection implements ServiceConnection { private BroadcastReceiver serviceMessageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - Log.d(TAG, "In OnReceive broadcast receiver"); if (NymeaAppService.NYMEA_APP_BROADCAST.equals(intent.getAction())) { String payload = intent.getStringExtra("data"); try { @@ -170,7 +169,7 @@ public class NymeaAppServiceConnection implements ServiceConnection { { JSONObject data = new JSONObject(payload); JSONObject params = data.getJSONObject("params"); - Log.d(TAG, "Broadcast received from NymeaAppService: " + data.getString("notification")); +// Log.d(TAG, "Broadcast received from NymeaAppService: " + data.getString("notification")); Log.d(TAG, params.toString()); if (data.getString("notification").equals("ThingStateChanged")) { @@ -178,7 +177,7 @@ public class NymeaAppServiceConnection implements ServiceConnection { UUID thingId = UUID.fromString(params.getString("thingId")); UUID stateTypeId = UUID.fromString(params.getString("stateTypeId")); String value = params.getString("value"); - Log.d(TAG, "Thing state changed: " + thingId + " stateTypeId: " + stateTypeId + " value: " + value); +// Log.d(TAG, "Thing state changed: " + thingId + " stateTypeId: " + stateTypeId + " value: " + value); Thing thing = getThing(thingId); if (thing != null) {