diff --git a/androidservice/androidservice.pro b/androidservice/androidservice.pro new file mode 100644 index 00000000..8b6cfe84 --- /dev/null +++ b/androidservice/androidservice.pro @@ -0,0 +1,52 @@ +TEMPLATE = lib +TARGET = service +CONFIG += dll +QT += core androidextras +QT += network qml quick quickcontrols2 svg websockets bluetooth charts + +include(../config.pri) +include(../android_openssl/openssl.pri) + + +INCLUDEPATH += $$top_srcdir/libnymea-app/ + +# https://bugreports.qt.io/browse/QTBUG-83165 +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 += \ + controlviews/devicecontrolapplication.cpp \ + nymeaappservice/nymeaappservice.cpp \ + nymeaappservice/androidbinder.cpp \ + ../nymea-app/stylecontroller.cpp \ + ../nymea-app/platformhelper.cpp \ + ../nymea-app/platformintegration/android/platformhelperandroid.cpp \ + service_main.cpp + +HEADERS += \ + controlviews/devicecontrolapplication.h \ + nymeaappservice/nymeaappservice.h \ + nymeaappservice/androidbinder.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 \ + controlviews/Main.qml + diff --git a/androidservice/controlviews/Main.qml b/androidservice/controlviews/Main.qml new file mode 100644 index 00000000..c87839d2 --- /dev/null +++ b/androidservice/controlviews/Main.qml @@ -0,0 +1,57 @@ +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}) + PlatformHelper.hideSplashScreen(); + } + + 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..1b319967 --- /dev/null +++ b/androidservice/controlviews/devicecontrolapplication.cpp @@ -0,0 +1,66 @@ +#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"); + + QString nymeaId = QtAndroid::androidActivity().callObjectMethod("nymeaId").toString(); + QString thingId = QtAndroid::androidActivity().callObjectMethod("thingId").toString(); + + QSettings settings; + + NymeaDiscovery *discovery = new NymeaDiscovery(this); + AWSClient::instance()->setConfig(settings.value("cloudEnvironment").toString()); + discovery->setAwsClient(AWSClient::instance()); + NymeaHost *host = 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); + } + + Engine *m_engine = new Engine(this); + + qDebug() << "Connecting to:" << host; + m_engine->jsonRpcClient()->connectToHost(host); + + qDebug() << "Creating QML view"; + QQmlApplicationEngine *qmlEngine = new QQmlApplicationEngine(this); + + 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/androidbinder.cpp b/androidservice/nymeaappservice/androidbinder.cpp new file mode 100644 index 00000000..b0213fc5 --- /dev/null +++ b/androidservice/nymeaappservice/androidbinder.cpp @@ -0,0 +1,115 @@ +#include "androidbinder.h" +#include "engine.h" +#include "types/device.h" + +#include +#include +#include +#include +#include + +AndroidBinder::AndroidBinder(NymeaAppService *service): + m_service(service) +{ +} + +bool AndroidBinder::onTransact(int code, const QAndroidParcel &data, const QAndroidParcel &reply, QAndroidBinder::CallType flags) +{ + qDebug() << "onTransact: code " << code << ", flags " << int(flags); + +// QString payload = data.readData(); + QString payload = data.handle().callObjectMethod("readString").toString(); + + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(payload.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "Error parsing JSON from parcel:" << error.errorString(); + qWarning() << payload; + return false; + } + QVariantMap request = jsonDoc.toVariant().toMap(); + + if (request.value("method").toString() == "GetInstances") { + QVariantMap params; + QVariantList instances; + foreach (const QUuid &nymeaId, m_service->engines().keys()) { + Engine *engine = m_service->engines().value(nymeaId); + QVariantMap instance; + instance.insert("id", nymeaId); + instance.insert("isReady", engine->jsonRpcClient()->connected() && !engine->thingManager()->fetchingData()); + instance.insert("name", engine->jsonRpcClient()->currentHost()->name()); + instances.append(instance); + } + params.insert("instances", instances); + sendReply(reply, params); + return true; + } + + if (request.value("method").toString() == "GetThings") { + QUuid nymeaId = request.value("params").toMap().value("nymeaId").toUuid(); + Engine *engine = m_service->engines().value(nymeaId); + if (!engine) { + qWarning() << "Android client requested things for an invalid nymea instance:" << nymeaId; + return false; + } + QVariantList thingsList; + for (int i = 0; i < engine->thingManager()->things()->rowCount(); i++) { + Device *thing = engine->thingManager()->things()->get(i); + QVariantMap thingMap; + thingMap.insert("id", thing->id()); + thingMap.insert("name", thing->name()); + thingMap.insert("className", thing->thingClass()->displayName()); + thingMap.insert("interfaces", thing->thingClass()->interfaces()); + QVariantList states; + for (int j = 0; j < thing->states()->rowCount(); j++) { + State *state = thing->states()->get(j); + QVariantMap stateMap; + stateMap.insert("stateTypeId", state->stateTypeId()); + stateMap.insert("name", thing->thingClass()->stateTypes()->getStateType(state->stateTypeId())->name()); + stateMap.insert("displayName", thing->thingClass()->stateTypes()->getStateType(state->stateTypeId())->displayName()); + stateMap.insert("value", state->value()); + states.append(stateMap); + } + thingMap.insert("states", states); + QVariantList actions; + for (int j = 0; j < thing->thingClass()->actionTypes()->rowCount(); j++) { + ActionType *actionType = thing->thingClass()->actionTypes()->get(j); + QVariantMap actionMap; + actionMap.insert("actionTypeId", actionType->id()); + actionMap.insert("name", actionType->name()); + actionMap.insert("displayName", actionType->displayName()); + actions.append(actionMap); + } + thingMap.insert("actions", actions); + thingsList.append(thingMap); + } + QVariantMap params; + params.insert("things", thingsList); + sendReply(reply, params); + return true; + } + + if (request.value("method").toString() == "ExecuteAction") { + qDebug() << "ExecuteAction"; + QUuid nymeaId = request.value("params").toMap().value("nymeaId").toUuid(); + Engine *engine = m_service->engines().value(nymeaId); + if (!engine) { + qWarning() << "Android client requested executeAction for an invalid nymea instance:" << nymeaId; + return false; + } + QUuid thingId = request.value("params").toMap().value("thingId").toUuid(); + QUuid actionTypeId = request.value("params").toMap().value("actionTypeId").toUuid(); + QVariantList params = request.value("params").toMap().value("params").toList(); + + qDebug() << "**** executeAction:" << thingId << actionTypeId << params; + engine->thingManager()->executeAction(thingId, actionTypeId, params); + } + + return false; +} + +void AndroidBinder::sendReply(const QAndroidParcel &reply, const QVariantMap ¶ms) +{ + QString payload = QJsonDocument::fromVariant(params).toJson(); + reply.handle().callMethod("writeString", "(Ljava/lang/String;)V", QAndroidJniObject::fromString(payload).object()); +} diff --git a/androidservice/nymeaappservice/androidbinder.h b/androidservice/nymeaappservice/androidbinder.h new file mode 100644 index 00000000..36a9b795 --- /dev/null +++ b/androidservice/nymeaappservice/androidbinder.h @@ -0,0 +1,23 @@ +#ifndef ANDROIDBINDER_H +#define ANDROIDBINDER_H + +#include + +#include "nymeaappservice.h" +#include "engine.h" + +class AndroidBinder : public QAndroidBinder +{ +public: + explicit AndroidBinder(NymeaAppService *service); + + bool onTransact(int code, const QAndroidParcel &data, const QAndroidParcel &reply, QAndroidBinder::CallType flags) override; + +private: + void sendReply(const QAndroidParcel &reply, const QVariantMap ¶ms); + +private: + NymeaAppService *m_service = nullptr; +}; + +#endif // ANDROIDBINDER_H diff --git a/androidservice/nymeaappservice/nymeaappservice.cpp b/androidservice/nymeaappservice/nymeaappservice.cpp new file mode 100644 index 00000000..d2c673ff --- /dev/null +++ b/androidservice/nymeaappservice/nymeaappservice.cpp @@ -0,0 +1,83 @@ +#include "nymeaappservice.h" +#include "androidbinder.h" + +#include +#include +#include +#include + +#include "connection/discovery/nymeadiscovery.h" +#include "connection/nymeahosts.h" + +NymeaAppService::NymeaAppService(int argc, char **argv): + QAndroidService(argc, argv, [=](const QAndroidIntent &) { + return new AndroidBinder{this}; + }) +{ + setApplicationName("nymea-app"); + setOrganizationName("nymea"); + + QSettings settings; + + NymeaDiscovery *discovery = new NymeaDiscovery(this); + AWSClient::instance()->setConfig(settings.value("cloudEnvironment").toString()); + discovery->setAwsClient(AWSClient::instance()); + + + for (int i = 0; i < 5; i++) { + settings.beginGroup(QString("tabSettings%1").arg(i)); + QUuid lastConnected = settings.value("lastConnectedHost").toUuid(); + settings.endGroup(); + + if (lastConnected.isNull()) { + continue; + } + NymeaHost *host = discovery->nymeaHosts()->find(lastConnected); + if (!host) { + continue; + } + + Engine *engine = new Engine(this); + engine->jsonRpcClient()->connectToHost(host); + m_engines.insert(host->uuid(), engine); + + + QObject::connect(engine->thingManager(), &DeviceManager::thingStateChanged, [=](const QUuid &thingId, const QUuid &stateTypeId, const QVariant &value){ + QVariantMap params; + params.insert("nymeaId", engine->jsonRpcClient()->currentHost()->uuid()); + params.insert("thingId", thingId); + params.insert("stateTypeId", stateTypeId); + params.insert("value", value); + sendNotification("ThingStateChanged", params); + }); + + connect(engine->thingManager(), &DeviceManager::fetchingDataChanged, [=]() { + qDebug() << "Fetching data changed"; + QVariantMap params; + params.insert("nymeaId", engine->jsonRpcClient()->currentHost()->uuid()); + params.insert("isReady", !engine->thingManager()->fetchingData()); + qDebug() << "Nymea host is ready" << engine->jsonRpcClient()->currentHost()->uuid(); + sendNotification("ReadyStateChanged", params); + }); + } + + qDebug() << "NymeaAppService started."; + +} + +QHash NymeaAppService::engines() const +{ + return m_engines; +} + +void NymeaAppService::sendNotification(const QString ¬ification, const QVariantMap ¶ms) +{ + QVariantMap data; + data.insert("notification", notification); + data.insert("params", params); + QString payload = QJsonDocument::fromVariant(data).toJson(); + QtAndroid::androidService().callMethod("sendBroadcast", + "(Ljava/lang/String;)V", + QAndroidJniObject::fromString(payload).object()); + +} diff --git a/androidservice/nymeaappservice/nymeaappservice.h b/androidservice/nymeaappservice/nymeaappservice.h new file mode 100644 index 00000000..6f6e653a --- /dev/null +++ b/androidservice/nymeaappservice/nymeaappservice.h @@ -0,0 +1,25 @@ +#ifndef NYMEAAPPSERVICE_H +#define NYMEAAPPSERVICE_H + +#include + +#include "engine.h" + +class NymeaAppService : public QAndroidService +{ + Q_OBJECT +public: + explicit NymeaAppService(int argc, char** argv); + + QHash engines() const; + +private: + void sendNotification(const QString ¬ification, const QVariantMap ¶ms); + + +private: + QHash m_engines; + +}; + +#endif // NYMEAAPPSERVICE_H diff --git a/androidservice/service_main.cpp b/androidservice/service_main.cpp new file mode 100644 index 00000000..46efab22 --- /dev/null +++ b/androidservice/service_main.cpp @@ -0,0 +1,37 @@ +#include +#include + +#include "nymeaappservice/nymeaappservice.h" +#include "controlviews/devicecontrolapplication.h" + +#include +#include + +int main(int argc, char *argv[]) +{ + qWarning() << "Service starting from a separate .so file"; + + QLoggingCategory::setFilterRules("qt.remoteobjects.debug=true\n"); + + 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); + + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + 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/config.pri b/config.pri index 9d0cae52..1dfee639 100644 --- a/config.pri +++ b/config.pri @@ -11,3 +11,6 @@ APP_REVISION=$$member(VERSION_INFO, 1) DEFINES+=APP_VERSION=\\\"$${APP_VERSION}\\\" android:QMAKE_POST_LINK += cp $$top_srcdir/version.txt $$top_builddir/ + +DISTFILES += \ + $$PWD/packaging/android/src/io/guh/nymeaapp/NymeaHost.java diff --git a/libnymea-app/devicemanager.cpp b/libnymea-app/devicemanager.cpp index a2eaafd1..b2ce20d7 100644 --- a/libnymea-app/devicemanager.cpp +++ b/libnymea-app/devicemanager.cpp @@ -175,10 +175,11 @@ void DeviceManager::notificationReceived(const QVariantMap &data) qWarning() << "Device state change notification received for an unknown device"; return; } - QUuid stateTyoeId = data.value("params").toMap().value("stateTypeId").toUuid(); + QUuid stateTypeId = data.value("params").toMap().value("stateTypeId").toUuid(); QVariant value = data.value("params").toMap().value("value"); -// qDebug() << "Device state changed for:" << dev->name() << "State name:" << dev->thingClass()->stateTypes()->getStateType(stateTyoeId) << "value:" << value; - dev->setStateValue(stateTyoeId, value); +// qDebug() << "Device state changed for:" << dev->name() << "State name:" << dev->thingClass()->stateTypes()->getStateType(stateTypeId) << "value:" << value; + dev->setStateValue(stateTypeId, value); + emit thingStateChanged(dev->id(), stateTypeId, value); } else if (notification == "Devices.DeviceAdded") { Device *dev = JsonTypes::unpackDevice(this, data.value("params").toMap().value("device").toMap(), m_thingClasses); if (!dev) { diff --git a/libnymea-app/devicemanager.h b/libnymea-app/devicemanager.h index b09d3ddc..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, @@ -151,7 +152,8 @@ signals: void fetchingDataChanged(); void notificationReceived(const QString &deviceId, const QString &eventTypeId, const QVariantList ¶ms); - void eventTriggered(const QString &deviceId, const QString &eventTypeId, const QVariantMap params); + void eventTriggered(const QUuid &deviceId, const QUuid &eventTypeId, const QVariantMap params); + void thingStateChanged(const QUuid &deviceId, const QUuid &stateTypeId, const QVariant &value); private: Vendors *m_vendors; diff --git a/libnymea-app/engine.h b/libnymea-app/engine.h index 300b7fb4..07b38c6a 100644 --- a/libnymea-app/engine.h +++ b/libnymea-app/engine.h @@ -61,9 +61,6 @@ class Engine : public QObject public: explicit Engine(QObject *parent = nullptr); - bool connected() const; - QString connectedHost() const; - DeviceManager *deviceManager() const; DeviceManager *thingManager() const; RuleManager *ruleManager() const; diff --git a/libnymea-common/libnymea-common.h b/libnymea-common/libnymea-common.h deleted file mode 100644 index 1fd15938..00000000 --- a/libnymea-common/libnymea-common.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -#if defined(LIBNYMEA_COMMON) -# define LIBNYMEA_COMMON_EXPORT Q_DECL_EXPORT -#else -# define LIBNYMEA_COMMON_EXPORT Q_DECL_IMPORT -#endif diff --git a/libnymea-common/libnymea-common.pro b/libnymea-common/libnymea-common.pro deleted file mode 100644 index a7e17797..00000000 --- a/libnymea-common/libnymea-common.pro +++ /dev/null @@ -1,15 +0,0 @@ -include(../config.pri) - -TARGET = nymea-common -TEMPLATE = lib -CONFIG += staticlib - -QT -= gui -QT += network - -HEADERS += \ - - -SOURCES += \ - - diff --git a/nymea-app.pro b/nymea-app.pro index da9e334c..7a4e805b 100644 --- a/nymea-app.pro +++ b/nymea-app.pro @@ -82,6 +82,12 @@ icons.path = /usr/share/ INSTALLS += desktopfile icons } +# Android service +android: { +SUBDIRS += androidservice +androidservice.depends = libnymea-app +} + # Linux desktop (snap package) snap: { desktopfile.files = packaging/linux/nymea-app.desktop diff --git a/nymea-app/nymea-app.pro b/nymea-app/nymea-app.pro index 3f368f1d..8bb1b85e 100644 --- a/nymea-app/nymea-app.pro +++ b/nymea-app/nymea-app.pro @@ -81,6 +81,8 @@ android { # https://bugreports.qt.io/browse/QTBUG-83165 LIBS += -L$${top_builddir}/libnymea-app/$${ANDROID_TARGET_ARCH} + + ANDROID_ABIS = armeabi-v7a arm64-v8a } macx: { @@ -160,4 +162,3 @@ BR=$$BRANDING target.path = /usr/bin INSTALLS += target - diff --git a/nymea-app/platformintegration/android/platformhelperandroid.cpp b/nymea-app/platformintegration/android/platformhelperandroid.cpp index 7332b8e9..7dcdb315 100644 --- a/nymea-app/platformintegration/android/platformhelperandroid.cpp +++ b/nymea-app/platformintegration/android/platformhelperandroid.cpp @@ -33,6 +33,7 @@ #include #include #include +#include // WindowManager.LayoutParams @@ -47,13 +48,16 @@ static PlatformHelperAndroid *m_instance; static QAndroidJniObject getAndroidWindow() { QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;"); - window.callMethod("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - window.callMethod("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS); return window; } PlatformHelperAndroid::PlatformHelperAndroid(QObject *parent) : PlatformHelper(parent) { +// QAndroidIntent serviceIntent(QtAndroid::androidActivity().object(), "io.guh.nymeaapp.NymeaAppControlService"); + +// m_serviceConnection = new DeviceControlServiceConnection(); +// QtAndroid::bindService(serviceIntent, *m_serviceConnection, QtAndroid::BindFlag::AutoCreate); + m_instance = this; } @@ -123,6 +127,33 @@ 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() + +//// QAndroidIntent serviceIntent(QtAndroid::androidActivity().object(), +//// "io/guh/nymeaapp/NymeaAppControlService"); +//// serviceIntent.putExtra("name", QByteArray("foobar")); + + +//// 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) { PlatformHelper::setTopPanelColor(color); @@ -132,6 +163,8 @@ void PlatformHelperAndroid::setTopPanelColor(const QColor &color) QtAndroid::runOnAndroidThread([=]() { QAndroidJniObject window = getAndroidWindow(); + window.callMethod("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.callMethod("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS); window.callMethod("setStatusBarColor", "(I)V", color.rgba()); }); diff --git a/nymea-app/platformintegration/android/platformhelperandroid.h b/nymea-app/platformintegration/android/platformhelperandroid.h index b648b755..fed19a80 100644 --- a/nymea-app/platformintegration/android/platformhelperandroid.h +++ b/nymea-app/platformintegration/android/platformhelperandroid.h @@ -31,9 +31,11 @@ #ifndef PLATFORMHELPERANDROID_H #define PLATFORMHELPERANDROID_H -#include #include "platformhelper.h" + +#include #include +#include class PlatformHelperAndroid : public PlatformHelper { @@ -62,7 +64,6 @@ public: private: static void permissionRequestFinished(const QtAndroid::PermissionResultMap &); - }; #endif // PLATFORMHELPERANDROID_H 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/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 4bbbaaf7..0b5b5f28 100644 --- a/packaging/android/AndroidManifest.xml +++ b/packaging/android/AndroidManifest.xml @@ -62,8 +62,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -73,9 +126,15 @@ + + + + + + - +