aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEgor Nemtsev <enemtsev@luxoft.com>2019-09-06 19:27:23 +0300
committerEgor Nemtsev <enemtsev@luxoft.com>2019-10-21 13:40:43 +0000
commitf9f71436818a5c2e18eab6de9117c6a611eb6ecc (patch)
tree09344e630c8521271b1f75d06fa82c574da781b4
parent23aec57aafb6b24e62e52e1ac76520ce755df272 (diff)
Authorization rework
- move widget-dependent web page interaction to qml part,so this doesn't require special build for appman - add captcha processing - use Neptune-style controls in AuthView Task-number: AUTOSUITE-1197 Change-Id: If52552eee29abc370b0aefe5ceb1edb11e9b024c Reviewed-by: Bramastyo Harimukti Santoso <bramastyo.harimukti.santoso@pelagicore.com>
-rw-r--r--app/AlexaView.qml12
-rw-r--r--app/AuthView.qml261
-rw-r--r--app/AuthWebPageInteraction.qml256
-rw-r--r--app/Header.qml7
-rw-r--r--app/MainView.qml22
-rw-r--r--app/app.pro3
-rw-r--r--plugins/alexaauth/alexaauth.cpp317
-rw-r--r--plugins/alexaauth/alexaauth.h130
-rw-r--r--plugins/alexaauth/alexaauth.pro16
-rw-r--r--plugins/alexaauth/alexaauth_plugin.cpp2
10 files changed, 561 insertions, 465 deletions
diff --git a/app/AlexaView.qml b/app/AlexaView.qml
index cae6809..7e55d12 100644
--- a/app/AlexaView.qml
+++ b/app/AlexaView.qml
@@ -65,16 +65,17 @@ Control {
target: interactionButton
height: Sizes.dp(120)
anchors.horizontalCenterOffset: -root.width/2 + interactionButton.width / 2 + Sizes.dp(50)
- anchors.topMargin: Sizes.dp(550) - interactionButton.height / 2
+ anchors.topMargin: Sizes.dp(100) - interactionButton.height / 2
}
PropertyChanges {
target: stopSpeakingButton
anchors.horizontalCenterOffset: root.width/2 - stopSpeakingButton.width / 2 - Sizes.dp(50)
- anchors.topMargin: Sizes.dp(550) - stopSpeakingButton.height / 2
+ anchors.topMargin: Sizes.dp(100) - stopSpeakingButton.height / 2
}
PropertyChanges {
target: cardPane
- height: root.height / 2
+ anchors.topMargin: interactionButton.height * 1.5
+ height: root.height - cardPane.anchors.topMargin
opacity: 1
}
PropertyChanges {
@@ -265,7 +266,7 @@ Control {
visible: opacity > 0
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
- anchors.topMargin: Sizes.dp(600)
+ anchors.topMargin: Sizes.dp(300)
width: height
height: parent.height/2 > Sizes.dp(270) ? Sizes.dp(270) : parent.height/2
background: Rectangle {
@@ -319,7 +320,7 @@ Control {
id: stopSpeakingButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
- anchors.topMargin: Sizes.dp(600) + interactionButton.height + Sizes.dp(100)
+ anchors.topMargin: interactionButton.anchors.topMargin + interactionButton.height + Sizes.dp(100)
width: parent.width > Sizes.dp(270) ? Sizes.dp(270) : parent.width
height: Sizes.dp(70)
opacity: (AlexaInterface.dialogState === Alexa.Listening) || (AlexaInterface.dialogState === Alexa.Speaking) ? 1 : 0
@@ -338,7 +339,6 @@ Control {
Item {
id: cardPane
anchors.top: parent.top
- anchors.topMargin: Sizes.dp(650)
anchors.left: parent.left
anchors.right: parent.right
height: 0
diff --git a/app/AuthView.qml b/app/AuthView.qml
index e34f35f..b787d4a 100644
--- a/app/AuthView.qml
+++ b/app/AuthView.qml
@@ -32,11 +32,10 @@
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtGraphicalEffects 1.0
-
+import QtQuick.Layouts 1.13
import QtWebView 1.1
import alexainterface 1.0
-import alexaauth 1.0
import shared.utils 1.0
import shared.controls 1.0
@@ -49,52 +48,41 @@ Control {
property var alexaAuth
property bool authorizationRequested: false
- Item {
+ ColumnLayout {
id: initStateView
anchors.top: parent.top
- anchors.topMargin: Sizes.dp(500)
- width: 0.5 * root.width
+ anchors.topMargin: Sizes.dp(200)
+ width: 0.4 * root.width
anchors.horizontalCenter: parent.horizontalCenter
opacity: 0
visible: opacity > 0
+ spacing: Sizes.dp(25)
- DropShadow {
- anchors.fill: emailField
- horizontalOffset: Sizes.dp(1)
- verticalOffset: Sizes.dp(2)
- radius: 6
- color: "#80000000"
- source: emailField.background
+ Label {
+ id: loginLabel
+ text: qsTr("Amazon account:")
+ width: parent.width
+ font.pixelSize: Sizes.fontSizeM
+ Layout.alignment: Qt.AlignHCenter
}
TextField {
id: emailField
- width: parent.width
- height: Sizes.dp(54)
color: "gray"
font.pixelSize: Sizes.fontSizeS
placeholderText: "email"
+ inputMethodHints: Qt.ImhEmailCharactersOnly
background: Rectangle {
anchors.fill: parent
- radius: 4
+ radius: Sizes.dp(4)
}
- }
-
- DropShadow {
- anchors.fill: passwordField
- horizontalOffset: Sizes.dp(1)
- verticalOffset: Sizes.dp(2)
- radius: 6
- color: "#80000000"
- source: passwordField.background
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
}
TextField {
id: passwordField
- width: parent.width
height: Sizes.dp(54)
- anchors.top: emailField.bottom
- anchors.topMargin: Sizes.dp(25)
color: "gray"
font.pixelSize: Sizes.fontSizeS
placeholderText: "password"
@@ -102,154 +90,159 @@ Control {
passwordCharacter: '*'
background: Rectangle {
anchors.fill: parent
- radius: 4
+ radius: Sizes.dp(4)
}
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
}
}
- Item {
+ ColumnLayout {
+ id: captchaView
+ anchors.top: parent.top
+ anchors.topMargin: Sizes.dp(200)
+ width: 0.5 * root.width
+ anchors.horizontalCenter: parent.horizontalCenter
+ opacity: 0
+ visible: opacity > 0
+ spacing: Sizes.dp(25)
+
+ Image {
+ id: captchaImage
+ width: Sizes.dp(sourceSize.width)
+ height: Sizes.dp(sourceSize.height)
+ source: alexaAuth.captchaUrl
+ onSourceChanged: {
+ captchaField.text = ""
+ }
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ TextField {
+ id: captchaField
+ width: parent.width
+ color: "gray"
+ text: ""
+ placeholderText: "Enter the characters you see"
+ font.pixelSize: Sizes.fontSizeS
+ background: Rectangle {
+ anchors.fill: parent
+ radius: Sizes.dp(4)
+ }
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
+ }
+ }
+
+ ColumnLayout {
id: manualAuthorization
anchors.horizontalCenter: parent.horizontalCenter
width: 0.65 * parent.width
opacity: 0
visible: opacity > 0
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ spacing: Sizes.dp(15)
Label {
id: authCode
- anchors.bottom: webView.top
- anchors.horizontalCenter: webView.horizontalCenter
- height: Sizes.dp(60)
- verticalAlignment: Text.AlignVCenter
font.pixelSize: Sizes.fontSizeM
text: "Your code: " + AlexaInterface.authCode
visible: AlexaInterface.authCode !== ""
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ Label {
+ id: errorText
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.Wrap
+ font.pixelSize: Sizes.fontSizeS
+ text: "Cannot authorize due to invalid client id. Check the client id in the AlexaClientSDKConfig.json and restart the Alexa application"
+ visible: webView.url.toString() === ""
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
}
WebView {
id: webView
- y: Sizes.dp(436) + Sizes.dp(100)
- width: parent.width
height: Sizes.dp(700)
- anchors.horizontalCenter: parent.horizontalCenter
url: AlexaInterface.authUrl
visible: url !== ""
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
}
Label {
id: helpText
- anchors.top: webView.bottom
- anchors.topMargin: Sizes.dp(15)
- anchors.horizontalCenter: parent.horizontalCenter
- width: parent.width
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
font.pixelSize: Sizes.fontSizeS
text: "Couldn't authorize automatically. Proceed by filling your email, password and authorization code in the web form."
visible: webView.url.toString() !== ""
- }
-
- Label {
- id: errorText
- anchors.centerIn: parent
- anchors.verticalCenterOffset: Sizes.dp(600)
- width: parent.width
- horizontalAlignment: Text.AlignHCenter
- wrapMode: Text.Wrap
- font.pixelSize: Sizes.fontSizeS
- text: "Cannot authorize due to invalid client id. Check the client id in the AlexaClientSDKConfig.json and restart the Alexa application"
- visible: webView.url.toString() === ""
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
}
}
- // The item holds the same position than the interaction button in AlexaView
- // The purpose is to prevent a jump when moved from AuthView to AlexaView
- Item {
- id: authButtonItem
+
+ Button {
+ id: authButton
+
+ implicitWidth: Sizes.dp(315)
+ implicitHeight: Sizes.dp(64)
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
- anchors.topMargin: Sizes.dp(600)
- height: parent.height/2 > Sizes.dp(270) ? Sizes.dp(270) : parent.height/2
- width: height
+ anchors.topMargin: Sizes.dp(500)
visible: opacity > 0
opacity: 1
-
- DropShadow {
- anchors.fill: authButton
- horizontalOffset: Sizes.dp(3)
- verticalOffset: Sizes.dp(3)
- radius: 8.0
- samples: 17
- color: "#80000000"
- source: authButton.background
- }
-
- Button {
- id: authButton
- anchors.centerIn: parent
- width: parent.width
- height: Sizes.dp(70)
- enabled: !alexaAuth.isAuthorizing && emailField.text !== "" && passwordField.text !== ""
- text: "Authorize"
- font.pixelSize: Sizes.fontSizeL
- icon.color: enabled ? Style.contrastColor : "black"
- background: ButtonBackground {
- border.color: parent.enabled ? "#5FCAF4" : "lightgray"
- color: parent.enabled ? "#5FCAF4" : "lightgray"
-
- }
- onClicked: {
- alexaAuth.email = emailField.text
- alexaAuth.password = passwordField.text
+ enabled: (!alexaAuth.isAuthorizing && emailField.text !== "" && passwordField.text !== "")
+ || (alexaAuth.isFillingCaptcha && captchaField.text !== "")
+ text: qsTr("Authorize")
+ font.pixelSize: Sizes.fontSizeS
+ onClicked: {
+ alexaAuth.email = emailField.text
+ alexaAuth.password = passwordField.text
+ if (alexaAuth.isFillingCaptcha) {
+ alexaAuth.captcha = captchaField.text
+ alexaAuth.authorizeWithCaptcha()
+ } else {
alexaAuth.authorize()
- root.authorizationRequested = true
}
+ root.authorizationRequested = true
}
}
- Row {
- anchors.top: authButtonItem.bottom
- anchors.topMargin: Sizes.dp(40)
- anchors.horizontalCenter: parent.horizontalCenter
- spacing: Sizes.dp(20)
+ ColumnLayout {
+ anchors.top: parent.top
+ width: parent.width
- Item {
- id: spinner
- width: Sizes.dp(50)
- height: width
- anchors.verticalCenter: parent.verticalCenter
+ ProgressBar {
+ id: progress
+ width: parent.width
+ implicitHeight: Sizes.dp(8)
opacity: 0
visible: opacity > 0
-
- Image {
- id: spinnerImage
- anchors.fill: parent
- fillMode: Image.PreserveAspectFit
- source: "assets/spinner.png"
- }
-
- ColorOverlay {
- id: overlay
- anchors.fill: spinnerImage
- source: spinnerImage
- color: "#63abc8"
- visible: spinnerImage.opacity > 0
- RotationAnimation on rotation {
- loops: Animation.Infinite
- from: 0
- to: 360
- duration: 1000
- running: overlay.visible
- }
+ from: 0
+ to: 1
+ value: 0
+ indeterminate: true
+ Layout.fillWidth: true
+ SequentialAnimation on value {
+ loops: Animation.Infinite
+ PropertyAnimation { to: 0; duration: 1500 }
+ PropertyAnimation { to: 1; duration: 1500 }
}
}
Label {
id: authAppText
- anchors.verticalCenter: parent.verticalCenter
- text: qsTr("Authorizing application...")
+ text: qsTr("Authorizing device...")
font.pixelSize: Sizes.fontSizeL
opacity: 0
visible: opacity > 0
+ Layout.topMargin: Sizes.dp(400)
+ Layout.alignment: Qt.AlignHCenter
}
}
@@ -259,25 +252,35 @@ Control {
PropertyChanges { target: initStateView; opacity: 1 }
},
State {
+ name: "captcha"
+ PropertyChanges { target: captchaView; opacity: 1; anchors.top: initStateView.bottom;
+ anchors.topMargin: Sizes.dp(25) }
+ PropertyChanges { target: initStateView; opacity: 1 }
+ PropertyChanges { target: progress; opacity: 0 }
+ PropertyChanges { target: authAppText; opacity: 0 }
+ PropertyChanges { target: authButton; opacity: 1; anchors.top: captchaView.bottom;
+ anchors.topMargin: Sizes.dp(25) }
+ },
+ State {
name: "automatic_auth"
PropertyChanges { target: initStateView; opacity: 0 }
PropertyChanges { target: authAppText; opacity: 0.8 }
- PropertyChanges { target: spinner; opacity: 1 }
+ PropertyChanges { target: progress; opacity: 1 }
},
State {
name: "manual_auth"
PropertyChanges { target: initStateView; opacity: 0 }
PropertyChanges { target: manualAuthorization; opacity: 1 }
- PropertyChanges { target: spinner; opacity: 0 }
- PropertyChanges { target: authButtonItem; opacity: 0 }
+ PropertyChanges { target: progress; opacity: 0 }
+ PropertyChanges { target: authButton; opacity: 0 }
PropertyChanges { target: authAppText; opacity: 0 }
},
State {
name: "complete_auth"
PropertyChanges { target: manualAuthorization; opacity: 0 }
PropertyChanges { target: authAppText; opacity: 0.8; text: "Authorization completed" }
- PropertyChanges { target: spinner; opacity: 0 }
- PropertyChanges { target: authButtonItem; opacity: 0 }
+ PropertyChanges { target: progress; opacity: 0 }
+ PropertyChanges { target: authButton; opacity: 0 }
}
]
@@ -286,7 +289,11 @@ Control {
if (!root.authorizationRequested) {
return "initial_state"
} else if (alexaAuth.isAuthorizing) {
- return "automatic_auth"
+ if (alexaAuth.isFillingCaptcha) {
+ return "captcha"
+ } else {
+ return "automatic_auth"
+ }
} else {
return "manual_auth"
}
diff --git a/app/AuthWebPageInteraction.qml b/app/AuthWebPageInteraction.qml
new file mode 100644
index 0000000..6d133b4
--- /dev/null
+++ b/app/AuthWebPageInteraction.qml
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Luxoft Sweden AB
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Neptune 3 UI.
+**
+** $QT_BEGIN_LICENSE:GPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: GPL-3.0
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtWebEngine 1.8
+
+import alexaauth 1.0
+
+QtObject {
+ id: root
+
+ property bool isAuthorizing: false
+ property string authCode: ""
+ property url authUrl: ""
+ property int error: AlexaAuth.NoError
+ property string httpUserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36"
+ property bool authorizationSucceed: false
+ property string email: ""
+ property string password: ""
+ property bool isFillingCaptcha: false
+ property url captchaUrl: ""
+ property string captcha: ""
+ property WebEngineView authPage: WebEngineView {
+ id: authPage
+
+ property int retryCount: 0
+
+ function callAuthAction() {
+ switch (AlexaAuth.getAuthStage(authPage.title)){
+ case AlexaAuth.AuthSignIn:
+ signinToAmazon();
+ break;
+ case AlexaAuth.AuthRegisterDevice:
+ registerDevice();
+ break;
+ case AlexaAuth.AuthError:
+ error = AlexaAuth.AutomaticAuthFailed;
+ break;
+ }
+ }
+
+ Component.onCompleted: {
+ authPage.profile.httpAcceptLanguage = "en-US,en;q=0.9";
+ httpUserAgent = authPage.profile.httpUserAgent;
+ }
+ onLoadingChanged: {
+ if (loadRequest.status === WebEngineLoadRequest.LoadSucceededStatus) {
+ callAuthAction()
+ }
+ if (loadRequest.status === WebEngineLoadRequest.LoadFailedStatus) {
+ if (retryCount < 2) {
+ retryCount += 1
+ callAuthAction()
+ } else {
+ error = AlexaAuth.AutomaticAuthFailed;
+ }
+ }
+ }
+ }
+
+ function authorize()
+ {
+ if (AlexaAuth.parseJson()) {
+ isAuthorizing = true;
+ authPage.url = authUrl;
+ } else {
+ error = AlexaAuth.ConfigFileFailure;
+ }
+ }
+
+ function authorizeWithCaptcha()
+ {
+ isFillingCaptcha = false
+ inputEmail()
+ inputPassword()
+ inputCaptcha()
+ }
+
+ function signinToAmazon()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.SignIn);
+ authPage.runJavaScript(js, function(cb) {
+ switch (AlexaAuth.signinToAmazonResult(cb)) {
+ case AlexaAuth.SignInInputEmail:
+ inputEmail();
+ break
+ case AlexaAuth.SignInCaptcha:
+ error = AlexaAuth.ImageRecognizionRequired;
+ showCaptcha()
+ break
+ case AlexaAuth.SignInError:
+ error = AlexaAuth.ImageRecognizionRequired;
+ showCaptcha();
+ break
+ }
+ }
+ )
+ }
+
+ function showCaptcha()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.CaptchaSrc);
+ authPage.runJavaScript(js, function(cb) {
+ if (cb === null) {
+ console.warn("Unable to get captcha")
+ } else {
+ root.isFillingCaptcha = true;
+ root.captchaUrl = cb
+ }
+ } );
+ }
+
+ function inputCaptcha()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.GetCaptchaInput);
+ authPage.runJavaScript(js, function (cb) {
+ if (cb === null) {
+ console.warn("Captcha field doesn't exist on the page.")
+ error = AlexaAuth.HtmlItemNotFound;
+ } else {
+ var jsSet = AlexaAuth.getJSString(AlexaAuth.SetCaptcha, captcha);
+ authPage.runJavaScript(jsSet);
+ clickSignIn();
+ }
+ }
+ );
+ }
+
+ function inputEmail()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.GetEmailInput);
+ authPage.runJavaScript(js, function (cb) {
+ if (cb === null) {
+ console.warn("Email field doesn't exist on the page.")
+ error = AlexaAuth.HtmlItemNotFound;
+ } else {
+ var jsSet = AlexaAuth.getJSString(AlexaAuth.SetEmail, email);
+ authPage.runJavaScript(jsSet);
+ inputPassword();
+ }
+ }
+ );
+ }
+
+ function inputPassword()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.GetPasswordInput);
+ authPage.runJavaScript(js, function (cb) {
+ if (cb === null) {
+ console.warn("Password field doesn't exist on the page")
+ error = AlexaAuth.HtmlItemNotFound;
+ } else {
+ var jsSet = AlexaAuth.getJSString(AlexaAuth.SetPassword, password);
+ authPage.runJavaScript(jsSet);
+ clickSignIn();
+ }
+ }
+ );
+ }
+
+ function clickSignIn()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.GetClickSignIn);
+ authPage.runJavaScript(js, function (cb) {
+ if (cb === null) {
+ console.warn("Sign in button doesn't exist on the page")
+ error = AlexaAuth.HtmlItemNotFound;
+ } else {
+ var jsSet = AlexaAuth.getJSString(AlexaAuth.ClickElement, js);
+ authPage.runJavaScript(jsSet);
+ }
+ }
+ );
+ }
+
+ function registerDevice()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.RegisterDeviceTitle);
+ authPage.runJavaScript(js, function(cb) {
+ switch (AlexaAuth.registerDeviceResult(cb)) {
+ case AlexaAuth.RegisterDevice:
+ inputCode();
+ break
+ case AlexaAuth.RegisterDeviceSuccess:
+ isAuthorizing = false;
+ authorizationSucceed = true;
+ break
+ case AlexaAuth.RegisterDeviceError:
+ error = AlexaAuth.AutomaticAuthFailed;
+ break
+ }
+ }
+ )
+ }
+
+ function inputCode()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.GetInputCode);
+ authPage.runJavaScript(js, function (cb) {
+ if (cb === null) {
+ console.warn("No field for authorization code!")
+ error = AlexaAuth.HtmlItemNotFound;
+ } else if (authCode.length > 0){
+ var jsSet = AlexaAuth.getJSString(AlexaAuth.SetInputCode, authCode);
+ authPage.runJavaScript(jsSet);
+ clickContinue();
+ } else {
+ error = AlexaAuth.AutomaticAuthFailed;
+ }
+ }
+ );
+ }
+
+ function clickContinue()
+ {
+ var js = AlexaAuth.getJSString(AlexaAuth.GetContinue);
+ authPage.runJavaScript(js, function (cb) {
+ if (cb === null) {
+ console.warn("Not found 'continue' button")
+ error = AlexaAuth.HtmlItemNotFound;
+ } else {
+ var jsSet = AlexaAuth.getJSString(AlexaAuth.ClickElement, js);
+ authPage.runJavaScript(jsSet);
+ }
+ }
+ );
+ }
+}
diff --git a/app/Header.qml b/app/Header.qml
index 18c91cb..19b2223 100644
--- a/app/Header.qml
+++ b/app/Header.qml
@@ -45,20 +45,17 @@ Control {
property int headerTextIndex: 0
}
- width: parent.width
- height: Sizes.dp(100)
-
contentItem: Item {
Row {
id: animatedTextRow
anchors.centerIn: parent
- anchors.verticalCenterOffset: Sizes.dp(200)
+ anchors.verticalCenterOffset: Sizes.dp(50)
spacing: Sizes.dp(35)
Image {
id: alexaLogo
anchors.verticalCenter: parent.verticalCenter
fillMode: Image.PreserveAspectFit
- height: root.height*0.7
+ height: root.height*0.25
source: "assets/logo.png"
}
Label {
diff --git a/app/MainView.qml b/app/MainView.qml
index af823de..78e6dd7 100644
--- a/app/MainView.qml
+++ b/app/MainView.qml
@@ -50,20 +50,24 @@ Item {
property string neptuneState: "Maximized"
- AlexaAuth {
+ AuthWebPageInteraction {
id: alexaAuth
- httpUserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36"
+ onErrorChanged: {
+ if (error === AlexaAuth.AutomaticAuthFailed){
+ authView.state = "manual_auth"
+ }
+ }
}
Connections {
target: AlexaInterface
onAuthCodeChanged: {
- if (authCode !== "") {
- alexaAuth.authCode = authCode
+ if (AlexaInterface.authCode !== "") {
+ alexaAuth.authCode = AlexaInterface.authCode
}
}
onAuthUrlChanged: {
- alexaAuth.authUrl = authUrl
+ alexaAuth.authUrl = AlexaInterface.authUrl
}
Component.onCompleted: {
AlexaInterface.logLevel = Alexa.Debug9
@@ -71,8 +75,11 @@ Item {
}
Header {
+ id: header
anchors.top: parent.top
anchors.topMargin: Sizes.dp(80)
+ width: parent.width
+ height: Sizes.dp(356)
anchors.horizontalCenter: parent.horizontalCenter
unfoldHeader: alexaView.visible || authView.visible
visible: root.neptuneState === "Maximized"
@@ -80,10 +87,11 @@ Item {
Item {
id: paneMainView
- anchors.top: parent.top
+ anchors.top: header.bottom
anchors.left: parent.left
anchors.right: parent.right
- height: parent.height - Sizes.dp(50)
+ // to not overlap content with on-top widget
+ height: parent.height - (header.y + header.height)
AlexaView {
id: alexaView
diff --git a/app/app.pro b/app/app.pro
index 247f50b..7926e13 100644
--- a/app/app.pro
+++ b/app/app.pro
@@ -12,7 +12,8 @@ FILES += info.yaml \
Footer.qml \
WeatherCard.qml \
InfoCard.qml \
- MainView.qml
+ MainView.qml \
+ AuthWebPageInteraction.qml
app.files = $$FILES
app.path = /apps/com.luxoft.alexa
diff --git a/plugins/alexaauth/alexaauth.cpp b/plugins/alexaauth/alexaauth.cpp
index b45b9ab..b76e495 100644
--- a/plugins/alexaauth/alexaauth.cpp
+++ b/plugins/alexaauth/alexaauth.cpp
@@ -31,129 +31,71 @@
#include "alexaauth.h"
-#ifdef ALEXA_QT_WEBENGINE
-#include <QWebEngineCookieStore>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonValue>
#include <QJsonParseError>
-#endif
#include <QDebug>
#include <QFile>
+#include <QtWebEngine/QtWebEngine>
+#include <QQuickWebEngineProfile>
AlexaAuth::AlexaAuth(QObject *parent) : QObject(parent)
{
-#ifdef ALEXA_QT_WEBENGINE
- m_authPage.profile()->clearHttpCache();
- m_authPage.profile()->cookieStore()->deleteAllCookies();
- m_authPage.profile()->setHttpAcceptLanguage("en-US,en;q=0.9");
- m_httpUserAgent = m_authPage.profile()->httpUserAgent();
- m_error = ErrorState::None;
-#else
- qDebug() << "QWebEngine not available, cannot authorize automatically.";
- m_error = AlexaAuth::WebEngineNotAvailable;
-#endif
-}
-
-void AlexaAuth::setIsAuthorizing(bool isAuthorizing)
-{
- if (m_isAuthorizing == isAuthorizing)
- return;
-
- m_isAuthorizing = isAuthorizing;
- emit isAuthorizingChanged(m_isAuthorizing);
-}
-
-void AlexaAuth::setAuthCode(QString authCode)
-{
- qDebug() << Q_FUNC_INFO << " " << authCode;
- if (m_authCode == authCode)
- return;
-
- m_authCode = authCode;
- emit authCodeChanged(m_authCode);
-}
-
-void AlexaAuth::setAuthUrl(QUrl authUrl)
-{
- qDebug() << Q_FUNC_INFO << " " << authUrl;
- if (m_authUrl == authUrl)
- return;
-
- m_authUrl = authUrl;
- emit authUrlChanged(m_authUrl);
-}
-
-void AlexaAuth::setError(AlexaAuth::ErrorState error)
-{
- if (m_error == error)
- return;
-
- if (error != AlexaAuth::None) {
- setIsAuthorizing(false);
+ QtWebEngine::initialize();
+ QQuickWebEngineProfile::defaultProfile()->cookieStore()->deleteAllCookies();
+}
+
+QString AlexaAuth::getJSString(AlexaAuth::JSAuthString id, const QString &value) const
+{
+ QString result;
+ switch (id) {
+ case SignIn:
+ result = QString("document.getElementsByClassName('") + TAG_ALERT_HEADING_ID + "')[0].textContent";
+ break;
+ case CaptchaSrc:
+ result = QString("document.getElementById('") + TAG_CAPTCHA_IMAGE_ID +"').src";
+ break;
+ case GetCaptchaInput:
+ result = QString("document.getElementById('") + TAG_CAPTCHA_GUESS_ID + "')";
+ break;
+ case SetCaptcha:
+ result = QString("document.getElementById('") + TAG_CAPTCHA_GUESS_ID + "').value='" + value + "'";
+ break;
+ case GetEmailInput:
+ result = QString("document.getElementById('") + TAG_EMAIL_ID +"')";
+ break;
+ case SetEmail:
+ result = QString("document.getElementById('") + TAG_EMAIL_ID + "').value='" + value+ "'";
+ break;
+ case GetPasswordInput:
+ result = QString("document.getElementById('") + TAG_PASSWORD_ID +"')";
+ break;
+ case SetPassword:
+ result = QString("document.getElementById('") + TAG_PASSWORD_ID + "').value='" + value + "'";
+ break;
+ case GetClickSignIn:
+ result = QString("document.getElementById('") + TAG_SIGN_IN_SUBMIT_ID + "')";
+ break;
+ case RegisterDeviceTitle:
+ result = QString("document.getElementById('") + TAG_SUCCESS_TITLE_ID + "').textContent";
+ break;
+ case GetInputCode:
+ result = QString("document.getElementById('") + TAG_REGISTRATION_FIELD_ID + "')";
+ break;
+ case SetInputCode:
+ result = QString("document.getElementById('") + TAG_REGISTRATION_FIELD_ID + "').value='" + value + "'";
+ break;
+ case GetContinue:
+ result = QString("document.getElementById('") + TAG_CONTINUE_BUTTON_ID + "')";
+ break;
+ case ClickElement:
+ result = value + ".click()";
+ break;
}
- m_error = error;
-#ifdef ALEXA_QT_WEBENGINE
- QObject::disconnect( &m_authPage, &QWebEnginePage::loadFinished, this, &AlexaAuth::authPageLoaded);
-#endif
- emit errorChanged(m_error);
+ return result;
}
-void AlexaAuth::setHttpUserAgent(QString httpUserAgent)
+bool AlexaAuth::parseJson() const
{
- qDebug() << Q_FUNC_INFO << httpUserAgent;
- if (m_httpUserAgent == httpUserAgent)
- return;
-
- m_httpUserAgent = httpUserAgent;
-#ifdef ALEXA_QT_WEBENGINE
- m_authPage.profile()->setHttpUserAgent(m_httpUserAgent);
-#endif
- emit httpUserAgentChanged(m_httpUserAgent);
-}
-
-void AlexaAuth::setAuthorizationSucceed(bool authorizationSucceed)
-{
- if (m_authorizationSucceed == authorizationSucceed)
- return;
-
- m_authorizationSucceed = authorizationSucceed;
- emit authorizationSucceedChanged(m_authorizationSucceed);
-}
-
-void AlexaAuth::setEmail(QString email)
-{
- if (m_email == email)
- return;
- m_email = email;
- emit emailChanged(m_email);
-}
-
-void AlexaAuth::setPassword(QString password)
-{
- if (m_password == password)
- return;
- m_password = password;
- emit passwordChanged(m_password);
-}
-
-void AlexaAuth::authorize()
-{
- qDebug() << Q_FUNC_INFO << " " << m_authUrl;
-#ifdef ALEXA_QT_WEBENGINE
- if (parseJson()) {
- QObject::connect( &m_authPage, &QWebEnginePage::loadFinished, this, &AlexaAuth::authPageLoaded);
- setIsAuthorizing(true);
- m_authPage.load(m_authUrl);
- }
-#endif
-}
-
-#ifdef ALEXA_QT_WEBENGINE
-bool AlexaAuth::parseJson()
-{
- qDebug() << Q_FUNC_INFO;
if (qEnvironmentVariableIsSet("ALEXA_SDK_CONFIG_FILE")) {
QFile file(qEnvironmentVariable("ALEXA_SDK_CONFIG_FILE"));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -167,9 +109,9 @@ bool AlexaAuth::parseJson()
line = in.readLine();
indx = line.indexOf("//");
if ( indx >= 0 ) {
- lines += line.mid(0, indx);
+ lines += line.mid(0, indx);
} else {
- lines += line + "\n";
+ lines += line + "\n";
}
}
file.close();
@@ -178,144 +120,55 @@ bool AlexaAuth::parseJson()
if (jsonError.error != QJsonParseError::NoError){
qDebug() << "Cannot parse AlexaClientSDKConfig.json: " << jsonError.errorString();
- setError(AlexaAuth::ConfigFileFailure);
return false;
}
return true;
} else {
qWarning() << "Couldn't open the config file AlexaClientSDKConfig.json";
- setError(AlexaAuth::ConfigFileFailure);
return false;
}
} else {
qWarning() << "Couldn't read the environment variable ALEXA_SDK_CONFIG_FILE";
- setError(AlexaAuth::ConfigFileFailure);
return false;
}
}
-void AlexaAuth::authPageLoaded(bool ok)
+AlexaAuth::AuthStage AlexaAuth::getAuthStage(const QString &title)
{
- qDebug() << Q_FUNC_INFO << " " << ok << " " << m_authPage.title();
- if (ok) {
- if (m_authPage.title() == HTML_TITLE_FIRST) {
- QTimer::singleShot(2000, this, &AlexaAuth::signinToAmazon);
-
- } else if (m_authPage.title() == HTML_TITLE_SECOND) {
- QTimer::singleShot(1000, this, &AlexaAuth::registerDevice);
-
- } else {
- qWarning() << "Unknown HTML title " << m_authPage.title();
- setError(AlexaAuth::AutomaticAuthFailed);
- }
- } else {
- qWarning() << "Something went wrong to load the auth page";
- setError(AlexaAuth::AutomaticAuthFailed);
+ if (title == HTML_TITLE_FIRST) {
+ return AuthSignIn;
+ } else if (title == HTML_TITLE_SECOND) {
+ return AuthRegisterDevice;
}
+ qWarning() << "Unknown HTML title " << title;
+ return AuthError;
}
-void AlexaAuth::signinToAmazon()
-{
- qDebug() << Q_FUNC_INFO;
- m_authPage.runJavaScript(QString("document.getElementsByClassName('") + TAG_ALERT_HEADING_ID + "')[0].textContent", [this](const QVariant &cb) {
- if (cb.isNull() || cb.toString() == "" || cb.toString() == HTML_ENABLE_COOKIES) {
- inputEMail();
- } else if (cb.toString() == HTML_IMPORTANT_MESSAGE) {
- qWarning() << "Image capture detected! Cannot proceed automatically. Please, authorize manually on " << m_authUrl;
- setError(AlexaAuth::ImageRecognizionRequired);
- } else {
- qDebug() << "Something went wrong in " << Q_FUNC_INFO << " " << cb.toString();
- setError(AlexaAuth::AutomaticAuthFailed);
- }
- });
-}
-
-void AlexaAuth::inputEMail()
+AlexaAuth::SignInResult AlexaAuth::signinToAmazonResult(const QVariant &cb)
{
- qDebug() << Q_FUNC_INFO;
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_EMAIL_ID +"')", [this](const QVariant &cb) {
- if (cb.isNull()) {
- qWarning() << "Email field doesn't exist on the page.";
- setError(AlexaAuth::HtmlItemNotFound);
- } else {
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_EMAIL_ID + "').value='" + m_email + "'");
- QTimer::singleShot(2000, this, &AlexaAuth::inputPassword);
- }
- });
-}
-
-void AlexaAuth::inputPassword()
-{
- qDebug() << Q_FUNC_INFO;
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_PASSWORD_ID + "')", [this](const QVariant &cb){
- if (cb.isNull()) {
- qWarning() << "Password field doesn't exist on the page";
- setError(AlexaAuth::HtmlItemNotFound);
- } else {
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_PASSWORD_ID + "').value='" + m_password + "'");
- QTimer::singleShot(2000, this, &AlexaAuth::clickSignIn);
- }
- });
-}
-
-void AlexaAuth::clickSignIn()
-{
- qDebug() << Q_FUNC_INFO;
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_SIGN_IN_SUBMIT_ID + "')", [this](const QVariant &cb){
- if (cb.isNull()) {
- qWarning() << "Sign in button doesn't exist on the page";
- setError(AlexaAuth::HtmlItemNotFound);
- } else {
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_SIGN_IN_SUBMIT_ID + "').click()");
- }
- });
-}
-
-void AlexaAuth::registerDevice()
-{
- qDebug() << Q_FUNC_INFO;
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_SUCCESS_TITLE_ID + "').textContent", [this](const QVariant &cb) {
- if (cb.toString() == HTML_REGISTER_DEVICE) {
- inputCode();
- } else if (cb.toString() == HTML_SUCCESS) {
- qDebug() << "Automatic authorization completed successfully.";
- setIsAuthorizing(false);
- setAuthorizationSucceed(true);
- } else {
- qDebug() << "Something went wrong in " << Q_FUNC_INFO << " " << cb.toString();
- setError(AlexaAuth::AutomaticAuthFailed);
- }
- });
-}
+ if (cb.isNull() || cb.toString() == "" || cb.toString() == HTML_ENABLE_COOKIES) {
+ return SignInInputEmail;
+ } else if (cb.toString() == HTML_IMPORTANT_MESSAGE) {
+ qWarning() << "Image capture detected!";
+ return SignInCaptcha;
+ } else if (cb.toString() == HTML_TITLE_ERROR_CAPTCHA) {
+ qWarning() << "Image capture detected!, wrong captcha";
+ return SignInCaptcha;
+ }
-void AlexaAuth::inputCode()
-{
- qDebug() << Q_FUNC_INFO;
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_REGISTRATION_FIELD_ID + "')" , [this] (const QVariant &cb) {
- if (cb.isNull()) {
- qWarning() << "No field for authorization code!";
- setError(AlexaAuth::HtmlItemNotFound);
- } else if (m_authCode.length() > 0) {
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_REGISTRATION_FIELD_ID + "').value='" + m_authCode + "'");
- QTimer::singleShot(2000, this, &AlexaAuth::clickContinue);
- } else {
- qDebug() << "Authorization code was empty";
- setError(AlexaAuth::AutomaticAuthFailed);
- }
- });
+ qDebug() << "Something went wrong in " << Q_FUNC_INFO << " " << cb.toString();
+ return SignInError;
}
-void AlexaAuth::clickContinue()
+AlexaAuth::RegisterDeviceResult AlexaAuth::registerDeviceResult(const QVariant &cb)
{
- qDebug() << Q_FUNC_INFO;
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_CONTINUE_BUTTON_ID + "')", [this] (const QVariant &cb) {
- if (cb.isNull()) {
- qWarning() << "Not found 'continue' button";
- setError(AlexaAuth::HtmlItemNotFound);
- } else {
- m_authPage.runJavaScript(QString("document.getElementById('") + TAG_CONTINUE_BUTTON_ID + "').click()");
- }
- });
- // todo: check what happens if the code was wrong
+ if (cb.toString() == HTML_REGISTER_DEVICE) {
+ return AlexaAuth::RegisterDevice;
+ } else if (cb.toString() == HTML_SUCCESS) {
+ qDebug() << "Automatic authorization completed successfully.";
+ return AlexaAuth::RegisterDeviceSuccess;
+ } else {
+ qDebug() << "Something went wrong in " << Q_FUNC_INFO << " " << cb.toString();
+ return AlexaAuth::RegisterDeviceError;
+ }
}
-#endif
diff --git a/plugins/alexaauth/alexaauth.h b/plugins/alexaauth/alexaauth.h
index be81e79..b61efac 100644
--- a/plugins/alexaauth/alexaauth.h
+++ b/plugins/alexaauth/alexaauth.h
@@ -35,9 +35,8 @@
#include <QObject>
#include <QTimer>
#include <QUrl>
-#ifdef ALEXA_QT_WEBENGINE
-#include <QWebEnginePage>
-#include <QWebEngineProfile>
+#include <QQmlEngine>
+
// ## Step 1, login to amazon.developer.com
#define HTML_TITLE_FIRST "Amazon Sign-In"
@@ -47,9 +46,12 @@
#define TAG_EMAIL_ID "ap_email"
#define TAG_PASSWORD_ID "ap_password"
#define TAG_SIGN_IN_SUBMIT_ID "signInSubmit"
+#define TAG_CAPTCHA_IMAGE_ID "auth-captcha-image"
+#define TAG_CAPTCHA_GUESS_ID "auth-captcha-guess"
// ## Step 2, give authorization code
#define HTML_TITLE_SECOND "Amazon Two-Step Verification"
+#define HTML_TITLE_ERROR_CAPTCHA "There was a problem"
#define TAG_REGISTRATION_FIELD_ID "cbl-registration-field"
#define TAG_CONTINUE_BUTTON_ID "cbl-continue-button"
@@ -57,25 +59,14 @@
#define HTML_REGISTER_DEVICE "Register Your Device"
#define HTML_SUCCESS "Success!"
#define TAG_SUCCESS_TITLE_ID "cbl-page-title"
-#endif
class AlexaAuth : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool isAuthorizing READ isAuthorizing NOTIFY isAuthorizingChanged)
- Q_PROPERTY(QString authCode READ authCode WRITE setAuthCode NOTIFY authCodeChanged)
- Q_PROPERTY(QUrl authUrl READ authUrl WRITE setAuthUrl NOTIFY authUrlChanged)
- Q_PROPERTY(ErrorState error READ error NOTIFY errorChanged)
- Q_PROPERTY(QString httpUserAgent READ httpUserAgent WRITE setHttpUserAgent NOTIFY httpUserAgentChanged)
- Q_PROPERTY(bool authorizationSucceed READ authorizationSucceed NOTIFY authorizationSucceedChanged)
- Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
- Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
-
public:
-
enum ErrorState {
- None,
+ NoError,
WebEngineNotAvailable,
ConfigFileFailure,
HtmlItemNotFound,
@@ -84,64 +75,61 @@ public:
};
Q_ENUM(ErrorState)
+ enum AuthStage {
+ AuthSignIn,
+ AuthRegisterDevice,
+ AuthError
+ };
+ Q_ENUM(AuthStage)
+
+ enum SignInResult {
+ SignInInputEmail,
+ SignInCaptcha,
+ SignInError
+ };
+ Q_ENUM(SignInResult)
+
+ enum RegisterDeviceResult {
+ RegisterDevice,
+ RegisterDeviceSuccess,
+ RegisterDeviceError
+ };
+ Q_ENUM(RegisterDeviceResult)
+
+ enum JSAuthString {
+ SignIn,
+ CaptchaSrc,
+ GetCaptchaInput,
+ SetCaptcha,
+ GetEmailInput,
+ SetEmail,
+ GetPasswordInput,
+ SetPassword,
+ GetClickSignIn,
+ RegisterDeviceTitle,
+ GetInputCode,
+ SetInputCode,
+ GetContinue,
+ ClickElement
+ };
+ Q_ENUM(JSAuthString)
+
explicit AlexaAuth(QObject *parent = nullptr);
- Q_INVOKABLE void authorize();
-
- bool isAuthorizing() const { return m_isAuthorizing; }
- QString authCode() const { return m_authCode; }
- QUrl authUrl() const { return m_authUrl; }
- ErrorState error() const { return m_error; }
- QString httpUserAgent() const { return m_httpUserAgent; }
- bool authorizationSucceed() const { return m_authorizationSucceed; }
- QString email() const { return m_email; }
- QString password() const { return m_password; }
-
- void setIsAuthorizing(bool isAuthorizing);
- void setAuthCode(QString authCode);
- void setAuthUrl(QUrl authUrl);
- void setError(AlexaAuth::ErrorState error);
- void setHttpUserAgent(QString httpUserAgent);
- void setAuthorizationSucceed(bool authorizationSucceed);
- void setEmail(QString email);
- void setPassword(QString password);
-
-
-signals:
- void isAuthorizingChanged(bool isAuthorizing);
- void authCodeChanged(QString authCode);
- void authUrlChanged(QUrl authUrl);
- void errorChanged(AlexaAuth::ErrorState error);
- void httpUserAgentChanged(QString httpUserAgent);
- void authorizationSucceedChanged(bool authorizationSucceed);
- void emailChanged(QString email);
- void passwordChanged(QString password);
-
-public slots:
-
-
-private:
-#ifdef ALEXA_QT_WEBENGINE
- bool parseJson();
- void authPageLoaded(bool ok);
- void signinToAmazon();
- void inputEMail();
- void inputPassword();
- void clickSignIn();
- void registerDevice();
- void inputCode();
- void clickContinue();
-
- QWebEnginePage m_authPage;
-#endif
- QString m_authCode;
- bool m_isAuthorizing = false;
- QUrl m_authUrl;
- ErrorState m_error = ErrorState::None;
- QString m_httpUserAgent;
- bool m_authorizationSucceed = false;
- QString m_email;
- QString m_password;
+ Q_INVOKABLE bool parseJson() const;
+ Q_INVOKABLE AlexaAuth::AuthStage getAuthStage(const QString &title);
+ Q_INVOKABLE QString getJSString(AlexaAuth::JSAuthString id, const QString &value = "") const;
+ Q_INVOKABLE AlexaAuth::SignInResult signinToAmazonResult(const QVariant &cb);
+ Q_INVOKABLE AlexaAuth::RegisterDeviceResult registerDeviceResult(const QVariant &cb);
};
+static QObject *alexaAuthSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+
+ AlexaAuth *singletonObject = new AlexaAuth();
+ return singletonObject;
+}
+
#endif // ALEXAAUTH_H
diff --git a/plugins/alexaauth/alexaauth.pro b/plugins/alexaauth/alexaauth.pro
index fdd2c14..f075d0f 100644
--- a/plugins/alexaauth/alexaauth.pro
+++ b/plugins/alexaauth/alexaauth.pro
@@ -1,20 +1,6 @@
TEMPLATE = lib
TARGET = alexaauth
-QT += qml quick
-
-# Is Qt Application manager compiled with 'enable-widgets' configuration.
-# Do not change without recompiling the Qt Application Manger.
-QAPPMAN_ENABLES_WIDGETS = 0
-
-equals(QAPPMAN_ENABLES_WIDGETS, 1) {
- qtHaveModule(webenginewidgets) {
- QT += webenginewidgets
- DEFINES += ALEXA_QT_WEBENGINE
- }
- else {
- message("Qt module webenginewidgets is not available.")
- }
-}
+QT += qml quick webengine
CONFIG += plugin c++14
diff --git a/plugins/alexaauth/alexaauth_plugin.cpp b/plugins/alexaauth/alexaauth_plugin.cpp
index 03f31a8..94e8728 100644
--- a/plugins/alexaauth/alexaauth_plugin.cpp
+++ b/plugins/alexaauth/alexaauth_plugin.cpp
@@ -36,6 +36,6 @@
void AlexaAuthPlugin::registerTypes(const char *uri)
{
- qmlRegisterType<AlexaAuth>(uri, 1, 0, "AlexaAuth");
+ qmlRegisterSingletonType<AlexaAuth>(uri, 1, 0, "AlexaAuth", alexaAuthSingletonProvider);
}