From aec0d7c5df9ad13da47223c85b7cc04be70b60ea Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 16 Sep 2021 16:17:07 +0200 Subject: [PATCH] Add support for deep linking through push notification data --- libnymea-app/types/thing.cpp | 7 ++- nymea-app.pro | 3 +- nymea-app/main.cpp | 8 ++- nymea-app/nymea-app.pro | 2 +- nymea-app/platformhelper.cpp | 42 ++++++++++++- nymea-app/platformhelper.h | 15 ++++- .../nymeaapp/NymeaAppNotificationService.java | 19 +++++- .../io/guh/nymeaapp/NymeaAppActivity.java | 13 ++++ .../android/platformhelperandroid.cpp | 15 +++++ .../android/platformhelperandroid.h | 1 + .../ios/pushnotifications.mm | 7 ++- .../ubports/platformhelperubports.cpp | 62 ++++++++++++++++++- .../ubports/platformhelperubports.h | 21 +++++++ .../ruletemplates/notificationtemplates.json | 16 +++++ nymea-app/ui/MainPage.qml | 25 ++++++-- nymea-app/ui/RootItem.qml | 45 ++++++++++++++ nymea-app/ui/components/MainViewBase.qml | 5 ++ .../ui/connection/NewConnectionWizard.qml | 3 + nymea-app/ui/magic/NewThingMagicPage.qml | 7 +++ packaging/ubuntu/click/clickable.json | 4 +- packaging/ubuntu/click/manifest.json | 3 +- packaging/ubuntu/click/nymea-app.desktop | 2 +- packaging/ubuntu/click/pushexec | 16 ++++- 23 files changed, 320 insertions(+), 21 deletions(-) diff --git a/libnymea-app/types/thing.cpp b/libnymea-app/types/thing.cpp index 25961141..c4c92295 100644 --- a/libnymea-app/types/thing.cpp +++ b/libnymea-app/types/thing.cpp @@ -222,8 +222,11 @@ int Thing::executeAction(const QString &actionName, const QVariantList ¶ms) { ActionType *actionType = m_thingClass->actionTypes()->findByName(actionName); if (!actionType) { - qCWarning(dcThingManager) << "No such action name" << actionName << "in thing class" << m_thingClass->name(); - return -1; + actionType = m_thingClass->actionTypes()->getActionType(QUuid(actionName)); + if (!actionType) { + qCWarning(dcThingManager) << "No such action" << actionName << "in thing class" << m_thingClass->name(); + return -1; + } } QVariantList finalParams; diff --git a/nymea-app.pro b/nymea-app.pro index 8c518a01..9a372192 100644 --- a/nymea-app.pro +++ b/nymea-app.pro @@ -105,7 +105,8 @@ ubuntu_files.files += \ packaging/ubuntu/click/appicon.svg \ packaging/ubuntu/click/push.json \ packaging/ubuntu/click/push-apparmor.json \ - packaging/ubuntu/click/pushexec + packaging/ubuntu/click/pushexec \ + packaging/ubuntu/click/urls.json INSTALLS += ubuntu_files } diff --git a/nymea-app/main.cpp b/nymea-app/main.cpp index c4ee8a71..2e2da306 100644 --- a/nymea-app/main.cpp +++ b/nymea-app/main.cpp @@ -98,7 +98,13 @@ int main(int argc, char *argv[]) // Initialize app log controller as early as possible, but after setting app name and printing initial startup info AppLogController::instance(); - qCInfo(dcApplication()) << "*** nymea:app starting ***" << QDateTime::currentDateTime().toString(); + qCInfo(dcApplication()) << "*** nymea:app starting ***" << QDateTime::currentDateTime().toString() << application.arguments(); + + foreach (const QString &argument, application.arguments()) { + if (argument.startsWith("nymea://notification")) { + PlatformHelper::instance()->notificationActionReceived(QUrlQuery(QUrl(argument).query()).queryItemValue("nymeaData")); + } + } QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); diff --git a/nymea-app/nymea-app.pro b/nymea-app/nymea-app.pro index ddf18467..b0ba94c1 100644 --- a/nymea-app/nymea-app.pro +++ b/nymea-app/nymea-app.pro @@ -183,7 +183,7 @@ ubports: { DEFINES += UBPORTS CONFIG += link_pkgconfig - PKGCONFIG += connectivity-qt1 + PKGCONFIG += connectivity-qt1 dbus-1 libnih-dbus libnih HEADERS += platformintegration/ubports/pushclient.h \ platformintegration/ubports/platformhelperubports.h \ diff --git a/nymea-app/platformhelper.cpp b/nymea-app/platformhelper.cpp index 4e01fb86..8100a737 100644 --- a/nymea-app/platformhelper.cpp +++ b/nymea-app/platformhelper.cpp @@ -34,6 +34,8 @@ #include #include #include +#include +#include #if defined Q_OS_ANDROID #include @@ -56,9 +58,34 @@ PlatformHelper::PlatformHelper(QObject *parent) : QObject(parent) } -PlatformHelper *PlatformHelper::instance() +void PlatformHelper::notificationActionReceived(const QString &nymeaData) { - if (!s_instance) { + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(nymeaData.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPlatformIntegration()) << "Received a notification action but cannot parse it:" << error.errorString() << nymeaData; + return; + } + QVariantMap map = jsonDoc.toVariant().toMap(); + QUuid id = QUuid::createUuid(); + map.insert("id", id); + + // transforming data from a url query to a map for easier processing in QML + QUrlQuery query(map.value("data").toString()); + QVariantMap dataMap; + for (int i = 0; i < query.queryItems().count(); i++) { + const QPair &item = query.queryItems().at(i); + dataMap.insert(item.first, item.second); + } + map.insert("dataMap", dataMap); + + m_pendingNotificationActions.insert(id, map); + emit pendingNotificationActionsChanged(); +} + +PlatformHelper *PlatformHelper::instance(bool create) +{ + if (!s_instance && create) { #ifdef Q_OS_ANDROID s_instance = new PlatformHelperAndroid(); #elif defined(Q_OS_IOS) @@ -178,6 +205,17 @@ bool PlatformHelper::darkModeEnabled() const return false; } +QVariantList PlatformHelper::pendingNotificationActions() const +{ + return m_pendingNotificationActions.values(); +} + +void PlatformHelper::notificationActionHandled(const QUuid &id) +{ + m_pendingNotificationActions.remove(id); + emit pendingNotificationActionsChanged(); +} + bool PlatformHelper::splashVisible() const { return m_splashVisible; diff --git a/nymea-app/platformhelper.h b/nymea-app/platformhelper.h index 8e8a30ec..d30cf0bf 100644 --- a/nymea-app/platformhelper.h +++ b/nymea-app/platformhelper.h @@ -33,6 +33,9 @@ #include #include +#include +#include +#include class QQmlEngine; class QJSEngine; @@ -54,6 +57,7 @@ class PlatformHelper : public QObject Q_PROPERTY(QColor topPanelColor READ topPanelColor WRITE setTopPanelColor NOTIFY topPanelColorChanged) Q_PROPERTY(QColor bottomPanelColor READ bottomPanelColor WRITE setBottomPanelColor NOTIFY bottomPanelColorChanged) Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged) + Q_PROPERTY(QVariantList pendingNotificationActions READ pendingNotificationActions NOTIFY pendingNotificationActionsChanged) public: enum HapticsFeedback { @@ -63,7 +67,7 @@ public: }; Q_ENUM(HapticsFeedback) - static PlatformHelper* instance(); + static PlatformHelper* instance(bool create = true); virtual ~PlatformHelper() = default; virtual bool hasPermissions() const; @@ -89,6 +93,9 @@ public: virtual bool darkModeEnabled() const; + QVariantList pendingNotificationActions() const; + Q_INVOKABLE void notificationActionHandled(const QUuid &id); + virtual bool splashVisible() const; virtual void setSplashVisible(bool splashVisible); Q_INVOKABLE virtual void hideSplashScreen(); @@ -102,6 +109,9 @@ public: Q_INVOKABLE virtual void shareFile(const QString &fileName); static QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine); + + void notificationActionReceived(const QString &nymeaData); + signals: void permissionsRequestFinished(); void screenTimeoutChanged(); @@ -110,6 +120,7 @@ signals: void bottomPanelColorChanged(); void darkModeEnabledChanged(); void splashVisibleChanged(); + void pendingNotificationActionsChanged(); protected: explicit PlatformHelper(QObject *parent = nullptr); @@ -121,6 +132,8 @@ private: QColor m_bottomPanelColor = QColor("black"); bool m_splashVisible = true; + + QHash m_pendingNotificationActions; }; #endif // PLATFORMHELPER_H diff --git a/nymea-app/platformintegration/android/java-firebase/io/guh/nymeaapp/NymeaAppNotificationService.java b/nymea-app/platformintegration/android/java-firebase/io/guh/nymeaapp/NymeaAppNotificationService.java index 66887058..c182f8a6 100644 --- a/nymea-app/platformintegration/android/java-firebase/io/guh/nymeaapp/NymeaAppNotificationService.java +++ b/nymea-app/platformintegration/android/java-firebase/io/guh/nymeaapp/NymeaAppNotificationService.java @@ -44,7 +44,13 @@ public class NymeaAppNotificationService extends FirebaseMessagingService { Intent intent = new Intent(this, NymeaAppActivity.class); // intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_ONE_SHOT); - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, 0); + + Log.d(TAG, "adding extra data to intent: " + remoteMessage.getData().get("nymeaData")); + + intent.setAction(Intent.ACTION_SEND); + intent.putExtra("notificationData", remoteMessage.getData().get("nymeaData")); + + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_CANCEL_CURRENT); // We can't directly access R.drawable.ic_stat_notification from here: // When the package is branded, the package name is not "io.guh.nymeaapp" and resources in @@ -63,6 +69,17 @@ public class NymeaAppNotificationService extends FirebaseMessagingService { .setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI) .setContentIntent(pendingIntent); + // Action tests +// Intent actionIntent = new Intent(this, NymeaAppActivity.class); +// actionIntent.setAction(Intent.ACTION_SEND); +// actionIntent.putExtra("foobar", "baz"); +// PendingIntent actionPendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, actionIntent, PendingIntent.FLAG_CANCEL_CURRENT); + +// notificationBuilder.addAction(resId, "30%", actionPendingIntent); +// notificationBuilder.addAction(resId, "50%", actionPendingIntent); +// notificationBuilder.addAction(resId, "70%", actionPendingIntent); + // Action tests end + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java b/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java index ced4de7a..6d1552a3 100644 --- a/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java +++ b/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java @@ -20,6 +20,7 @@ public class NymeaAppActivity extends org.qtproject.qt5.android.bindings.QtActiv private static Context context = null; private static native void darkModeEnabledChangedJNI(); + private static native void notificationActionReceivedJNI(String data); @Override public void onCreate(Bundle savedInstanceState) { @@ -27,12 +28,24 @@ public class NymeaAppActivity extends org.qtproject.qt5.android.bindings.QtActiv this.context = getApplicationContext(); } + public void onNewIntent (Intent intent) { + Log.d(TAG, "New intent: " + intent); + String notificationData = intent.getStringExtra("notificationData"); + if (notificationData != null) { + Log.d(TAG, "Intent data: " + notificationData); + notificationActionReceivedJNI(notificationData); + } + } + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); NymeaAppActivity.darkModeEnabledChangedJNI(); } + public String notificationData() { + return getIntent().getStringExtra("notificationData"); + } public static Context getAppContext() { return NymeaAppActivity.context; diff --git a/nymea-app/platformintegration/android/platformhelperandroid.cpp b/nymea-app/platformintegration/android/platformhelperandroid.cpp index 01655bee..1683f82e 100644 --- a/nymea-app/platformintegration/android/platformhelperandroid.cpp +++ b/nymea-app/platformintegration/android/platformhelperandroid.cpp @@ -48,6 +48,7 @@ static PlatformHelperAndroid *m_instance = nullptr; static JNINativeMethod methods[] = { { "darkModeEnabledChangedJNI", "()V", (void *)PlatformHelperAndroid::darkModeEnabledChangedJNI }, + { "notificationActionReceivedJNI", "(Ljava/lang/String;)V", (void *)PlatformHelperAndroid::notificationActionReceivedJNI }, }; JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) @@ -77,6 +78,10 @@ PlatformHelperAndroid::PlatformHelperAndroid(QObject *parent) : PlatformHelper(p { m_instance = this; + QString notificationData = QtAndroid::androidActivity().callObjectMethod("notificationData", "()Ljava/lang/String;").toString(); + if (!notificationData.isNull()) { + notificationActionReceived(notificationData); + } } void PlatformHelperAndroid::requestPermissions() @@ -274,3 +279,13 @@ void PlatformHelperAndroid::darkModeEnabledChangedJNI() emit m_instance->darkModeEnabledChanged(); } } + +void PlatformHelperAndroid::notificationActionReceivedJNI(JNIEnv *env, jobject, jstring data) +{ + // Only call the platformhelper if it exists yet. We may get this callback before the Qt part is created + // and we don't want to create the PlatformHelper on the android thread. + PlatformHelper* platformHelper = PlatformHelperAndroid::instance(false); + if (platformHelper) { + platformHelper->notificationActionReceived(env->GetStringUTFChars(data, nullptr)); + } +} diff --git a/nymea-app/platformintegration/android/platformhelperandroid.h b/nymea-app/platformintegration/android/platformhelperandroid.h index 50a5554f..4fd305fe 100644 --- a/nymea-app/platformintegration/android/platformhelperandroid.h +++ b/nymea-app/platformintegration/android/platformhelperandroid.h @@ -68,6 +68,7 @@ public: void shareFile(const QString &fileName) override; static void darkModeEnabledChangedJNI(); + static void notificationActionReceivedJNI(JNIEnv *env, jobject /*thiz*/, jstring data); private: static void permissionRequestFinished(const QtAndroid::PermissionResultMap &); diff --git a/nymea-app/platformintegration/ios/pushnotifications.mm b/nymea-app/platformintegration/ios/pushnotifications.mm index 753506e8..5fbfcb2a 100644 --- a/nymea-app/platformintegration/ios/pushnotifications.mm +++ b/nymea-app/platformintegration/ios/pushnotifications.mm @@ -1,8 +1,8 @@ #import "UIKit/UIKit.h" #import -// Include our C++ class #include "pushnotifications.h" +#include "platformhelper.h" #include @@ -76,6 +76,11 @@ -(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler{ NSLog(@"User Info : %@",response.notification.request.content.userInfo); qDebug() << "received notification response!"; + + NSString *nymeaData = response.notification.request.content.userInfo[@"gcm.notification.nymeaData"]; + + PlatformHelper::instance()->notificationActionReceived(QString::fromNSString(nymeaData)); + completionHandler(); } diff --git a/nymea-app/platformintegration/ubports/platformhelperubports.cpp b/nymea-app/platformintegration/ubports/platformhelperubports.cpp index 50296b72..4cbd56cf 100644 --- a/nymea-app/platformintegration/ubports/platformhelperubports.cpp +++ b/nymea-app/platformintegration/ubports/platformhelperubports.cpp @@ -1,11 +1,23 @@ +#include +#include + #include "platformhelperubports.h" + #include #include +#include +#include +#include +#include +#include -PlatformHelperUBPorts::PlatformHelperUBPorts(QObject *parent) : PlatformHelper(parent) + +PlatformHelperUBPorts::PlatformHelperUBPorts(QObject *parent): + PlatformHelper(parent), + m_uriHandlerObject(this) { - + setupUriHandler(); } QString PlatformHelperUBPorts::platform() const @@ -21,3 +33,49 @@ QString PlatformHelperUBPorts::deviceSerial() const } return s.value("deviceSerial").toString(); } + +void PlatformHelperUBPorts::setupUriHandler() +{ + QString objectPath; + + if (!QDBusConnection::sessionBus().isConnected()) { + qWarning() << "UCUriHandler: D-Bus session bus is not connected, ignoring."; + return; + } + + // Get the object path based on the "APP_ID" environment variable. + QByteArray applicationId = qgetenv("APP_ID"); + if (applicationId.isEmpty()) { + qWarning() << "UCUriHandler: Empty \"APP_ID\" environment variable, ignoring."; + return; + } + char* path = nih_dbus_path(NULL, "", applicationId.constData(), nullptr); + objectPath = QString::fromLocal8Bit(path); + nih_free(path); + + // Ensure handler is running on the main thread. + QCoreApplication* instance = QCoreApplication::instance(); + if (instance) { + moveToThread(instance->thread()); + } else { + qWarning() << "UCUriHandler: Created before QCoreApplication, application may misbehave."; + } + + QDBusConnection::sessionBus().registerObject( + objectPath, &m_uriHandlerObject, QDBusConnection::ExportAllSlots); +} + +UriHandlerObject::UriHandlerObject(PlatformHelper *platformHelper): + m_platformHelper(platformHelper) +{ +} + +void UriHandlerObject::Open(const QStringList& uris, const QHash& platformData) +{ + Q_UNUSED(platformData); + foreach (const QString &uri, uris) { + if (uri.startsWith("nymea://notification")) { + m_platformHelper->notificationActionReceived(QUrlQuery(QUrl(uri)).queryItemValue("nymeaData")); + } + } +} diff --git a/nymea-app/platformintegration/ubports/platformhelperubports.h b/nymea-app/platformintegration/ubports/platformhelperubports.h index 10606e58..04575345 100644 --- a/nymea-app/platformintegration/ubports/platformhelperubports.h +++ b/nymea-app/platformintegration/ubports/platformhelperubports.h @@ -5,6 +5,22 @@ #include "platformhelper.h" +class UriHandlerObject: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Application") + + public: + UriHandlerObject(PlatformHelper* platformHelper); + + public Q_SLOTS: + void Open(const QStringList& uris, const QHash& platformData); + + private: + PlatformHelper* m_platformHelper = nullptr; +}; + + class PlatformHelperUBPorts : public PlatformHelper { Q_OBJECT @@ -16,6 +32,11 @@ public: signals: +private: + void setupUriHandler(); + + UriHandlerObject m_uriHandlerObject; + }; #endif // PLATFORMHELPERUBPORTS_H diff --git a/nymea-app/ruletemplates/notificationtemplates.json b/nymea-app/ruletemplates/notificationtemplates.json index c6bd69c5..4ee49f3b 100644 --- a/nymea-app/ruletemplates/notificationtemplates.json +++ b/nymea-app/ruletemplates/notificationtemplates.json @@ -26,6 +26,10 @@ { "name": "body", "value": "%0 runs out of battery" + }, + { + "name": "data", + "value": "open=$0" } ] } @@ -58,6 +62,10 @@ { "name": "body", "value": "%0 runs dry" + }, + { + "name": "data", + "value": "open=$0" } ] } @@ -90,6 +98,10 @@ { "name": "body", "value": "%0 has disconnected" + }, + { + "name": "data", + "value": "open=$0" } ] } @@ -122,6 +134,10 @@ { "name": "body", "value": "%0 has connected" + }, + { + "name": "data", + "value": "open=$0" } ] } diff --git a/nymea-app/ui/MainPage.qml b/nymea-app/ui/MainPage.qml index 5ab9b984..1fff8b7b 100644 --- a/nymea-app/ui/MainPage.qml +++ b/nymea-app/ui/MainPage.qml @@ -44,6 +44,11 @@ import "mainviews" Page { id: root + // Removing the background from this page only because the MainViewBase adds it again in + // a deepter layer as we need to include it in the blurring of the header and footer. + // We don't want to paint the background on the entire screen twice (overdraw is costly) + background: null + function configureViews() { if (Configuration.hasOwnProperty("mainViewsFilter")) { console.warn("Main views configuration is disabled by app configuration") @@ -54,10 +59,21 @@ Page { d.configOverlay = configComponent.createObject(contentContainer) } - // Removing the background from this page only because the MainViewBase adds it again in - // a deepter layer as we need to include it in the blurring of the header and footer. - // We don't want to paint the background on the entire screen twice (overdraw is costly) - background: null + function goToView(viewName, data) { + // We allow separating the target by : and pass more stuff to + console.log("Going to main view", viewName, filteredContentModel.count, data) + for (var i = 0; i < filteredContentModel.count; i++) { + console.log("got", i, filteredContentModel.modelData(i, "name")) + if (filteredContentModel.modelData(i, "name") === viewName) { + console.log("activating", i) +// mainViewSettings.currentIndex = i; +// tabBar.currentIndex = i; + swipeView.setCurrentIndex(i) + swipeView.currentItem.item.handleEvent(data) + break; + } + } + } header: Item { id: mainHeader @@ -245,6 +261,7 @@ Page { Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Repeater { + id: mainViewsRepeater model: d.configOverlay != null ? null : filteredContentModel delegate: Loader { diff --git a/nymea-app/ui/RootItem.qml b/nymea-app/ui/RootItem.qml index fe859c2c..2d856ccf 100644 --- a/nymea-app/ui/RootItem.qml +++ b/nymea-app/ui/RootItem.qml @@ -426,12 +426,57 @@ Item { target: engine.thingManager onFetchingDataChanged: { if (!engine.thingManager.fetchingData) { + processPendingPushNotificationActions(); updatePushNotificationThings() } PlatformHelper.hideSplashScreen(); } } + Connections { + target: PlatformHelper + onPendingNotificationActionsChanged: { + processPendingPushNotificationActions() + } + } + function processPendingPushNotificationActions() { + print("pending notification actions changed:", PlatformHelper.pendingNotificationActions) + if (PlatformHelper.pendingNotificationActions.length > 0) { + var notificationAction = PlatformHelper.pendingNotificationActions[0] + if (notificationAction.serverUuid.replace(/[{]]/g, "") !== engine.jsonRpcClient.serverUuid.toString().replace(/[{}]/g, "")) { + print("notification action for different server") + return; + } + print("handling action", JSON.stringify(notificationAction)) + + if (notificationAction.dataMap.hasOwnProperty("open")) { + // It could be just a thing ID + var target = notificationAction.dataMap["open"] + var thing = engine.thingManager.things.getThing(target) + if (thing) { + print("opening thing:", thing.name) + pageStack.push("/ui/devicepages/" + NymeaUtils.interfaceListToDevicePage(thing.thingClass.interfaces), {thing: thing}) + } else { + // or a view name + console.log("going to main view:", target) + pageStack.currentItem.goToView(target, notificationAction.dataMap) + } + } + + if (notificationAction.dataMap.hasOwnProperty("execute")) { + var action = notificationAction.dataMap["execute"] + var thingId = notificationAction.dataMap["thingId"] + var actionParams = JSON.parse(notificationAction.dataMap["actionParams"]) + print("executing:", thingId, action, actionParams) + engine.thingManager.things.getThing(thingId).executeAction(action, actionParams); + } + + + PlatformHelper.notificationActionHandled(notificationAction.id) + } + } + + Component { id: invalidVersionComponent Popup { diff --git a/nymea-app/ui/components/MainViewBase.qml b/nymea-app/ui/components/MainViewBase.qml index 86845e10..2f3a843e 100644 --- a/nymea-app/ui/components/MainViewBase.qml +++ b/nymea-app/ui/components/MainViewBase.qml @@ -49,6 +49,11 @@ Item { property var headerButtons: [] + // Override this to receive events (e.g. from push notification bubbles) + function handleEvent(data) { + print("handleEvent not implemented in", title) + } + Background { anchors.fill: parent } diff --git a/nymea-app/ui/connection/NewConnectionWizard.qml b/nymea-app/ui/connection/NewConnectionWizard.qml index 1663416e..2d1f86af 100644 --- a/nymea-app/ui/connection/NewConnectionWizard.qml +++ b/nymea-app/ui/connection/NewConnectionWizard.qml @@ -747,6 +747,8 @@ WizardPageBase { content: ColumnLayout { Layout.fillWidth: true + Layout.leftMargin: Style.margins + Layout.rightMargin: Style.margins Layout.maximumWidth: 500 Layout.alignment: Qt.AlignHCenter Layout.preferredHeight: visibleContentHeight @@ -755,6 +757,7 @@ WizardPageBase { wrapMode: Text.WordWrap text: qsTr("You can now go ahead and configure your nymea system.") visible: wirelessConnectionCompletedPage.host != null + horizontalAlignment: Text.AlignHCenter } BusyIndicator { Layout.alignment: Qt.AlignHCenter diff --git a/nymea-app/ui/magic/NewThingMagicPage.qml b/nymea-app/ui/magic/NewThingMagicPage.qml index d2541a59..28836fa7 100644 --- a/nymea-app/ui/magic/NewThingMagicPage.qml +++ b/nymea-app/ui/magic/NewThingMagicPage.qml @@ -278,6 +278,7 @@ Page { print("replacing args", typeof actionParam.value) if (typeof actionParam.value === "string") { actionParam.value = actionParam.value.replace("%" + selectionId, thingName); + actionParam.value = actionParam.value.replace("$" + selectionId, selectedThings[selectionId]); } } } @@ -287,6 +288,7 @@ Page { var actionParam = action.ruleActionParams.get(k); if (typeof actionParam.value === "string") { actionParam.value = actionParam.value.replace("%" + selectionId, thingName); + actionParam.value = actionParam.value.replace("$" + selectionId, selectedThings[selectionId]); } } } @@ -445,6 +447,11 @@ Page { for (var j = 0; j < ruleActionTemplate.ruleActionParamTemplates.count; j++) { var ruleActionParamTemplate = ruleActionTemplate.ruleActionParamTemplates.get(j) var paramType = actionType.paramTypes.findByName(ruleActionParamTemplate.paramName); + if (!paramType) { + print("Skipping template action param", ruleActionParamTemplate, "as action type does not have this param") + continue; + } + if (ruleActionParamTemplate.value !== undefined) { ruleAction.ruleActionParams.setRuleActionParam(paramType.id, ruleActionParamTemplate.value) } else if (ruleActionParamTemplate.eventInterface && ruleActionParamTemplate.eventName && ruleActionParamTemplate.eventParamName) { diff --git a/packaging/ubuntu/click/clickable.json b/packaging/ubuntu/click/clickable.json index 3e88d87b..198b2d8a 100644 --- a/packaging/ubuntu/click/clickable.json +++ b/packaging/ubuntu/click/clickable.json @@ -11,7 +11,9 @@ "qml-module-qtcharts", "qml-module-qt-labs-calendar", "libconnectivity-qt1-dev", - "libunity-api-dev" + "libunity-api-dev", + "libnih-dbus-dev", + "libdbus-1-dev" ], "install_qml": [ "/usr/lib/${ARCH_TRIPLET}/qt5/qml/Qt/labs/calendar/" diff --git a/packaging/ubuntu/click/manifest.json b/packaging/ubuntu/click/manifest.json index 7c4fcc8c..d52aba1c 100644 --- a/packaging/ubuntu/click/manifest.json +++ b/packaging/ubuntu/click/manifest.json @@ -6,7 +6,8 @@ "hooks": { "nymea-app": { "apparmor": "nymea-app.apparmor", - "desktop": "nymea-app.desktop" + "desktop": "nymea-app.desktop", + "urls": "urls.json" }, "push": { "apparmor": "push-apparmor.json", diff --git a/packaging/ubuntu/click/nymea-app.desktop b/packaging/ubuntu/click/nymea-app.desktop index c96d1355..99d0daa1 100644 --- a/packaging/ubuntu/click/nymea-app.desktop +++ b/packaging/ubuntu/click/nymea-app.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Name=nymea:app -Exec=usr/bin/nymea-app +Exec=usr/bin/nymea-app %U Icon=appicon.svg Terminal=false Type=Application diff --git a/packaging/ubuntu/click/pushexec b/packaging/ubuntu/click/pushexec index 5de224cf..c92e19f7 100755 --- a/packaging/ubuntu/click/pushexec +++ b/packaging/ubuntu/click/pushexec @@ -7,13 +7,25 @@ import json f1, f2 = sys.argv[1:3] payloadJson = json.load(open(f1)) +print("<<<< Input: %s" % payloadJson) +# Set an icon dir_path = os.path.dirname(os.path.realpath(__file__)) payloadJson["notification"]["card"]["icon"] = dir_path + "/appicon.svg" -payloadJson["notification"]["card"]["actions"] = ["appid://io.guh.nymeaapp/nymea-app/current-user-version"] + +# Define the on-click action +action = "appid://io.guh.nymeaapp/nymea-app/current-user-version" # The default action (just opening the app) +if "nymeaData" in payloadJson["notification"]: + action = "nymea://notification?nymeaData=%s" % json.dumps(payloadJson["notification"]["nymeaData"]) + +payloadJson["notification"]["card"]["actions"] = [action] #payloadJson["notification"]["emblem-counter"] = {"count": 1, "visible": True } -print(payloadJson) +#print("nymeaData: %s" % nymeaData) +print("action: %s" % action) + +print(">>>> Output: %s" % payloadJson) + open(f2, "w").write(json.dumps(payloadJson) + "\n")