From 06e76bc287278c343ea076636f3caf3005909d25 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sun, 20 Jun 2021 15:09:53 +0200 Subject: [PATCH] Implement Qr-code generation and scanning --- blueROCK.pro | 6 + headers/bluerockbackend.h | 8 +- resources/qml/Components/QrCodeScanPopup.qml | 203 +++++++++++++++++++ resources/qml/Components/ResultDelegate.qml | 6 +- resources/qml/Components/SharePopup.qml | 89 ++++++-- resources/qml/Pages/StartPage.qml | 22 +- resources/qml/Pages/WidgetPage.qml | 23 ++- resources/qml/Widgets/ResultWidget.qml | 2 +- resources/qml/main.qml | 53 +++-- resources/qml/qml.qrc | 1 + resources/shared/PosterTemplate.xcf | Bin 246154 -> 246154 bytes sources/bluerockbackend.cpp | 46 +++-- sources/main.cpp | 4 + 13 files changed, 400 insertions(+), 63 deletions(-) create mode 100644 resources/qml/Components/QrCodeScanPopup.qml diff --git a/blueROCK.pro b/blueROCK.pro index a5c666b..718c049 100644 --- a/blueROCK.pro +++ b/blueROCK.pro @@ -98,6 +98,12 @@ ios { xcode_product_bundle_identifier_setting.value = "de.itsblue.bluerock" } +CONFIG += enable_decoder_qr_code \ + enable_encoder_qr_code \ + qzxing_multimedia \ + qzxing_qml +include(qzxing/src/QZXing-components.pri) + # this has to be the last line! ANDROID_ABIS = armeabi-v7a arm64-v8a diff --git a/headers/bluerockbackend.h b/headers/bluerockbackend.h index b03b146..0819bb9 100644 --- a/headers/bluerockbackend.h +++ b/headers/bluerockbackend.h @@ -33,6 +33,7 @@ #include #include #include +#include "QZXing.h" #include "shareUtils/shareutils.h" @@ -43,9 +44,10 @@ public: explicit BlueRockBackend(QObject *parent = nullptr); private: - QVariantMap senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery()); + QVariantMap _senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery()); ShareUtils* _shareUtils; + const QStringList _validBaseDomains = {"digitalrock.de", "bluerock.dev"}; signals: @@ -53,8 +55,8 @@ public slots: QVariant getWidgetData(QVariantMap params); QVariantMap getParamsFromUrl(QString url); - void shareResultsAsUrl(QString url); - void shareResultsAsPoster(QString url); + void shareResultsAsUrl(QString url, QString compName); + void shareResultsAsPoster(QString url, QString compName); }; diff --git a/resources/qml/Components/QrCodeScanPopup.qml b/resources/qml/Components/QrCodeScanPopup.qml new file mode 100644 index 0000000..a02f341 --- /dev/null +++ b/resources/qml/Components/QrCodeScanPopup.qml @@ -0,0 +1,203 @@ +import QtQuick 2.0 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import QZXing 3.1 +import QtMultimedia 5.12 +import QtQuick.Shapes 1.12 +import QtQuick.Controls.Material 2.12 + + +Dialog { + id: control + + property string _statusText: "" + property string _statusColor: Material.primaryTextColor + property bool _freezeScanning: false + + parent: Overlay.overlay + + x: (parent.width - width) * 0.5 + y: (parent.height - height) * 0.5 + + height: app.height * 0.8 + width: app.width * 0.8 + + modal: true + title: "Scan QR-Code" + + standardButtons: Dialog.Cancel + + onOpened: { + setDefaultStatusText() + control._freezeScanning = false + cameraLoader.sourceComponent = cameraComponent + } + + onClosed: cameraLoader.sourceComponent = null + + function setDefaultStatusText() { + _statusText = "Place the Code in the center" + _statusColor = Material.primaryTextColor + } + + contentItem: Loader { + id: cameraLoader + + asynchronous: true + sourceComponent: null + } + + Component { + id: cameraComponent + Item { + anchors.fill: parent + + Camera { + id: camera + captureMode: Camera.CaptureStillImage + imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceAuto + + focus { + focusMode: Camera.FocusContinuous + focusPointMode: Camera.FocusPointCenter + } + } + + VideoOutput { + id: videoOutput + x: 0 + y: 0 + width: parent.width + height: parent.height + + fillMode: VideoOutput.PreserveAspectCrop + + source: camera + filters: [ zxingFilter ] + focus : visible // to receive focus and capture key events when visible + + autoOrientation: true + + MouseArea { + anchors.fill: parent + + onClicked: { + if (camera.lockStatus !== Camera.Unlocked) + camera.unlock(); + + camera.searchAndLock(); + } + } + + Rectangle { + anchors { + top: parent.top + left: parent.left + right: app.landscape() ? focusIndicatorRect.left : parent.right + bottom: app.landscape() ? parent.bottom : focusIndicatorRect.top + } + + opacity: focusIndicatorRect.opacity + color: focusIndicatorRect.border.color + } + + Rectangle { + id: focusIndicatorRect + anchors.centerIn: parent + + width: Math.min(parent.height, parent.width) + height: width + + border.width: width * 0.1 + border.color: "#000000" + + opacity: 0.3 + color: "transparent" + } + + Rectangle { + anchors { + bottom: focusIndicatorRect.bottom + bottomMargin: height * 0.5 + horizontalCenter: focusIndicatorRect.horizontalCenter + } + + width: (focusIndicatorRect.width - focusIndicatorRect.border.width * 2) * 0.8 + height: focusIndicatorRect.border.width + + radius: height * 0.3 + + color: Material.backgroundColor + + Material.elevation: 10 + + + Label { + anchors { + fill: parent + margins: height * 0.1 + } + + color: control._statusColor + + font.pixelSize: height * 0.5 + fontSizeMode: Text.Fit + minimumPixelSize: height * 0.2 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: control._statusText + } + } + + Rectangle { + anchors { + top: app.landscape() ? parent.top : focusIndicatorRect.bottom + left: app.landscape() ? focusIndicatorRect.right : parent.left + right: parent.right + bottom: parent.bottom + } + + opacity: focusIndicatorRect.opacity + color: focusIndicatorRect.border.color + } + } + } + } + + + QZXingFilter { + id: zxingFilter + + decoder { + onTagFound: { + if(control._freezeScanning) + return + + control._freezeScanning = true + + control._statusText = "Plase wait..." + + if(app.openWidgetFromUrl(tag)) + control.close() + else { + control._statusText = "Invalid QR-Code" + control._statusColor = Material.color(Material.Red) + statusTextResetTimer.start() + control._freezeScanning = false + } + } + + enabledDecoders: QZXing.DecoderFormat_QR_CODE + } + } + + Timer { + id: statusTextResetTimer + running: false + repeat: false + interval: 3000 + onTriggered: setDefaultStatusText() + } +} diff --git a/resources/qml/Components/ResultDelegate.qml b/resources/qml/Components/ResultDelegate.qml index 790e083..dd8125b 100644 --- a/resources/qml/Components/ResultDelegate.qml +++ b/resources/qml/Components/ResultDelegate.qml @@ -226,7 +226,7 @@ ColoredItemDelegate { // outline context.lineWidth = 1; - context.strokeStyle = '#424242'; + context.strokeStyle = Material.primaryTextColor; context.stroke(); if(resultData[1] > 0){ @@ -254,7 +254,7 @@ ColoredItemDelegate { // outline context.lineWidth = 1; - context.strokeStyle = '#424242'; + context.strokeStyle = Material.primaryTextColor; context.stroke(); @@ -279,7 +279,7 @@ ColoredItemDelegate { // outline context.lineWidth = 1; - context.strokeStyle = '#424242'; + context.strokeStyle = Material.primaryTextColor; context.stroke(); } } diff --git a/resources/qml/Components/SharePopup.qml b/resources/qml/Components/SharePopup.qml index be10465..d566d02 100644 --- a/resources/qml/Components/SharePopup.qml +++ b/resources/qml/Components/SharePopup.qml @@ -1,11 +1,13 @@ import QtQuick 2.0 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 +import QZXing 3.1 Dialog { id: control property string _shareUrl + property string _compName parent: Overlay.overlay @@ -13,31 +15,78 @@ Dialog { y: (parent.height - height) * 0.5 modal: true - title: "Share these results" - contentItem: RowLayout { - Repeater { - id: buttonRepeater - property var buttons: [ - ["\uf0c1", "Link", serverConn.shareResultsAsUrl], - ["\uf029", "QR-code", null], - ["\uf1c1", "Poster", serverConn.shareResultsAsPoster], - ] - - model: buttons - - delegate: Button { - flat: true - font.family: fa5solid.name - text: "" + modelData[0] + "

