From 63e5d6bf7742cbeb93c0bfba97bff6caac7ed83e Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 16 Sep 2020 17:27:19 +0200 Subject: [PATCH] Mostly finished now --- androidservice/androidbinder.cpp | 4 ++ androidservice/androidservice.pro | 26 ++++++-- androidservice/controlviews/Main.qml | 56 ++++++++++++++++ androidservice/controlviews/controlviews.qrc | 5 ++ .../controlviews/devicecontrolapplication.cpp | 62 ++++++++++++++++++ .../controlviews/devicecontrolapplication.h | 14 ++++ .../nymeaappservice/nymeaappservice.cpp | 43 +++++++++++++ .../nymeaappservice/nymeaappservice.h | 20 ++++++ androidservice/service_main.cpp | 64 +++++++------------ libnymea-app/devicemanager.h | 1 + nymea-app/nymea-app.pro | 2 - nymea-app/platformhelper.cpp | 5 -- nymea-app/platformhelper.h | 2 - .../android/platformhelperandroid.cpp | 40 ++++++------ .../android/platformhelperandroid.h | 1 - nymea-app/ui/Nymea.qml | 46 ------------- nymea-app/ui/RootItem.qml | 6 -- nymea-app/ui/devicepages/GarageThingPage.qml | 2 +- nymea-app/ui/grouping/GroupThingsPage.qml | 2 +- nymea-app/ui/mainviews/FavoritesView.qml | 2 +- nymea-app/ui/utils/NymeaUtils.qml | 49 ++++++++++++++ packaging/android/AndroidManifest.xml | 33 ++++++++-- .../guh/nymeaapp/NymeaAppControlService.java | 50 ++++++++++++--- .../nymeaapp/NymeaAppControlsActivity.java | 35 ++++++++++ .../src/io/guh/nymeaapp/NymeaAppService.java | 8 ++- .../nymeaapp/NymeaAppServiceConnection.java | 13 +++- 26 files changed, 441 insertions(+), 150 deletions(-) create mode 100644 androidservice/controlviews/Main.qml create mode 100644 androidservice/controlviews/controlviews.qrc create mode 100644 androidservice/controlviews/devicecontrolapplication.cpp create mode 100644 androidservice/controlviews/devicecontrolapplication.h create mode 100644 androidservice/nymeaappservice/nymeaappservice.cpp create mode 100644 androidservice/nymeaappservice/nymeaappservice.h diff --git a/androidservice/androidbinder.cpp b/androidservice/androidbinder.cpp index 029f17e3..8e923806 100644 --- a/androidservice/androidbinder.cpp +++ b/androidservice/androidbinder.cpp @@ -22,8 +22,12 @@ bool AndroidBinder::onTransact(int code, const QAndroidParcel &data, const QAndr switch (code) { case 0: { // Status request + qDebug() << "Engine is:" << m_engine->jsonRpcClient()->connected(); bool isReady = m_engine->jsonRpcClient()->connected() && !m_engine->thingManager()->fetchingData(); reply.handle().callMethod("writeBoolean", "(Z)V", isReady); + if (isReady) { + reply.handle().callMethod("writeString", "(Ljava/lang/String;)V", QAndroidJniObject::fromString(m_engine->jsonRpcClient()->currentHost()->name()).object()); + } } break; case 1: {// Things request QVariantList thingsList; diff --git a/androidservice/androidservice.pro b/androidservice/androidservice.pro index 62c2cb68..bf2b6ddf 100644 --- a/androidservice/androidservice.pro +++ b/androidservice/androidservice.pro @@ -16,19 +16,37 @@ LIBS += -L$${top_builddir}/libnymea-app/$${ANDROID_TARGET_ARCH} LIBS += -L$$top_builddir/libnymea-app/ -lnymea-app PRE_TARGETDEPS += ../libnymea-app +RESOURCES += controlviews/controlviews.qrc \ + ../nymea-app/resources.qrc \ + ../nymea-app/images.qrc \ + ../nymea-app/styles.qrc + +INCLUDEPATH += ../nymea-app/ + SOURCES += \ androidbinder.cpp \ + controlviews/devicecontrolapplication.cpp \ + nymeaappservice/nymeaappservice.cpp \ + ../nymea-app/stylecontroller.cpp \ + ../nymea-app/platformhelper.cpp \ + ../nymea-app/platformintegration/android/platformhelperandroid.cpp \ service_main.cpp -#HEADERS += servicemessenger.h - HEADERS += \ - androidbinder.h + androidbinder.h \ + controlviews/devicecontrolapplication.h \ + nymeaappservice/nymeaappservice.h \ + ../nymea-app/stylecontroller.h \ + ../nymea-app/platformhelper.h \ + ../nymea-app/platformintegration/android/platformhelperandroid.h \ DISTFILES += \ ../packaging/android/src/io/guh/nymeaapp/Action.java \ + ../packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java \ + ../packaging/android/src/io/guh/nymeaapp/NymeaAppService.java \ ../packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java \ ../packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java \ ../packaging/android/src/io/guh/nymeaapp/Thing.java \ - ../packaging/android/src/io/guh/nymeaapp/State.java + ../packaging/android/src/io/guh/nymeaapp/State.java \ + controlviews/Main.qml diff --git a/androidservice/controlviews/Main.qml b/androidservice/controlviews/Main.qml new file mode 100644 index 00000000..ad29e35a --- /dev/null +++ b/androidservice/controlviews/Main.qml @@ -0,0 +1,56 @@ +import QtQuick 2.8 +import QtQuick.Controls 2.2 +import QtQuick.Controls.Material 2.1 +import QtQuick.Layouts 1.2 +import Qt.labs.settings 1.0 +import Nymea 1.0 +import "qrc:/ui/devicepages/" + +ApplicationWindow { + id: app + visible: true + visibility: ApplicationWindow.FullScreen + + color: Material.background + + // Those variables must be present in the Style + title: appName + Material.primary: primaryColor + Material.accent: accentColor + Material.foreground: foregroundColor + + property int margins: 16 + property int bigMargins: 20 + property int extraSmallFont: 10 + property int smallFont: 13 + property int mediumFont: 16 + property int largeFont: 20 + property int iconSize: 30 + property int delegateHeight: 60 + property color backgroundColor: Material.background + + readonly property bool landscape: app.width > app.height + + ThingsProxy { + id: thingProxy + engine: _engine + filterDeviceId: controlledThingId + } + + property Thing controlledThing: engine.thingManager.fetchingData ? null : engine.thingManager.things.getThing(controlledThingId) + + onControlledThingChanged: { + loader.setSource("qrc:/ui/devicepages/" + NymeaUtils.interfaceListToDevicePage(controlledThing.thingClass.interfaces), {thing: controlledThing, header: null}) + } + + Loader { + id: loader + anchors.fill: parent + anchors.bottomMargin: app.margins // For some reason the bottom edge seems a bit off in the overlay + } + + onClosing: { + print("************* Control View closing") + } + +} diff --git a/androidservice/controlviews/controlviews.qrc b/androidservice/controlviews/controlviews.qrc new file mode 100644 index 00000000..f907b18e --- /dev/null +++ b/androidservice/controlviews/controlviews.qrc @@ -0,0 +1,5 @@ + + + Main.qml + + diff --git a/androidservice/controlviews/devicecontrolapplication.cpp b/androidservice/controlviews/devicecontrolapplication.cpp new file mode 100644 index 00000000..8122649b --- /dev/null +++ b/androidservice/controlviews/devicecontrolapplication.cpp @@ -0,0 +1,62 @@ +#include "devicecontrolapplication.h" + +#include "engine.h" +#include "connection/discovery/nymeadiscovery.h" +#include "connection/nymeahosts.h" +#include "libnymea-app-core.h" +#include "../nymea-app/stylecontroller.h" +#include "../nymea-app/platformhelper.h" +#include "../nymea-app/platformintegration/android/platformhelperandroid.h" + +#include +#include +#include +#include +#include + +QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + return new PlatformHelperAndroid(); +} + +DeviceControlApplication::DeviceControlApplication(int argc, char *argv[]) : QApplication(argc, argv) +{ + setApplicationName("nymea-app"); + setOrganizationName("nymea"); + + qDebug() << "Creating QML view"; + QQmlApplicationEngine *qmlEngine = new QQmlApplicationEngine(this); + + Engine *m_engine = new Engine(this); + + QSettings settings; + settings.beginGroup("tabSettings0"); + QUuid lastConnected = settings.value("lastConnectedHost").toUuid(); + settings.endGroup(); + + NymeaDiscovery *discovery = new NymeaDiscovery(this); + + NymeaHost *host = discovery->nymeaHosts()->find(lastConnected); + qDebug() << "**** Tab settings" << lastConnected << host; + if (host) { + m_engine->jsonRpcClient()->connectToHost(host); + } + + QString thingId = QtAndroid::androidActivity().callObjectMethod("thingId").toString(); + + registerQmlTypes(); + + qmlRegisterSingletonType("Nymea", 1, 0, "PlatformHelper", platformHelperProvider); + qmlRegisterSingletonType(QUrl("qrc:///ui/utils/NymeaUtils.qml"), "Nymea", 1, 0, "NymeaUtils" ); + + StyleController styleController; + qmlEngine->rootContext()->setContextProperty("styleController", &styleController); + qmlEngine->rootContext()->setContextProperty("engine", m_engine); + qmlEngine->rootContext()->setContextProperty("_engine", m_engine); + qmlEngine->rootContext()->setContextProperty("controlledThingId", thingId); + + qmlEngine->load(QUrl(QLatin1String("qrc:/Main.qml"))); +} + diff --git a/androidservice/controlviews/devicecontrolapplication.h b/androidservice/controlviews/devicecontrolapplication.h new file mode 100644 index 00000000..4c80e850 --- /dev/null +++ b/androidservice/controlviews/devicecontrolapplication.h @@ -0,0 +1,14 @@ +#ifndef DEVICECONTROLAPPLICATION_H +#define DEVICECONTROLAPPLICATION_H + +#include + +class DeviceControlApplication : public QApplication +{ + Q_OBJECT +public: + explicit DeviceControlApplication(int argc, char *argv[]); + +}; + +#endif // DEVICECONTROLAPPLICATION_H diff --git a/androidservice/nymeaappservice/nymeaappservice.cpp b/androidservice/nymeaappservice/nymeaappservice.cpp new file mode 100644 index 00000000..27c38c31 --- /dev/null +++ b/androidservice/nymeaappservice/nymeaappservice.cpp @@ -0,0 +1,43 @@ +#include "nymeaappservice.h" +#include "androidbinder.h" + +#include +#include +#include + +#include "connection/discovery/nymeadiscovery.h" +#include "connection/nymeahosts.h" + +NymeaAppService::NymeaAppService(int argc, char **argv): + QAndroidService(argc, argv, [=](const QAndroidIntent &) { + qDebug() << "Android service onBind()"; + return new AndroidBinder{m_engine}; + }), + m_engine(new Engine(this)) +{ + setApplicationName("nymea-app"); + setOrganizationName("nymea"); + + QSettings settings; + settings.beginGroup("tabSettings0"); + QUuid lastConnected = settings.value("lastConnectedHost").toUuid(); + settings.endGroup(); + + NymeaDiscovery *discovery = new NymeaDiscovery(); + + NymeaHost *host = discovery->nymeaHosts()->find(lastConnected); + qDebug() << "**** Tab settings" << lastConnected << host; + if (host) { + m_engine->jsonRpcClient()->connectToHost(host); + } + + QObject::connect(m_engine->thingManager(), &DeviceManager::thingStateChanged, [=](const QUuid &thingId, const QUuid &stateTypeId, const QVariant &value){ +// qDebug() << "**** State changed" << thingId << stateTypeId << value; + QtAndroid::androidService().callMethod("sendBroadcast", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + QAndroidJniObject::fromString(thingId.toString()).object(), + QAndroidJniObject::fromString(stateTypeId.toString()).object(), + QAndroidJniObject::fromString(value.toString()).object()); + }); + + +} diff --git a/androidservice/nymeaappservice/nymeaappservice.h b/androidservice/nymeaappservice/nymeaappservice.h new file mode 100644 index 00000000..89d441f9 --- /dev/null +++ b/androidservice/nymeaappservice/nymeaappservice.h @@ -0,0 +1,20 @@ +#ifndef NYMEAAPPSERVICE_H +#define NYMEAAPPSERVICE_H + +#include + +#include "engine.h" + +class NymeaAppService : public QAndroidService +{ + Q_OBJECT +public: + explicit NymeaAppService(int argc, char** argv); + +signals: +private: + Engine *m_engine = nullptr; + +}; + +#endif // NYMEAAPPSERVICE_H diff --git a/androidservice/service_main.cpp b/androidservice/service_main.cpp index 9a63316e..46efab22 100644 --- a/androidservice/service_main.cpp +++ b/androidservice/service_main.cpp @@ -1,55 +1,37 @@ #include -#include -#include #include -#include -#include "androidbinder.h" +#include "nymeaappservice/nymeaappservice.h" +#include "controlviews/devicecontrolapplication.h" -#include "engine.h" -#include "connection/discovery/nymeadiscovery.h" -#include "connection/nymeahosts.h" +#include +#include int main(int argc, char *argv[]) { qWarning() << "Service starting from a separate .so file"; + QLoggingCategory::setFilterRules("qt.remoteobjects.debug=true\n"); - Engine *engine = new Engine(); -// engine->jsonRpcClient()->connectToHost() - - - QAndroidService app(argc, argv, [=](const QAndroidIntent &) { - qDebug() << "Android service onBind()"; - return new AndroidBinder{engine}; - }); - - app.setApplicationName("nymea-app"); - app.setOrganizationName("nymea"); - - qDebug() << "Starting nymea app service"; - - QSettings settings; - settings.beginGroup("tabSettings0"); - QUuid lastConnected = settings.value("lastConnectedHost").toUuid(); - settings.endGroup(); - - NymeaDiscovery *discovery = new NymeaDiscovery(); - - NymeaHost *host = discovery->nymeaHosts()->find(lastConnected); - qDebug() << "**** Tab settings" << lastConnected << host; - if (host) { - engine->jsonRpcClient()->connectToHost(host); + QStringList args; + for (int i = 0; i < argc; i++) { + args.append(QByteArray(argv[i])); + qDebug() << "nymea-app: Added command line arg" << args.last(); } + QCommandLineParser parser; + QCommandLineOption controlActivityOption("controlActivity"); + parser.addOption(controlActivityOption); + parser.parse(args); - QObject::connect(engine->thingManager(), &DeviceManager::thingStateChanged, [=](const QUuid &thingId, const QUuid &stateTypeId, const QVariant &value){ - qDebug() << "**** State changed" << thingId << stateTypeId << value; - qDebug() << "Sending broadcast"; - QtAndroid::androidService().callMethod("sendBroadcast", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", - QAndroidJniObject::fromString(thingId.toString()).object(), - QAndroidJniObject::fromString(stateTypeId.toString()).object(), - QAndroidJniObject::fromString(value.toString()).object()); - }); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - return app.exec(); + QCoreApplication *app; + if (parser.isSet(controlActivityOption)) { + qDebug() << "nymea-app: Starting Device Control Activity"; + app = new DeviceControlApplication(argc, argv); + } else { + qDebug() << "nymea-app: Starting NymeaAppService background service"; + app = new NymeaAppService(argc, argv); + } + return app->exec(); } diff --git a/libnymea-app/devicemanager.h b/libnymea-app/devicemanager.h index 1afcc01d..f8e1fc1d 100644 --- a/libnymea-app/devicemanager.h +++ b/libnymea-app/devicemanager.h @@ -61,6 +61,7 @@ class DeviceManager : public JsonHandler Q_PROPERTY(bool fetchingData READ fetchingData NOTIFY fetchingDataChanged) + Q_ENUMS(RemovePolicy) public: enum RemovePolicy { RemovePolicyNone, diff --git a/nymea-app/nymea-app.pro b/nymea-app/nymea-app.pro index c4ae3f28..a832a44d 100644 --- a/nymea-app/nymea-app.pro +++ b/nymea-app/nymea-app.pro @@ -77,8 +77,6 @@ android { $$ANDROID_PACKAGE_SOURCE_DIR/gradlew.bat \ $$ANDROID_PACKAGE_SOURCE_DIR/src/io/guh/nymeaapp/NymeaAppActivity.java \ $$ANDROID_PACKAGE_SOURCE_DIR/src/io/guh/nymeaapp/NymeaAppNotificationService.java \ - $$ANDROID_PACKAGE_SOURCE_DIR/src/io/guh/nymeaapp/NymeaAppControlService.java \ - $$ANDROID_PACKAGE_SOURCE_DIR/src/io/guh/nymeaapp/NymeaAppService.java \ $$ANDROID_PACKAGE_SOURCE_DIR/LICENSE # https://bugreports.qt.io/browse/QTBUG-83165 diff --git a/nymea-app/platformhelper.cpp b/nymea-app/platformhelper.cpp index e3226514..27a97d4d 100644 --- a/nymea-app/platformhelper.cpp +++ b/nymea-app/platformhelper.cpp @@ -98,8 +98,3 @@ QString PlatformHelper::fromClipBoard() { return QApplication::clipboard()->text(); } - -void PlatformHelper::syncThings() -{ - // no-op by default -} diff --git a/nymea-app/platformhelper.h b/nymea-app/platformhelper.h index de32c3a2..d0074ef7 100644 --- a/nymea-app/platformhelper.h +++ b/nymea-app/platformhelper.h @@ -88,8 +88,6 @@ public: Q_INVOKABLE virtual void toClipBoard(const QString &text); Q_INVOKABLE virtual QString fromClipBoard(); - Q_INVOKABLE virtual void syncThings(); - signals: void permissionsRequestFinished(); void screenTimeoutChanged(); diff --git a/nymea-app/platformintegration/android/platformhelperandroid.cpp b/nymea-app/platformintegration/android/platformhelperandroid.cpp index e0861943..7dcdb315 100644 --- a/nymea-app/platformintegration/android/platformhelperandroid.cpp +++ b/nymea-app/platformintegration/android/platformhelperandroid.cpp @@ -127,32 +127,32 @@ void PlatformHelperAndroid::vibrate(PlatformHelper::HapticsFeedback feedbackType QtAndroid::androidActivity().callMethod("vibrate","(I)V", duration); } -void PlatformHelperAndroid::syncThings() -{ - - QAndroidIntent serviceIntent(QtAndroid::androidActivity().object(), - "io/guh/nymeaapp/NymeaAppService"); - QAndroidJniObject result = QtAndroid::androidActivity().callObjectMethod( - "startService", - "(Landroid/content/Intent;)Landroid/content/ComponentName;", - serviceIntent.handle().object()); - - -// QtAndroid::androidService() +//void PlatformHelperAndroid::syncThings() +//{ // QAndroidIntent serviceIntent(QtAndroid::androidActivity().object(), -// "io/guh/nymeaapp/NymeaAppControlService"); -// serviceIntent.putExtra("name", QByteArray("foobar")); +// "io/guh/nymeaapp/NymeaAppService"); +// QAndroidJniObject result = QtAndroid::androidActivity().callObjectMethod( +// "startService", +// "(Landroid/content/Intent;)Landroid/content/ComponentName;", +// serviceIntent.handle().object()); -// m_serviceConnection->handle().callMethod("syncThings", "(Ljava/lang/String;)V", "bla"); +//// QtAndroid::androidService() + +//// QAndroidIntent serviceIntent(QtAndroid::androidActivity().object(), +//// "io/guh/nymeaapp/NymeaAppControlService"); +//// serviceIntent.putExtra("name", QByteArray("foobar")); -// QAndroidJniObject result = QtAndroid::androidActivity().callObjectMethod( -// "syncThings", -// "(Landroid/content/Intent;)Landroid/content/ComponentName;", -// m_serviceConnection->handle().object()); -} +//// m_serviceConnection->handle().callMethod("syncThings", "(Ljava/lang/String;)V", "bla"); + + +//// QAndroidJniObject result = QtAndroid::androidActivity().callObjectMethod( +//// "syncThings", +//// "(Landroid/content/Intent;)Landroid/content/ComponentName;", +//// m_serviceConnection->handle().object()); +//} void PlatformHelperAndroid::setTopPanelColor(const QColor &color) { diff --git a/nymea-app/platformintegration/android/platformhelperandroid.h b/nymea-app/platformintegration/android/platformhelperandroid.h index d393d549..fed19a80 100644 --- a/nymea-app/platformintegration/android/platformhelperandroid.h +++ b/nymea-app/platformintegration/android/platformhelperandroid.h @@ -57,7 +57,6 @@ public: QString deviceManufacturer() const override; Q_INVOKABLE void vibrate(HapticsFeedback feedbackType) override; - Q_INVOKABLE void syncThings() override; void setTopPanelColor(const QColor &color) override; void setTopPanelTheme(Theme theme); diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index 87f17cb9..e50a72c0 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -456,52 +456,6 @@ ApplicationWindow { } } - function interfaceListToDevicePage(interfaceList) { - var page; - if (interfaceList.indexOf("media") >= 0) { - page = "MediaDevicePage.qml"; - } else if (interfaceList.indexOf("button") >= 0) { - page = "ButtonDevicePage.qml"; - } else if (interfaceList.indexOf("powerswitch") >= 0) { - page = "ButtonDevicePage.qml"; - } else if (interfaceList.indexOf("weather") >= 0) { - page = "WeatherDevicePage.qml"; - } else if (interfaceList.indexOf("heating") >= 0 || interfaceList.indexOf("thermostat") >= 0) { - page = "HeatingDevicePage.qml"; - } else if (interfaceList.indexOf("sensor") >= 0) { - page = "SensorDevicePage.qml"; - } else if (interfaceList.indexOf("inputtrigger") >= 0) { - page = "InputTriggerDevicePage.qml"; - } else if (interfaceList.indexOf("garagedoor") >= 0 ) { - page = "GarageThingPage.qml"; - } else if (interfaceList.indexOf("light") >= 0) { - page = "LightDevicePage.qml"; - } else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) { - page = "ShutterDevicePage.qml"; - } else if (interfaceList.indexOf("awning") >= 0) { - page = "AwningDevicePage.qml"; - } else if (interfaceList.indexOf("notifications") >= 0) { - page = "NotificationsDevicePage.qml"; - } else if (interfaceList.indexOf("fingerprintreader") >= 0) { - page = "FingerprintReaderDevicePage.qml"; - } else if (interfaceList.indexOf("smartmeter") >= 0) { - page = "SmartMeterDevicePage.qml" - } else if (interfaceList.indexOf("powersocket") >= 0) { - page = "PowersocketDevicePage.qml"; - } else if (interfaceList.indexOf("doorbell") >= 0) { - page = "DoorbellDevicePage.qml"; - } else if (interfaceList.indexOf("irrigation") >= 0) { - page = "IrrigationDevicePage.qml"; - } else if (interfaceList.indexOf("ventilation") >= 0) { - page = "VentilationDevicePage.qml"; - } else if (interfaceList.indexOf("barcodescanner") >= 0) { - page = "BarcodeScannerThingPage.qml"; - } else { - page = "GenericDevicePage.qml"; - } - print("Selecting page", page, "for interface list:", interfaceList) - return page; - } function pad(num, size) { var s = "000000000" + num; diff --git a/nymea-app/ui/RootItem.qml b/nymea-app/ui/RootItem.qml index 8fb50f6f..e38bc904 100644 --- a/nymea-app/ui/RootItem.qml +++ b/nymea-app/ui/RootItem.qml @@ -132,12 +132,6 @@ Item { initialItem: Page {} } - Button { - anchors.centerIn: parent - text: "bla" - onClicked: PlatformHelper.syncThings() - } - Component.onCompleted: { setupPushNotifications(); if (autoConnectHost.length > 0) { diff --git a/nymea-app/ui/devicepages/GarageThingPage.qml b/nymea-app/ui/devicepages/GarageThingPage.qml index e12e34cf..e314ad76 100644 --- a/nymea-app/ui/devicepages/GarageThingPage.qml +++ b/nymea-app/ui/devicepages/GarageThingPage.qml @@ -79,7 +79,7 @@ DevicePageBase { Layout.alignment: Qt.AlignHCenter property string currentImage: { if (root.isExtended) { - return app.pad(Math.round(root.percentageState.value / 10), 2) + "0" + return NymeaUtils.pad(Math.round(root.percentageState.value / 10), 2) + "0" } if (root.intermediatePositionStateType) { return root.stateState.value === "closed" ? "100" diff --git a/nymea-app/ui/grouping/GroupThingsPage.qml b/nymea-app/ui/grouping/GroupThingsPage.qml index b5ea065c..6966be01 100644 --- a/nymea-app/ui/grouping/GroupThingsPage.qml +++ b/nymea-app/ui/grouping/GroupThingsPage.qml @@ -129,7 +129,7 @@ Page { device: devicesInGroup.get(index) - onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(deviceClass.interfaces)), {device: device}) + onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + NymeaUtils.interfaceListToDevicePage(deviceClass.interfaces)), {device: device}) } } diff --git a/nymea-app/ui/mainviews/FavoritesView.qml b/nymea-app/ui/mainviews/FavoritesView.qml index 48c4eaf9..d54f10b9 100644 --- a/nymea-app/ui/mainviews/FavoritesView.qml +++ b/nymea-app/ui/mainviews/FavoritesView.qml @@ -64,7 +64,7 @@ MainViewBase { height: gridView.cellHeight device: engine.deviceManager.devices.getDevice(deviceId) - onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(deviceClass.interfaces)), {device: device}) + onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + NymeaUtils.interfaceListToDevicePage(deviceClass.interfaces)), {device: device}) onPressAndHold: root.editMode = true diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml index 2ed6f93d..a705e64c 100644 --- a/nymea-app/ui/utils/NymeaUtils.qml +++ b/nymea-app/ui/utils/NymeaUtils.qml @@ -15,4 +15,53 @@ Item { } return str; } + + function interfaceListToDevicePage(interfaceList) { + print("**** getting page for interfaces", interfaceList) + var page; + if (interfaceList.indexOf("media") >= 0) { + page = "MediaDevicePage.qml"; + } else if (interfaceList.indexOf("button") >= 0) { + page = "ButtonDevicePage.qml"; + } else if (interfaceList.indexOf("powerswitch") >= 0) { + page = "ButtonDevicePage.qml"; + } else if (interfaceList.indexOf("weather") >= 0) { + page = "WeatherDevicePage.qml"; + } else if (interfaceList.indexOf("heating") >= 0 || interfaceList.indexOf("thermostat") >= 0) { + page = "HeatingDevicePage.qml"; + } else if (interfaceList.indexOf("sensor") >= 0) { + page = "SensorDevicePage.qml"; + } else if (interfaceList.indexOf("inputtrigger") >= 0) { + page = "InputTriggerDevicePage.qml"; + } else if (interfaceList.indexOf("garagedoor") >= 0 ) { + page = "GarageThingPage.qml"; + } else if (interfaceList.indexOf("light") >= 0) { + page = "LightDevicePage.qml"; + } else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) { + page = "ShutterDevicePage.qml"; + } else if (interfaceList.indexOf("awning") >= 0) { + page = "AwningDevicePage.qml"; + } else if (interfaceList.indexOf("notifications") >= 0) { + page = "NotificationsDevicePage.qml"; + } else if (interfaceList.indexOf("fingerprintreader") >= 0) { + page = "FingerprintReaderDevicePage.qml"; + } else if (interfaceList.indexOf("smartmeter") >= 0) { + page = "SmartMeterDevicePage.qml" + } else if (interfaceList.indexOf("powersocket") >= 0) { + page = "PowersocketDevicePage.qml"; + } else if (interfaceList.indexOf("doorbell") >= 0) { + page = "DoorbellDevicePage.qml"; + } else if (interfaceList.indexOf("irrigation") >= 0) { + page = "IrrigationDevicePage.qml"; + } else if (interfaceList.indexOf("ventilation") >= 0) { + page = "VentilationDevicePage.qml"; + } else if (interfaceList.indexOf("barcodescanner") >= 0) { + page = "BarcodeScannerThingPage.qml"; + } else { + page = "GenericDevicePage.qml"; + } + print("Selecting page", page, "for interface list:", interfaceList) + return page; + } + } diff --git a/packaging/android/AndroidManifest.xml b/packaging/android/AndroidManifest.xml index 85aabd73..69b817b6 100644 --- a/packaging/android/AndroidManifest.xml +++ b/packaging/android/AndroidManifest.xml @@ -63,8 +63,29 @@ - + + + + + + + + + + + + + + + + + + + + + + @@ -101,11 +122,11 @@ - - - - - + + + + + diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java index 68afe3cd..b1f54ad3 100644 --- a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlService.java @@ -26,6 +26,11 @@ import io.reactivex.processors.ReplayProcessor; import org.reactivestreams.FlowAdapters; import org.json.*; +// Android device controls service + +// This service is instantiated by the android device controls on demand. It will +// connect to the NymeaAppService and interact with nymea through that. + public class NymeaAppControlService extends ControlsProviderService { private String TAG = "nymea-app: NymeaAppControlService"; private NymeaAppServiceConnection m_serviceConnection; @@ -65,8 +70,6 @@ public class NymeaAppControlService extends ControlsProviderService { return; } -// ArrayList things = m_serviceConnection.getThings(); - for (Thing thing : m_serviceConnection.getThings()) { Log.d(TAG, "Processing thing: " + thing.name); @@ -147,6 +150,10 @@ public class NymeaAppControlService extends ControlsProviderService { actionTypeId = thing.actionByName("close").typeId; } param = ""; + } else if (thing.interfaces.contains("extendedvolumecontroller")) { + actionTypeId = thing.stateByName("volume").typeId; + FloatAction fAction = (FloatAction) action; + param = String.valueOf(Math.round(fAction.getNewValue())); } else { Log.d(TAG, "Unhandled action for: " + thing.name); consumer.accept(ControlAction.RESPONSE_FAIL); @@ -158,20 +165,31 @@ public class NymeaAppControlService extends ControlsProviderService { } + private HashMap m_intents = new HashMap(); + private Control thingToControl(Thing thing) { - Log.d(TAG, "Creating control for thing: " + thing.name + " id: " + thing.id); +// Log.d(TAG, "Creating control for thing: " + thing.name + " id: " + thing.id); + // NOTE: intentId 1 doesn't work for some reason I don't understand yet... so let's make sure we never add "1" to it by always added 100 + int intentId = m_intents.size() + 100; + PendingIntent pi; + if (m_intents.containsKey(thing.id)) { + intentId = m_intents.get(thing.id); + } else { + m_intents.put(thing.id, intentId); + } - // TODO: Create Intent to launch control view Context context = getBaseContext(); - Intent intent = new Intent(this, NymeaAppActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - PendingIntent m_pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + Intent intent = new Intent(context, NymeaAppControlsActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.putExtra("thingId", thing.id); + pi = PendingIntent.getActivity(context, intentId, intent, PendingIntent.FLAG_UPDATE_CURRENT); + Log.d(TAG, "Created pendingintent for " + thing.name + " with id " + intentId + " and extra " + thing.id); - Control.StatefulBuilder builder = new Control.StatefulBuilder(thing.id, m_pi) + Control.StatefulBuilder builder = new Control.StatefulBuilder(thing.id, pi) .setTitle(thing.name) .setSubtitle(thing.className) - .setStructure("TestLocation"); + .setStructure(m_serviceConnection.nymeaName()); if (thing.interfaces.contains("impulsebasedgaragedoor")) { builder.setDeviceType(DeviceTypes.TYPE_GARAGE); @@ -202,12 +220,24 @@ public class NymeaAppControlService extends ControlsProviderService { State powerState = thing.stateByName("power"); ControlButton controlButton = new ControlButton(powerState.value.equals("true"), powerState.displayName); builder.setControlTemplate(new ToggleTemplate(thing.id, controlButton)); + } else if (thing.interfaces.contains("mediaplayer")) { + if (thing.stateByName("playerType").value == "video") { + builder.setDeviceType(DeviceTypes.TYPE_TV); + } else { + // FIXME: There doesn't seem to be a speaker DeviceType!?! + builder.setDeviceType(DeviceTypes.TYPE_TV); + } + if (thing.interfaces.contains("extendedvolumecontroller")) { + State volumeState = thing.stateByName("volume"); + RangeTemplate rangeTemplate = new RangeTemplate(thing.id, 0, 100, Float.parseFloat(volumeState.value), 1, volumeState.displayName); + builder.setControlTemplate(rangeTemplate); + } } else { builder.setDeviceType(DeviceTypes.TYPE_GENERIC_ON_OFF); } builder.setStatus(Control.STATUS_OK); - Log.d(TAG, "Created control for thing: " + thing.name + " id: " + thing.id); +// Log.d(TAG, "Created control for thing: " + thing.name + " id: " + thing.id); return builder.build(); } } diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java index 64335642..01274bf3 100644 --- a/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppControlsActivity.java @@ -7,7 +7,42 @@ import android.os.Build; import android.telephony.TelephonyManager; import android.provider.Settings.Secure; import android.os.Vibrator; +import android.os.Process; + +// An activity spawned by android device controls on demand. public class NymeaAppControlsActivity extends org.qtproject.qt5.android.bindings.QtActivity { + private static final String TAG = "nymea-app: NymeaAppControlActivity"; + + + @Override public void onPause() { + Log.d(TAG, "Pausing..."); + System.exit(0); + } + + @Override public void onResume() { + super.onResume(); + Log.d(TAG, "Resuming..."); + } + + + @Override public void onDestroy() { + Log.d(TAG, "Destroying..."); + } + + + public String thingId() + { + Log.d(TAG, "ThingId called!"); + Log.d(TAG, getIntent().getStringExtra("thingId")); + return getIntent().getStringExtra("thingId"); + } + + public void vibrate(int duration) + { + Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); + v.vibrate(duration); + } + } diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppService.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppService.java index f5b8cef5..640a4888 100644 --- a/packaging/android/src/io/guh/nymeaapp/NymeaAppService.java +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppService.java @@ -6,6 +6,11 @@ import android.util.Log; import org.qtproject.qt5.android.bindings.QtService; +// Background service establishing a connection to nymea and providing data on android specific interfaces +// such as IBinder and BroadcastListener + +// This service loads the service_main Qt entry point and does most of its work in C++/Qt + public class NymeaAppService extends QtService { public static final String BROADCAST_STATE_CHANGE = "io.guh.nymeaapp.NymeaAppService.broadcast.stateChanged"; @@ -36,14 +41,13 @@ public class NymeaAppService extends QtService } public void sendBroadcast(String thingId, String stateTypeId, String value) { -// String name = new String(intent.getByteArrayExtra("name")); Intent sendToUiIntent = new Intent(); sendToUiIntent.setAction(BROADCAST_STATE_CHANGE); sendToUiIntent.putExtra("name", "io.guh.nymeaapp.NymeaAppService"); sendToUiIntent.putExtra("thingId", thingId); sendToUiIntent.putExtra("stateTypeId", stateTypeId); sendToUiIntent.putExtra("value", value); - Log.d(TAG, "Service sending broadcast"); +// Log.d(TAG, "Service sending broadcast"); sendBroadcast(sendToUiIntent); } } diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java index 06ef24b1..c5ff87f8 100644 --- a/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppServiceConnection.java @@ -22,6 +22,8 @@ import io.reactivex.processors.ReplayProcessor; import org.json.*; +// Helper class to establish a connection to the NymeaAppService and interact +// with that using IBinder and ServiceBroadcastListener public class NymeaAppServiceConnection implements ServiceConnection { private static final String TAG = "nymea-app: NymeaAppServiceConnection"; @@ -30,6 +32,7 @@ public class NymeaAppServiceConnection implements ServiceConnection { private boolean m_isReady = false; private Context m_context; + private String m_nymeaName = "nymea"; private ArrayList m_things = new ArrayList<>(); public NymeaAppServiceConnection(Context context) { @@ -45,6 +48,10 @@ public class NymeaAppServiceConnection implements ServiceConnection { return m_isConnectedToNymea; } + final public String nymeaName() { + return m_nymeaName; + } + final public boolean isReady() { return m_isReady; } @@ -93,6 +100,8 @@ public class NymeaAppServiceConnection implements ServiceConnection { ready = retParcel.readBoolean(); if (!ready) { Thread.sleep(100); + } else { + m_nymeaName = retParcel.readString(); } } while (!ready); Log.d(TAG, "Service connected to nymea!"); @@ -185,13 +194,13 @@ 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"); +// Log.d(TAG, "In OnReceive broadcast receiver"); if (NymeaAppService.BROADCAST_STATE_CHANGE.equals(intent.getAction())) { String name = intent.getStringExtra("name"); String thingId = intent.getStringExtra("thingId"); String stateTypeId = intent.getStringExtra("stateTypeId"); String value = intent.getStringExtra("value"); - Log.d(TAG, "Thing state changed: " + thingId + " stateTypeId: " + stateTypeId + " value: " + value); +// Log.d(TAG, "Thing state changed: " + thingId + " stateTypeId: " + stateTypeId + " value: " + value); for (int i = 0; i < m_things.size(); i++) { if (m_things.get(i).id.equals(thingId)) {