Merge PR #193: Improve Kiosk experience
This commit is contained in:
commit
56c4999dfd
@ -177,8 +177,12 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
|
||||
certificateFingerprint.append(digest.mid(i,1).toHex().toUpper());
|
||||
}
|
||||
|
||||
// Ignore self signed certs for connections to localhost
|
||||
if (QHostAddress(transport->url().host()) == QHostAddress::LocalHost || QHostAddress(transport->url().host()) == QHostAddress::LocalHostIPv6) {
|
||||
ignoredErrors.append(error);
|
||||
|
||||
// Check old style fingerprint storage
|
||||
if (storedFingerPrint == certificateFingerprint) {
|
||||
} else if (storedFingerPrint == certificateFingerprint) {
|
||||
qDebug() << "This fingerprint is known to us.";
|
||||
ignoredErrors.append(error);
|
||||
|
||||
|
||||
@ -271,6 +271,7 @@ void JsonRpcClient::processCreateUser(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "create user response:" << data;
|
||||
if (data.value("status").toString() == "success" && data.value("params").toMap().value("error").toString() == "UserErrorNoError") {
|
||||
emit createUserSucceeded();
|
||||
m_initialSetupRequired = false;
|
||||
emit initialSetupRequiredChanged();
|
||||
} else {
|
||||
|
||||
@ -93,6 +93,7 @@ signals:
|
||||
void invalidProtocolVersion(const QString &actualVersion, const QString &minimumVersion);
|
||||
void authenticationFailed();
|
||||
void pushButtonAuthFailed();
|
||||
void createUserSucceeded();
|
||||
void createUserFailed(const QString &error);
|
||||
void cloudConnectionStateChanged();
|
||||
|
||||
|
||||
@ -75,6 +75,8 @@ int main(int argc, char *argv[])
|
||||
parser.addHelpOption();
|
||||
QCommandLineOption kioskOption = QCommandLineOption({"k", "kiosk"}, "Start the application in kiosk mode.");
|
||||
parser.addOption(kioskOption);
|
||||
QCommandLineOption connectOption = QCommandLineOption({"c", "connect"}, "Connect to nymea:core without discovery.", "host");
|
||||
parser.addOption(connectOption);
|
||||
parser.process(application);
|
||||
|
||||
// Initialize app log controller as early as possible, but after setting app name etc
|
||||
@ -124,6 +126,7 @@ int main(int argc, char *argv[])
|
||||
engine->rootContext()->setContextProperty("styleController", &styleController);
|
||||
|
||||
engine->rootContext()->setContextProperty("kioskMode", parser.isSet(kioskOption));
|
||||
engine->rootContext()->setContextProperty("autoConnectHost", parser.value(connectOption));
|
||||
|
||||
engine->rootContext()->setContextProperty("systemProductType", QSysInfo::productType());
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ linux:!android:LIBS += -lavahi-client -lavahi-common
|
||||
PRE_TARGETDEPS += ../libnymea-app-core ../libnymea-common
|
||||
|
||||
HEADERS += \
|
||||
platformintegration/generic/raspberrypihelper.h \
|
||||
stylecontroller.h \
|
||||
pushnotifications.h \
|
||||
platformhelper.h \
|
||||
@ -24,6 +25,7 @@ HEADERS += \
|
||||
applogcontroller.h
|
||||
|
||||
SOURCES += main.cpp \
|
||||
platformintegration/generic/raspberrypihelper.cpp \
|
||||
stylecontroller.cpp \
|
||||
pushnotifications.cpp \
|
||||
platformhelper.cpp \
|
||||
|
||||
@ -4,3 +4,18 @@ PlatformHelper::PlatformHelper(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool PlatformHelper::canControlScreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int PlatformHelper::screenTimeout() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PlatformHelper::setScreenTimeout(int screenTimeout)
|
||||
{
|
||||
Q_UNUSED(screenTimeout)
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ class PlatformHelper : public QObject
|
||||
Q_PROPERTY(QString deviceModel READ deviceModel CONSTANT)
|
||||
Q_PROPERTY(QString deviceManufacturer READ deviceManufacturer CONSTANT)
|
||||
Q_PROPERTY(QString machineHostname READ machineHostname CONSTANT)
|
||||
Q_PROPERTY(bool canControlScreen READ canControlScreen CONSTANT)
|
||||
Q_PROPERTY(int screenTimeout READ screenTimeout WRITE setScreenTimeout NOTIFY screenTimeoutChanged)
|
||||
|
||||
public:
|
||||
enum HapticsFeedback {
|
||||
@ -35,10 +37,15 @@ public:
|
||||
virtual QString deviceModel() const = 0;
|
||||
virtual QString deviceManufacturer() const = 0;
|
||||
|
||||
virtual bool canControlScreen() const;
|
||||
virtual int screenTimeout() const;
|
||||
virtual void setScreenTimeout(int screenTimeout);
|
||||
|
||||
Q_INVOKABLE virtual void vibrate(HapticsFeedback feedbackType) = 0;
|
||||
|
||||
signals:
|
||||
void permissionsRequestFinished();
|
||||
void screenTimeoutChanged();
|
||||
};
|
||||
|
||||
#endif // PLATFORMHELPER_H
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
PlatformHelperGeneric::PlatformHelperGeneric(QObject *parent) : PlatformHelper(parent)
|
||||
{
|
||||
|
||||
m_piHelper = new RaspberryPiHelper(this);
|
||||
}
|
||||
|
||||
void PlatformHelperGeneric::requestPermissions()
|
||||
@ -49,6 +49,24 @@ QString PlatformHelperGeneric::deviceManufacturer() const
|
||||
return QSysInfo::productType();
|
||||
}
|
||||
|
||||
bool PlatformHelperGeneric::canControlScreen() const
|
||||
{
|
||||
return m_piHelper->active();
|
||||
}
|
||||
|
||||
int PlatformHelperGeneric::screenTimeout() const
|
||||
{
|
||||
return m_piHelper->screenTimeout();
|
||||
}
|
||||
|
||||
void PlatformHelperGeneric::setScreenTimeout(int timeout)
|
||||
{
|
||||
if (m_piHelper->screenTimeout() != timeout) {
|
||||
m_piHelper->setScreenTimeout(timeout);
|
||||
emit screenTimeoutChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformHelperGeneric::vibrate(PlatformHelper::HapticsFeedback feedbyckType)
|
||||
{
|
||||
Q_UNUSED(feedbyckType)
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include "platformhelper.h"
|
||||
#include "raspberrypihelper.h"
|
||||
|
||||
class PlatformHelperGeneric : public PlatformHelper
|
||||
{
|
||||
@ -21,10 +22,14 @@ public:
|
||||
virtual QString deviceModel() const override;
|
||||
virtual QString deviceManufacturer() const override;
|
||||
|
||||
Q_INVOKABLE virtual void vibrate(HapticsFeedback feedbyckType) override;
|
||||
signals:
|
||||
virtual bool canControlScreen() const override;
|
||||
virtual int screenTimeout() const override;
|
||||
virtual void setScreenTimeout(int timeout) override;
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE virtual void vibrate(HapticsFeedback feedbyckType) override;
|
||||
|
||||
private:
|
||||
RaspberryPiHelper *m_piHelper = nullptr;
|
||||
};
|
||||
|
||||
#endif // PLATFORMHELPERGENERIC_H
|
||||
|
||||
106
nymea-app/platformintegration/generic/raspberrypihelper.cpp
Normal file
106
nymea-app/platformintegration/generic/raspberrypihelper.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "raspberrypihelper.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QApplication>
|
||||
#include <QWindow>
|
||||
#include <QSettings>
|
||||
|
||||
RaspberryPiHelper::RaspberryPiHelper(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_sysFsFile.setFileName("/sys/class/backlight/rpi_backlight/bl_power");
|
||||
bool available = m_sysFsFile.open(QFile::ReadWrite | QFile::Text);
|
||||
|
||||
if (!available) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Raspberry Pi detected. Enabling backlight control";
|
||||
|
||||
screenOn();
|
||||
|
||||
foreach (QWindow *w, qApp->topLevelWindows()) {
|
||||
w->installEventFilter(this);
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
m_screenOffTimer.setInterval(settings.value("screenOffTimeout", 15000).toInt());
|
||||
m_screenOffTimer.setSingleShot(true);
|
||||
connect(&m_screenOffTimer, &QTimer::timeout, this, &RaspberryPiHelper::screenOff);
|
||||
if (m_screenOffTimer.interval() > 0) {
|
||||
m_screenOffTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
bool RaspberryPiHelper::active() const
|
||||
{
|
||||
return m_sysFsFile.isOpen();
|
||||
}
|
||||
|
||||
int RaspberryPiHelper::screenTimeout() const
|
||||
{
|
||||
return m_screenOffTimer.interval();
|
||||
}
|
||||
|
||||
void RaspberryPiHelper::setScreenTimeout(int timeout)
|
||||
{
|
||||
m_screenOffTimer.setInterval(timeout);
|
||||
QSettings settings;
|
||||
settings.setValue("screenOffTimeout", timeout);
|
||||
if (timeout > 0) {
|
||||
m_screenOffTimer.start();
|
||||
} else {
|
||||
m_screenOffTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool RaspberryPiHelper::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (m_screenOffTimer.interval() == 0) {
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
QList<QEvent::Type> watchedTypes = {
|
||||
QEvent::ActivationChange,
|
||||
QEvent::ApplicationStateChange,
|
||||
QEvent::KeyPress,
|
||||
QEvent::KeyRelease,
|
||||
QEvent::MouseButtonPress,
|
||||
QEvent::MouseButtonRelease,
|
||||
QEvent::MouseMove,
|
||||
QEvent::Show,
|
||||
QEvent::TouchBegin,
|
||||
QEvent::TouchEnd,
|
||||
QEvent::TouchUpdate,
|
||||
};
|
||||
if (!watchedTypes.contains(event->type())) {
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
if (!m_screenOffTimer.isActive()) {
|
||||
screenOn();
|
||||
m_screenOffTimer.start();
|
||||
return true;
|
||||
}
|
||||
m_screenOffTimer.start( );
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void RaspberryPiHelper::screenOn()
|
||||
{
|
||||
qDebug() << "Turning screen on";
|
||||
int ret = m_sysFsFile.write("0\n");
|
||||
m_sysFsFile.flush();
|
||||
if (ret < 0) {
|
||||
qWarning() << "Failed to power on screen";
|
||||
}
|
||||
}
|
||||
|
||||
void RaspberryPiHelper::screenOff()
|
||||
{
|
||||
qDebug() << "Turning screen off";
|
||||
int ret = m_sysFsFile.write("1\n");
|
||||
m_sysFsFile.flush();
|
||||
if (ret < 0) {
|
||||
qWarning() << "Failed to power off screen";
|
||||
}
|
||||
}
|
||||
29
nymea-app/platformintegration/generic/raspberrypihelper.h
Normal file
29
nymea-app/platformintegration/generic/raspberrypihelper.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef RASPBERRYPIHELPER_H
|
||||
#define RASPBERRYPIHELPER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QFile>
|
||||
|
||||
class RaspberryPiHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RaspberryPiHelper(QObject *parent = nullptr);
|
||||
|
||||
bool active() const;
|
||||
int screenTimeout() const;
|
||||
void setScreenTimeout(int timeout);
|
||||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void screenOn();
|
||||
void screenOff();
|
||||
|
||||
private:
|
||||
QTimer m_screenOffTimer;
|
||||
QFile m_sysFsFile;
|
||||
};
|
||||
|
||||
#endif // RASPBERRYPIHELPER_H
|
||||
@ -2,7 +2,7 @@
|
||||
<qresource prefix="/">
|
||||
<file>ui/Nymea.qml</file>
|
||||
<file>ui/SettingsPage.qml</file>
|
||||
<file>ui/LoginPage.qml</file>
|
||||
<file>ui/connection/LoginPage.qml</file>
|
||||
<file>ui/MagicPage.qml</file>
|
||||
<file>ui/PushButtonAuthPage.qml</file>
|
||||
<file>ui/KeyboardLoader.qml</file>
|
||||
@ -182,5 +182,6 @@
|
||||
<file>ui/magic/SelectStatePage.qml</file>
|
||||
<file>ui/system/SystemUpdatePage.qml</file>
|
||||
<file>ui/components/UpdateRunningOverlay.qml</file>
|
||||
<file>ui/connection/SetupWizard.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -1,24 +1,32 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick 2.4
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitHeight: childrenRect.height
|
||||
implicitHeight: active ? childrenRect.height : 0
|
||||
property bool active: d.kbd && d.kbd.active
|
||||
|
||||
Behavior on implicitHeight { NumberAnimation { duration: 130; easing.type: Easing.InOutQuad } }
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property var kbd: null
|
||||
property string virtualKeyboardString:
|
||||
'
|
||||
import QtQuick 2.8;
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
InputPanel {
|
||||
id: inputPanel
|
||||
y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
property string virtualKeyboardString:
|
||||
'
|
||||
import QtQuick 2.8;
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
InputPanel {
|
||||
id: inputPanel
|
||||
y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
'
|
||||
|
||||
Component.onCompleted: {
|
||||
if (useVirtualKeyboard) {
|
||||
var kbd = Qt.createQmlObject(virtualKeyboardString, root);
|
||||
d.kbd = Qt.createQmlObject(d.virtualKeyboardString, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,141 +0,0 @@
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "components"
|
||||
|
||||
Page {
|
||||
id: root
|
||||
signal backPressed();
|
||||
|
||||
header: GuhHeader {
|
||||
text: qsTr("Welcome to %1!").arg(app.systemName)
|
||||
backButtonVisible: true
|
||||
onBackPressed: root.backPressed()
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: engine.jsonRpcClient
|
||||
onAuthenticationFailed: {
|
||||
var popup = errorDialog.createObject(root)
|
||||
popup.text = qsTr("Sorry, that wasn't right. Try again please.")
|
||||
popup.open();
|
||||
}
|
||||
onCreateUserFailed: {
|
||||
print("createUser failed")
|
||||
var message;
|
||||
switch (error) {
|
||||
case "UserErrorInvalidUserId":
|
||||
message = qsTr("The email you've entered isn't valid.")
|
||||
break;
|
||||
case "UserErrorDuplicateUserId":
|
||||
message = qsTr("The email you've entered is already used.")
|
||||
break;
|
||||
case "UserErrorBadPassword":
|
||||
message = qsTr("The password you've chose is too weak.")
|
||||
break;
|
||||
case "UserErrorBackendError":
|
||||
message = qsTr("An error happened with the user storage. Please make sure your %1 box is installed correctly.")
|
||||
break;
|
||||
}
|
||||
var popup = errorDialog.createObject(root, {text: message});
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.jsonRpcClient.initialSetupRequired ?
|
||||
qsTr("In order to use your %1 system, please enter your email address and set a password for your nymea box.").arg(app.systemName)
|
||||
: qsTr("In order to use your %1 system, please log in.").arg(app.systemName)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
text: qsTr("Your e-mail address:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
placeholderText: "john.smith@cooldomain.com"
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Password:")
|
||||
}
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
visible: engine.jsonRpcClient.initialSetupRequired
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Confirm password:")
|
||||
}
|
||||
TextField {
|
||||
id: confirmPasswordTextField
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
visible: engine.jsonRpcClient.initialSetupRequired
|
||||
opacity: (passwordTextField.text.length > 0 && passwordTextField.text.length < 8) || passwordTextField.text != confirmPasswordTextField.text ? 1 : 0
|
||||
text: passwordTextField.text.length < 8 ? qsTr("This password isn't long enought to be secure, add some more characters please.")
|
||||
: qsTr("The passwords don't match.")
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.preferredHeight: confirmPasswordTextField.height * 2
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("OK")
|
||||
enabled: usernameTextField.text.length >= 5 && passwordTextField.text.length >= 8
|
||||
&& (!engine.jsonRpcClient.initialSetupRequired || confirmPasswordTextField.text == passwordTextField.text)
|
||||
onClicked: {
|
||||
if (engine.jsonRpcClient.initialSetupRequired) {
|
||||
print("create user")
|
||||
engine.jsonRpcClient.createUser(usernameTextField.text, passwordTextField.text);
|
||||
} else {
|
||||
print("authenticate", usernameTextField.text, passwordTextField.text, "nymea-app")
|
||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.text, "nymea-app");
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: errorDialog
|
||||
ErrorDialog {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,6 @@ Page {
|
||||
ColorIcon {
|
||||
height: app.iconSize / 2
|
||||
width: height
|
||||
visible: infoPane.connectedState !== null && infoPane.connectedState.value === false
|
||||
color: "white"
|
||||
name: "../images/system-update.svg"
|
||||
RotationAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; running: engine.systemController.updateRunning }
|
||||
|
||||
@ -54,6 +54,7 @@ ApplicationWindow {
|
||||
RootItem {
|
||||
id: rootItem
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: keyboardRect.height
|
||||
}
|
||||
|
||||
NymeaDiscovery {
|
||||
|
||||
@ -33,31 +33,30 @@ Page {
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter }
|
||||
anchors.margins: app.margins
|
||||
spacing: app.margins
|
||||
spacing: app.margins * 2
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: app.accentColor
|
||||
text: qsTr("Authentication required")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
|
||||
ColorIcon {
|
||||
height: app.iconSize * 2
|
||||
width: height
|
||||
color: app.accentColor
|
||||
name: "../images/info.svg"
|
||||
}
|
||||
|
||||
Label {
|
||||
color: app.accentColor
|
||||
text: qsTr("Authentication required")
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
Image {
|
||||
Layout.preferredWidth: app.iconSize * 6
|
||||
Layout.preferredHeight: width
|
||||
source: "images/nymea-box-setup.svg"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("Please press the button on your %1 box to authenticate this device.").arg(app.systemName)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
@ -105,8 +105,10 @@ Item {
|
||||
|
||||
Component.onCompleted: {
|
||||
setupPushNotifications();
|
||||
|
||||
if (tabSettings.lastConnectedHost.length > 0) {
|
||||
if (autoConnectHost.length > 0) {
|
||||
var host = discovery.nymeaHosts.createLanHost("Manual connection", autoConnectHost);
|
||||
engine.connection.connect(host)
|
||||
} else if (tabSettings.lastConnectedHost.length > 0) {
|
||||
print("Last connected host was", tabSettings.lastConnectedHost)
|
||||
var cachedHost = discovery.nymeaHosts.find(tabSettings.lastConnectedHost);
|
||||
if (cachedHost) {
|
||||
@ -143,7 +145,17 @@ Item {
|
||||
})
|
||||
return;
|
||||
} else {
|
||||
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
||||
if (engine.jsonRpcClient.initialSetupRequired) {
|
||||
var page = pageStack.push(Qt.resolvedUrl("connection/SetupWizard.qml"));
|
||||
page.backPressed.connect(function() {
|
||||
tabSettings.lastConnectedHost = "";
|
||||
engine.connection.disconnect()
|
||||
init();
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
var page = pageStack.push(Qt.resolvedUrl("connection/LoginPage.qml"));
|
||||
page.backPressed.connect(function() {
|
||||
tabSettings.lastConnectedHost = "";
|
||||
engine.connection.disconnect()
|
||||
|
||||
@ -89,6 +89,35 @@ Page {
|
||||
checked: settings.showConnectionTabs
|
||||
onClicked: settings.showConnectionTabs = checked
|
||||
}
|
||||
CheckDelegate {
|
||||
id: screenOffCheck
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Turn screen off when idle")
|
||||
visible: PlatformHelper.canControlScreen
|
||||
checked: PlatformHelper.screenTimeout > 0
|
||||
onClicked: PlatformHelper.screenTimeout = (checked ? 15000 : 0)
|
||||
}
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: screenOffCheck.height
|
||||
visible: PlatformHelper.screenTimeout > 0
|
||||
topPadding: 0
|
||||
contentItem: RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Screen off timeout")
|
||||
}
|
||||
SpinBox {
|
||||
value: PlatformHelper.screenTimeout / 1000
|
||||
onValueModified: {
|
||||
PlatformHelper.screenTimeout = value * 1000
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: qsTr("seconds")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
|
||||
@ -12,7 +12,6 @@ Page {
|
||||
|
||||
Component.onCompleted: {
|
||||
print("Ready to connect")
|
||||
|
||||
pageStack.push(discoveryPage, StackView.Immediate)
|
||||
}
|
||||
|
||||
|
||||
158
nymea-app/ui/connection/LoginPage.qml
Normal file
158
nymea-app/ui/connection/LoginPage.qml
Normal file
@ -0,0 +1,158 @@
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
id: root
|
||||
signal backPressed();
|
||||
|
||||
header: GuhHeader {
|
||||
text: qsTr("Welcome to %1!").arg(app.systemName)
|
||||
backButtonVisible: true
|
||||
onBackPressed: root.backPressed()
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: engine.jsonRpcClient
|
||||
onAuthenticationFailed: {
|
||||
var popup = errorDialog.createObject(root)
|
||||
popup.text = qsTr("Sorry, that wasn't right. Try again please.")
|
||||
popup.open();
|
||||
}
|
||||
onCreateUserSucceeded: {
|
||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.text, "nymea-app");
|
||||
}
|
||||
|
||||
onCreateUserFailed: {
|
||||
print("createUser failed")
|
||||
var message;
|
||||
switch (error) {
|
||||
case "UserErrorInvalidUserId":
|
||||
message = qsTr("The email you've entered isn't valid.")
|
||||
break;
|
||||
case "UserErrorDuplicateUserId":
|
||||
message = qsTr("The email you've entered is already used.")
|
||||
break;
|
||||
case "UserErrorBadPassword":
|
||||
message = qsTr("The password you've chose is too weak.")
|
||||
break;
|
||||
case "UserErrorBackendError":
|
||||
message = qsTr("An error happened with the user storage. Please make sure your %1 box is installed correctly.")
|
||||
break;
|
||||
}
|
||||
var popup = errorDialog.createObject(root, {text: message});
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: contentColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
width: parent.width
|
||||
|
||||
spacing: app.margins
|
||||
|
||||
RowLayout {
|
||||
Layout.margins: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: app.iconSize * 2
|
||||
name: "../images/lock-closed.svg"
|
||||
color: app.accentColor
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.jsonRpcClient.initialSetupRequired ?
|
||||
qsTr("In order to use your %1 system, please enter your email address and set a password for your %1 box.").arg(app.systemName)
|
||||
: qsTr("In order to use your %1 system, please log in.").arg(app.systemName)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
columns: app.width > 400 ? 2 : 1
|
||||
|
||||
Label {
|
||||
text: qsTr("Your e-mail address:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
placeholderText: "john.smith@cooldomain.com"
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Password:")
|
||||
}
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: engine.jsonRpcClient.initialSetupRequired
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Confirm password:")
|
||||
}
|
||||
TextField {
|
||||
id: confirmPasswordTextField
|
||||
visible: engine.jsonRpcClient.initialSetupRequired
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
visible: engine.jsonRpcClient.initialSetupRequired
|
||||
opacity: (passwordTextField.text.length > 0 && passwordTextField.text.length < 8) || passwordTextField.text != confirmPasswordTextField.text ? 1 : 0
|
||||
text: passwordTextField.text.length < 8 ? qsTr("This password isn't long enought to be secure, add some more characters please.")
|
||||
: qsTr("The passwords don't match.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: app.accentColor
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
|
||||
text: qsTr("OK")
|
||||
enabled: usernameTextField.text.length >= 5 && passwordTextField.text.length >= 8
|
||||
&& (!engine.jsonRpcClient.initialSetupRequired || confirmPasswordTextField.text == passwordTextField.text)
|
||||
onClicked: {
|
||||
if (engine.jsonRpcClient.initialSetupRequired) {
|
||||
print("create user")
|
||||
engine.jsonRpcClient.createUser(usernameTextField.text, passwordTextField.text);
|
||||
} else {
|
||||
print("authenticate", usernameTextField.text, passwordTextField.text, "nymea-app")
|
||||
engine.jsonRpcClient.authenticate(usernameTextField.text, passwordTextField.text, "nymea-app");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: errorDialog
|
||||
ErrorDialog {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
28
nymea-app/ui/connection/SetupWizard.qml
Normal file
28
nymea-app/ui/connection/SetupWizard.qml
Normal file
@ -0,0 +1,28 @@
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
id: root
|
||||
signal backPressed();
|
||||
|
||||
header: GuhHeader {
|
||||
text: qsTr("First setup")
|
||||
backButtonVisible: true
|
||||
onBackPressed: root.backPressed()
|
||||
}
|
||||
|
||||
EmptyViewPlaceholder {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - app.margins * 2
|
||||
title: qsTr("Welcome to %1!").arg(app.systemName)
|
||||
text: qsTr("This %1 system has not been set up yet. This wizard will guide you through a few simple steps to set it up.").arg(app.systemName)
|
||||
imageSource: "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
|
||||
buttonText: qsTr("Next")
|
||||
onButtonClicked: {
|
||||
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
[Seat:*]
|
||||
autologin-user=nymea
|
||||
user-session=nymea-app-kiosk
|
||||
|
||||
xserver-command=X -nocursor
|
||||
@ -1,2 +1,3 @@
|
||||
#!/bin/sh
|
||||
/usr/bin/nymea-app --kiosk
|
||||
export QT_IM_MODULE=qtvirtualkeyboard
|
||||
/usr/bin/nymea-app --kiosk --connect nymeas://127.0.0.1:2222
|
||||
|
||||
2
packaging/linux-common/udev/90-pi-backlight.rules
Normal file
2
packaging/linux-common/udev/90-pi-backlight.rules
Normal file
@ -0,0 +1,2 @@
|
||||
ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chgrp nymea /sys/class/backlight/%k/bl_power"
|
||||
ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chmod g+w /sys/class/backlight/%k/bl_power"
|
||||
@ -50,5 +50,9 @@ Section: shells
|
||||
Multi-Arch: same
|
||||
Depends: nymea-app,
|
||||
openbox,
|
||||
lightdm,
|
||||
qtvirtualkeyboard-plugin,
|
||||
qtdeclarative5-folderlistmodel-plugin,
|
||||
Provides: lightdm-greeter
|
||||
Description: Run nymea:app in kiosk mode
|
||||
This package will install nymea:app in kiosk mode on your machine.
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
packaging/linux-common/nymea-app-kiosk.desktop /usr/share/xsessions/
|
||||
packaging/linux-common/lightdm/40-nymea-app-kiosk.conf /usr/share/lightdm/lightdm.conf.d/
|
||||
packaging/linux-common/lightdm/60-nymea-app-kiosk.conf /usr/share/lightdm/lightdm.conf.d/
|
||||
packaging/linux-common/nymea-app-kiosk-wrapper /usr/bin/
|
||||
packaging/linux-common/udev/90-pi-backlight.rules /lib/udev/rules.d/
|
||||
|
||||
Reference in New Issue
Block a user