" + modelData[1] + " " - onClicked: buttonRepeater.buttons[index][2](_shareUrl) - } - } + onClosed: { + shareComponentLoader.sourceComponent = null } - function appear(shareUrl) { + contentItem: Loader { + id: shareComponentLoader + + asynchronous: false + sourceComponent: null + } + + Component { + id: shareComponent + StackLayout { + id: stackLayout + currentIndex: 0 + + RowLayout { + id: menuRow + Repeater { + id: buttonRepeater + property var buttons: [ + ["\uf0c1", "Link", serverConn.shareResultsAsUrl], + ["\uf029", "QR-code", function() { + stackLayout.currentIndex = 1 + } + ], + ["\uf1c1", "Poster", serverConn.shareResultsAsPoster], + ] + + model: buttons + + delegate: Button { + flat: true + font.family: fa5solid.name + text: "" + modelData[0] + "

" + modelData[1] + " " + onClicked: buttonRepeater.buttons[index][2](_shareUrl, _compName) + } + } + } + + + Image { + id: qrCodeImage + + property int size: stackLayout.currentIndex === 1 ? (app.landscape() ? app.height * 0.8 : app.width * 0.8):menuRow.height + + Layout.preferredHeight: size + Layout.preferredWidth: size + + sourceSize.width: size + sourceSize.height: size + + fillMode: Image.PreserveAspectFit + + source: "image://QZXing/encode/" + _shareUrl + "?border=true&correctionLevel=H" + + Behavior on size { + NumberAnimation { + duration: 200 + } + } + } + } + } + + function appear(shareUrl, compName) { _shareUrl = shareUrl + _compName = compName + shareComponentLoader.sourceComponent = shareComponent control.open() } } diff --git a/resources/qml/Pages/StartPage.qml b/resources/qml/Pages/StartPage.qml index 5878095..7734679 100644 --- a/resources/qml/Pages/StartPage.qml +++ b/resources/qml/Pages/StartPage.qml @@ -38,7 +38,7 @@ Page { topMargin: root.height * 0.03 } - height: menuGr.buttonSize * 0.3 + height: app.landscape() ? menuGr.buttonSize * 0.2:menuGr.buttonSize * 0.3 } GridLayout { @@ -84,7 +84,7 @@ Page { } } - GridLayout { + Grid { id: footerMenu anchors { @@ -93,6 +93,11 @@ Page { horizontalCenter: parent.horizontalCenter } + width: app.landscape() ? childrenRect.width : parent.width * 0.8 + height: app.landscape() ? headerBadge.height : headerBadge.height * 2 + + columnSpacing: height * 0.1 + columns: app.landscape() ? 4:2 rows: app.landscape() ? 1:2 @@ -102,18 +107,19 @@ Page { ["\uf059", "IFSC results", ifscDisclaimerDialog.open], ["\uf042", Material.theme === Material.Light ? "Dark mode":"Light mode", app.toggleDarkMode], ["\uf05a", "About blueROCK", aboutBluerockDisclaimerDialog.open], - ["\uf029", "Scan QR code", null], + ["\uf029", "Scan QR code", qrCodeScanPopup.open], ] model: buttons delegate: Item { - Layout.preferredWidth: app.landscape() ? footerMenuButton.implicitWidth : root.width * 0.5 - (footerMenu.columnSpacing / 2) - Layout.preferredHeight: footerMenuButton.implicitHeight + width: app.landscape() ? footerMenuButton.implicitWidth : footerMenu.width * 0.5 - (footerMenu.columnSpacing / 2) + height: app.landscape() ? footerMenu.height : footerMenu.height * 0.5 - (footerMenu.rowSpacing / 2) Button { id: footerMenuButton + property bool isLeft: index % 2 === 0 anchors { @@ -122,6 +128,8 @@ Page { centerIn: app.landscape() ? parent : undefined } + height: parent.height + flat: true font.family: fa5solid.name @@ -157,7 +165,11 @@ Page { "This app is open source and licensed under the GNU agplV3 license," + "the source code can be found here.

" + "Resultservice and rankings provided by digital ROCK." + } + QrCodeScanPopup { + id: qrCodeScanPopup + Material.theme: root.Material.theme } } diff --git a/resources/qml/Pages/WidgetPage.qml b/resources/qml/Pages/WidgetPage.qml index cd9ac93..ecaf211 100644 --- a/resources/qml/Pages/WidgetPage.qml +++ b/resources/qml/Pages/WidgetPage.qml @@ -38,7 +38,9 @@ Page { Result, Ranking, - Aggregated // not yet implemented + Aggregated, // not yet implemented + + Invalid } title: widgetLd.item !== null && widgetLd.item.hasOwnProperty('title') ? widgetLd.item['title']:"" @@ -72,7 +74,6 @@ Page { // route: (int) round // type: ('','starters', 'nat_team_ranking', 'sektionenwertung', 'regionalzentren'), //} - var ret = serverConn.getWidgetData(params) root.status = ret["status"] @@ -80,7 +81,11 @@ Page { if(ret["status"] === 200){ root.widgetData = ret["data"] root.widgetType = checkWidgetType(params, root.widgetData) - if(widgetLd.load()){ + if(widgetType === WidgetPage.WidgetType.Invalid) { + root.ready = false + root.status = 906 + } + else if(widgetLd.load()){ root.ready = true } else { @@ -124,6 +129,10 @@ Page { } + function areParamsValid() { + + } + function checkWidgetType(params, widgetData){ var widgetType @@ -170,6 +179,9 @@ Page { // aggregated widgetType = WidgetPage.WidgetType.Aggregated } + else { + widgetType = WidgetPage.WidgetType.Invalid + } return widgetType } @@ -181,10 +193,9 @@ Page { return ret.join('&'); } - function shareWidget() { + function shareWidget(compName) { var url = "https://l.bluerock.dev/?" + encodeQueryData(params) - sharePu.appear(url) - console.log("Url will be:", url) + sharePu.appear(url, compName) } Loader { diff --git a/resources/qml/Widgets/ResultWidget.qml b/resources/qml/Widgets/ResultWidget.qml index 9a07249..418aa5d 100644 --- a/resources/qml/Widgets/ResultWidget.qml +++ b/resources/qml/Widgets/ResultWidget.qml @@ -69,7 +69,7 @@ DataListView { ToolButton { id: shareToolBt - onClicked: shareWidget() + onClicked: shareWidget(control.title) text: "\uf1e0" font.family: fa5solid.name diff --git a/resources/qml/main.qml b/resources/qml/main.qml index b60c655..5f6242d 100644 --- a/resources/qml/main.qml +++ b/resources/qml/main.qml @@ -28,6 +28,8 @@ import de.itsblue.blueROCK 1.0 import "./Pages" import "./Components" +import "./Widgets" + Window { visible: true width: 540 @@ -115,7 +117,7 @@ Window { 'sui_ice' : { 'label' : 'Iceclimbing', 'nation' : 'SUI', - 'wettk_reg' : '^[0-9]{2,2}_RC_.*', + 'wparams["valid"]ettk_reg' : '^[0-9]{2,2}_RC_.*', 'rang_title': '', 'bgcolor' : app.federalColor, //'#F0F0F0', 'sort_rank': 4, @@ -134,6 +136,7 @@ Window { //mainStack.push("Pages/AthleteSearchPage.qml") openWidget({comp: 11651, cat: 26}) //openWidget({person: 6623}) + //console.log(JSON.stringify(serverConn.getParamsFromUrl(""))) } FontLoader { @@ -154,11 +157,6 @@ Window { BlueRockBackend { id: serverConn - - Component.onCompleted: { - //var params = serverConn.getParamsFromUrl("https://www.digitalrock.de/egroupware/ranking/sitemgr/digitalrock/eliste.html#!comp=11471&cat=GER_F_A") - //app.openWidget(params) - } } AppSettings { @@ -539,17 +537,36 @@ Window { function openWidget(params) { loadingDl.open() - var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params}) - app.errorCode = calComp.status + console.log("Opening widget: ", JSON.stringify(params)) - if(calComp.ready) { - mainStack.push(calComp) - } - else { - delete(calComp) + var result = false + + if(Object.keys(params).length) { + var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params}) + app.errorCode = calComp.status + + if(calComp.ready) { + mainStack.push(calComp) + result = true + } + else { + delete(calComp) + } } loadingDl.close() + return result + } + + function openWidgetFromUrl(url) { + var result = serverConn.getParamsFromUrl(url) + + if(result["valid"]) { + openWidget(result["params"]) + return app.errorCode !== 906 + } + + return result["valid"] } function defaultString(string, defaultString) { @@ -592,6 +609,11 @@ Window { errorString = "Authentication required" errorDescription = "The server asked for user credentinals, please chack them and try again" break + case 404: + infoLevel = 2 + errorString = "Not found" + errorDescription = "The requested item was not found" + break case 500: infoLevel = 2 errorString = "Internal server error" @@ -627,6 +649,11 @@ Window { errorString = "Loading..." errorDescription = "Please wait while we're loading some data" break + case 906: + infoLevel = 2 + errorString = "Invalid Request" + errorDescription = "Invalid Request" + break default: infoLevel = 2 errorString = "Unexpected error ("+errorCode+")" diff --git a/resources/qml/qml.qrc b/resources/qml/qml.qrc index 2d4373e..5fe014c 100644 --- a/resources/qml/qml.qrc +++ b/resources/qml/qml.qrc @@ -29,5 +29,6 @@ Components/ColoredItemDelegate.qml Components/AlignedButton.qml Components/SharePopup.qml + Components/QrCodeScanPopup.qml diff --git a/resources/shared/PosterTemplate.xcf b/resources/shared/PosterTemplate.xcf index 45fbaae6c78151b85d6313034ad82504187e2aba..9781e61b0c5b9eeb01228b0ddffb94ff5150234b 100644 GIT binary patch delta 10546 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 = new ShareUtils(this); - this->shareResultsAsPoster("test"); } QVariant BlueRockBackend::getWidgetData(QVariantMap params) { @@ -49,7 +48,7 @@ QVariant BlueRockBackend::getWidgetData(QVariantMap params) { qDebug() << requestUrl; - QVariantMap ret = this->senddata(QUrl(requestUrl)); + QVariantMap ret = this->_senddata(QUrl(requestUrl)); if(ret["status"] != 200) { // request was a failure @@ -67,6 +66,18 @@ QVariant BlueRockBackend::getWidgetData(QVariantMap params) { QVariantMap BlueRockBackend::getParamsFromUrl(QString stringUrl) { stringUrl = stringUrl.replace("#!", "?"); QUrl url(stringUrl); + + if(!url.isValid() || url.isEmpty() || url.host().isEmpty()) + return {{"valid", false},{"params", QVariantMap()}} ; + + QStringList domainFragments = url.host().split("."); + QString tld = domainFragments.takeLast(); + QString domainName = domainFragments.takeLast(); + QString baseDomain = domainName + "." + tld; + + if(!this->_validBaseDomains.contains(baseDomain)) + return {{"valid", false},{"params", QVariantMap()}}; + QUrlQuery query(url.query()); QVariantMap params; @@ -78,31 +89,42 @@ QVariantMap BlueRockBackend::getParamsFromUrl(QString stringUrl) { params.insert(pair.first, pair.second); } - return params; + return {{"valid", true},{"params",params}}; } -void BlueRockBackend::shareResultsAsUrl(QString url) { - this->_shareUtils->shareText(url); +void BlueRockBackend::shareResultsAsUrl(QString url, QString compName) { + this->_shareUtils->shareText("Check out the results of " + compName + " over here:\n" + url); } -void BlueRockBackend::shareResultsAsPoster(QString url) { - QPdfWriter writer("/tmp/test.pdf"); +void BlueRockBackend::shareResultsAsPoster(QString url, QString compName) { + QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + path += "/" + compName + ".pdf"; + QPdfWriter writer(path); writer.setPageSize(QPageSize(QPageSize::A4)); writer.setPageMargins(QMargins(0, 0, 0, 0)); writer.setResolution(600); + QPainter painter(&writer); - painter.drawText(QRect(0, 0, 1980, 100),Qt::AlignHCenter|Qt::AlignBottom, - "Children's Health Checkup Form"); - QPixmap image(":/PosterTemplate.png"); - painter.drawPixmap(0,0, writer.width(), writer.height(), image); + QPixmap background(":/PosterTemplate.png"); + painter.drawPixmap(0,0, writer.width(), writer.height(), background); + + QPixmap barcode; + int size = writer.width() * 0.5; + QZXingEncoderConfig encoderConfig(QZXing::EncoderFormat_QR_CODE, QSize(size, size), QZXing::EncodeErrorCorrectionLevel_M, false, false); + barcode.convertFromImage(QZXing::encodeData(url, encoderConfig)); + painter.drawPixmap((writer.width() - size) / 2, size * 0.5, size, size, barcode); + painter.end(); + + int requestId; + this->_shareUtils->sendFile(path, compName, "application/pdf", requestId); } // ------------------------ // --- Helper functions --- // ------------------------ -QVariantMap BlueRockBackend::senddata(QUrl serviceUrl, QUrlQuery pdata) +QVariantMap BlueRockBackend::_senddata(QUrl serviceUrl, QUrlQuery pdata) { // create network manager QNetworkAccessManager * networkManager = new QNetworkAccessManager(); diff --git a/sources/main.cpp b/sources/main.cpp index b09f017..fc04d7e 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -22,6 +22,7 @@ #include #include #include +#include "QZXing.h" #include "headers/bluerockbackend.h" #include "headers/appsettings.h" @@ -46,6 +47,9 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty("QT_DEBUG", false); #endif + QZXing::registerQMLTypes(); + QZXing::registerQMLImageProvider(engine); + engine.rootContext()->setContextProperty("APP_VERSION", APP_VERSION); engine.load(QUrl(QStringLiteral("qrc:/main.qml")));