Implement Qr-code generation and scanning
This commit is contained in:
parent
8d1f0173ed
commit
06e76bc287
13 changed files with 400 additions and 63 deletions
|
@ -98,6 +98,12 @@ ios {
|
||||||
xcode_product_bundle_identifier_setting.value = "de.itsblue.bluerock"
|
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!
|
# this has to be the last line!
|
||||||
ANDROID_ABIS = armeabi-v7a arm64-v8a
|
ANDROID_ABIS = armeabi-v7a arm64-v8a
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QPageSize>
|
#include <QPageSize>
|
||||||
#include <QPdfWriter>
|
#include <QPdfWriter>
|
||||||
|
#include "QZXing.h"
|
||||||
|
|
||||||
#include "shareUtils/shareutils.h"
|
#include "shareUtils/shareutils.h"
|
||||||
|
|
||||||
|
@ -43,9 +44,10 @@ public:
|
||||||
explicit BlueRockBackend(QObject *parent = nullptr);
|
explicit BlueRockBackend(QObject *parent = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariantMap senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery());
|
QVariantMap _senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery());
|
||||||
|
|
||||||
ShareUtils* _shareUtils;
|
ShareUtils* _shareUtils;
|
||||||
|
const QStringList _validBaseDomains = {"digitalrock.de", "bluerock.dev"};
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
@ -53,8 +55,8 @@ public slots:
|
||||||
|
|
||||||
QVariant getWidgetData(QVariantMap params);
|
QVariant getWidgetData(QVariantMap params);
|
||||||
QVariantMap getParamsFromUrl(QString url);
|
QVariantMap getParamsFromUrl(QString url);
|
||||||
void shareResultsAsUrl(QString url);
|
void shareResultsAsUrl(QString url, QString compName);
|
||||||
void shareResultsAsPoster(QString url);
|
void shareResultsAsPoster(QString url, QString compName);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
203
resources/qml/Components/QrCodeScanPopup.qml
Normal file
203
resources/qml/Components/QrCodeScanPopup.qml
Normal file
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -226,7 +226,7 @@ ColoredItemDelegate {
|
||||||
|
|
||||||
// outline
|
// outline
|
||||||
context.lineWidth = 1;
|
context.lineWidth = 1;
|
||||||
context.strokeStyle = '#424242';
|
context.strokeStyle = Material.primaryTextColor;
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
if(resultData[1] > 0){
|
if(resultData[1] > 0){
|
||||||
|
@ -254,7 +254,7 @@ ColoredItemDelegate {
|
||||||
|
|
||||||
// outline
|
// outline
|
||||||
context.lineWidth = 1;
|
context.lineWidth = 1;
|
||||||
context.strokeStyle = '#424242';
|
context.strokeStyle = Material.primaryTextColor;
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ ColoredItemDelegate {
|
||||||
|
|
||||||
// outline
|
// outline
|
||||||
context.lineWidth = 1;
|
context.lineWidth = 1;
|
||||||
context.strokeStyle = '#424242';
|
context.strokeStyle = Material.primaryTextColor;
|
||||||
context.stroke();
|
context.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
|
import QZXing 3.1
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property string _shareUrl
|
property string _shareUrl
|
||||||
|
property string _compName
|
||||||
|
|
||||||
parent: Overlay.overlay
|
parent: Overlay.overlay
|
||||||
|
|
||||||
|
@ -13,31 +15,78 @@ Dialog {
|
||||||
y: (parent.height - height) * 0.5
|
y: (parent.height - height) * 0.5
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
title: "Share these results"
|
title: "Share these results"
|
||||||
|
|
||||||
contentItem: RowLayout {
|
onClosed: {
|
||||||
Repeater {
|
shareComponentLoader.sourceComponent = null
|
||||||
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: "<font size=\"+4\">" + modelData[0] + "</font><br><br> " + modelData[1] + " "
|
|
||||||
onClicked: buttonRepeater.buttons[index][2](_shareUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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: "<font size=\"+4\">" + modelData[0] + "</font><br><br> " + 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
|
_shareUrl = shareUrl
|
||||||
|
_compName = compName
|
||||||
|
shareComponentLoader.sourceComponent = shareComponent
|
||||||
control.open()
|
control.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ Page {
|
||||||
topMargin: root.height * 0.03
|
topMargin: root.height * 0.03
|
||||||
}
|
}
|
||||||
|
|
||||||
height: menuGr.buttonSize * 0.3
|
height: app.landscape() ? menuGr.buttonSize * 0.2:menuGr.buttonSize * 0.3
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
@ -84,7 +84,7 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
Grid {
|
||||||
id: footerMenu
|
id: footerMenu
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
|
@ -93,6 +93,11 @@ Page {
|
||||||
horizontalCenter: parent.horizontalCenter
|
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
|
columns: app.landscape() ? 4:2
|
||||||
rows: app.landscape() ? 1:2
|
rows: app.landscape() ? 1:2
|
||||||
|
|
||||||
|
@ -102,18 +107,19 @@ Page {
|
||||||
["\uf059", "IFSC results", ifscDisclaimerDialog.open],
|
["\uf059", "IFSC results", ifscDisclaimerDialog.open],
|
||||||
["\uf042", Material.theme === Material.Light ? "Dark mode":"Light mode", app.toggleDarkMode],
|
["\uf042", Material.theme === Material.Light ? "Dark mode":"Light mode", app.toggleDarkMode],
|
||||||
["\uf05a", "About blueROCK", aboutBluerockDisclaimerDialog.open],
|
["\uf05a", "About blueROCK", aboutBluerockDisclaimerDialog.open],
|
||||||
["\uf029", "Scan QR code", null],
|
["\uf029", "Scan QR code", qrCodeScanPopup.open],
|
||||||
]
|
]
|
||||||
|
|
||||||
model: buttons
|
model: buttons
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
|
|
||||||
Layout.preferredWidth: app.landscape() ? footerMenuButton.implicitWidth : root.width * 0.5 - (footerMenu.columnSpacing / 2)
|
width: app.landscape() ? footerMenuButton.implicitWidth : footerMenu.width * 0.5 - (footerMenu.columnSpacing / 2)
|
||||||
Layout.preferredHeight: footerMenuButton.implicitHeight
|
height: app.landscape() ? footerMenu.height : footerMenu.height * 0.5 - (footerMenu.rowSpacing / 2)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: footerMenuButton
|
id: footerMenuButton
|
||||||
|
|
||||||
property bool isLeft: index % 2 === 0
|
property bool isLeft: index % 2 === 0
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
|
@ -122,6 +128,8 @@ Page {
|
||||||
centerIn: app.landscape() ? parent : undefined
|
centerIn: app.landscape() ? parent : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
flat: true
|
flat: true
|
||||||
|
|
||||||
font.family: fa5solid.name
|
font.family: fa5solid.name
|
||||||
|
@ -157,7 +165,11 @@ Page {
|
||||||
"This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>," +
|
"This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>," +
|
||||||
"the source code can be found <a href='https://itsblue.dev/dorian/blueROCK/'>here</a>.<br><br>" +
|
"the source code can be found <a href='https://itsblue.dev/dorian/blueROCK/'>here</a>.<br><br>" +
|
||||||
"Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>."
|
"Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>."
|
||||||
|
}
|
||||||
|
|
||||||
|
QrCodeScanPopup {
|
||||||
|
id: qrCodeScanPopup
|
||||||
|
Material.theme: root.Material.theme
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,9 @@ Page {
|
||||||
Result,
|
Result,
|
||||||
|
|
||||||
Ranking,
|
Ranking,
|
||||||
Aggregated // not yet implemented
|
Aggregated, // not yet implemented
|
||||||
|
|
||||||
|
Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
title: widgetLd.item !== null && widgetLd.item.hasOwnProperty('title') ? widgetLd.item['title']:""
|
title: widgetLd.item !== null && widgetLd.item.hasOwnProperty('title') ? widgetLd.item['title']:""
|
||||||
|
@ -72,7 +74,6 @@ Page {
|
||||||
// route: (int) round
|
// route: (int) round
|
||||||
// type: ('','starters', 'nat_team_ranking', 'sektionenwertung', 'regionalzentren'),
|
// type: ('','starters', 'nat_team_ranking', 'sektionenwertung', 'regionalzentren'),
|
||||||
//}
|
//}
|
||||||
|
|
||||||
var ret = serverConn.getWidgetData(params)
|
var ret = serverConn.getWidgetData(params)
|
||||||
|
|
||||||
root.status = ret["status"]
|
root.status = ret["status"]
|
||||||
|
@ -80,7 +81,11 @@ Page {
|
||||||
if(ret["status"] === 200){
|
if(ret["status"] === 200){
|
||||||
root.widgetData = ret["data"]
|
root.widgetData = ret["data"]
|
||||||
root.widgetType = checkWidgetType(params, root.widgetData)
|
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
|
root.ready = true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -124,6 +129,10 @@ Page {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function areParamsValid() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function checkWidgetType(params, widgetData){
|
function checkWidgetType(params, widgetData){
|
||||||
var widgetType
|
var widgetType
|
||||||
|
|
||||||
|
@ -170,6 +179,9 @@ Page {
|
||||||
// aggregated
|
// aggregated
|
||||||
widgetType = WidgetPage.WidgetType.Aggregated
|
widgetType = WidgetPage.WidgetType.Aggregated
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
widgetType = WidgetPage.WidgetType.Invalid
|
||||||
|
}
|
||||||
|
|
||||||
return widgetType
|
return widgetType
|
||||||
}
|
}
|
||||||
|
@ -181,10 +193,9 @@ Page {
|
||||||
return ret.join('&');
|
return ret.join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
function shareWidget() {
|
function shareWidget(compName) {
|
||||||
var url = "https://l.bluerock.dev/?" + encodeQueryData(params)
|
var url = "https://l.bluerock.dev/?" + encodeQueryData(params)
|
||||||
sharePu.appear(url)
|
sharePu.appear(url, compName)
|
||||||
console.log("Url will be:", url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
|
|
|
@ -69,7 +69,7 @@ DataListView {
|
||||||
ToolButton {
|
ToolButton {
|
||||||
id: shareToolBt
|
id: shareToolBt
|
||||||
|
|
||||||
onClicked: shareWidget()
|
onClicked: shareWidget(control.title)
|
||||||
|
|
||||||
text: "\uf1e0"
|
text: "\uf1e0"
|
||||||
font.family: fa5solid.name
|
font.family: fa5solid.name
|
||||||
|
|
|
@ -28,6 +28,8 @@ import de.itsblue.blueROCK 1.0
|
||||||
import "./Pages"
|
import "./Pages"
|
||||||
import "./Components"
|
import "./Components"
|
||||||
|
|
||||||
|
import "./Widgets"
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
visible: true
|
visible: true
|
||||||
width: 540
|
width: 540
|
||||||
|
@ -115,7 +117,7 @@ Window {
|
||||||
'sui_ice' : {
|
'sui_ice' : {
|
||||||
'label' : 'Iceclimbing',
|
'label' : 'Iceclimbing',
|
||||||
'nation' : 'SUI',
|
'nation' : 'SUI',
|
||||||
'wettk_reg' : '^[0-9]{2,2}_RC_.*',
|
'wparams["valid"]ettk_reg' : '^[0-9]{2,2}_RC_.*',
|
||||||
'rang_title': '',
|
'rang_title': '',
|
||||||
'bgcolor' : app.federalColor, //'#F0F0F0',
|
'bgcolor' : app.federalColor, //'#F0F0F0',
|
||||||
'sort_rank': 4,
|
'sort_rank': 4,
|
||||||
|
@ -134,6 +136,7 @@ Window {
|
||||||
//mainStack.push("Pages/AthleteSearchPage.qml")
|
//mainStack.push("Pages/AthleteSearchPage.qml")
|
||||||
openWidget({comp: 11651, cat: 26})
|
openWidget({comp: 11651, cat: 26})
|
||||||
//openWidget({person: 6623})
|
//openWidget({person: 6623})
|
||||||
|
//console.log(JSON.stringify(serverConn.getParamsFromUrl("")))
|
||||||
}
|
}
|
||||||
|
|
||||||
FontLoader {
|
FontLoader {
|
||||||
|
@ -154,11 +157,6 @@ Window {
|
||||||
|
|
||||||
BlueRockBackend {
|
BlueRockBackend {
|
||||||
id: serverConn
|
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 {
|
AppSettings {
|
||||||
|
@ -539,17 +537,36 @@ Window {
|
||||||
function openWidget(params) {
|
function openWidget(params) {
|
||||||
loadingDl.open()
|
loadingDl.open()
|
||||||
|
|
||||||
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
|
console.log("Opening widget: ", JSON.stringify(params))
|
||||||
app.errorCode = calComp.status
|
|
||||||
|
|
||||||
if(calComp.ready) {
|
var result = false
|
||||||
mainStack.push(calComp)
|
|
||||||
}
|
if(Object.keys(params).length) {
|
||||||
else {
|
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
|
||||||
delete(calComp)
|
app.errorCode = calComp.status
|
||||||
|
|
||||||
|
if(calComp.ready) {
|
||||||
|
mainStack.push(calComp)
|
||||||
|
result = true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete(calComp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingDl.close()
|
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) {
|
function defaultString(string, defaultString) {
|
||||||
|
@ -592,6 +609,11 @@ Window {
|
||||||
errorString = "Authentication required"
|
errorString = "Authentication required"
|
||||||
errorDescription = "The server asked for user credentinals, please chack them and try again"
|
errorDescription = "The server asked for user credentinals, please chack them and try again"
|
||||||
break
|
break
|
||||||
|
case 404:
|
||||||
|
infoLevel = 2
|
||||||
|
errorString = "Not found"
|
||||||
|
errorDescription = "The requested item was not found"
|
||||||
|
break
|
||||||
case 500:
|
case 500:
|
||||||
infoLevel = 2
|
infoLevel = 2
|
||||||
errorString = "Internal server error"
|
errorString = "Internal server error"
|
||||||
|
@ -627,6 +649,11 @@ Window {
|
||||||
errorString = "Loading..."
|
errorString = "Loading..."
|
||||||
errorDescription = "Please wait while we're loading some data"
|
errorDescription = "Please wait while we're loading some data"
|
||||||
break
|
break
|
||||||
|
case 906:
|
||||||
|
infoLevel = 2
|
||||||
|
errorString = "Invalid Request"
|
||||||
|
errorDescription = "Invalid Request"
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
infoLevel = 2
|
infoLevel = 2
|
||||||
errorString = "Unexpected error ("+errorCode+")"
|
errorString = "Unexpected error ("+errorCode+")"
|
||||||
|
|
|
@ -29,5 +29,6 @@
|
||||||
<file>Components/ColoredItemDelegate.qml</file>
|
<file>Components/ColoredItemDelegate.qml</file>
|
||||||
<file>Components/AlignedButton.qml</file>
|
<file>Components/AlignedButton.qml</file>
|
||||||
<file>Components/SharePopup.qml</file>
|
<file>Components/SharePopup.qml</file>
|
||||||
|
<file>Components/QrCodeScanPopup.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Binary file not shown.
|
@ -21,7 +21,6 @@
|
||||||
BlueRockBackend::BlueRockBackend(QObject *parent) : QObject(parent)
|
BlueRockBackend::BlueRockBackend(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
this->_shareUtils = new ShareUtils(this);
|
this->_shareUtils = new ShareUtils(this);
|
||||||
this->shareResultsAsPoster("test");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant BlueRockBackend::getWidgetData(QVariantMap params) {
|
QVariant BlueRockBackend::getWidgetData(QVariantMap params) {
|
||||||
|
@ -49,7 +48,7 @@ QVariant BlueRockBackend::getWidgetData(QVariantMap params) {
|
||||||
|
|
||||||
qDebug() << requestUrl;
|
qDebug() << requestUrl;
|
||||||
|
|
||||||
QVariantMap ret = this->senddata(QUrl(requestUrl));
|
QVariantMap ret = this->_senddata(QUrl(requestUrl));
|
||||||
|
|
||||||
if(ret["status"] != 200) {
|
if(ret["status"] != 200) {
|
||||||
// request was a failure
|
// request was a failure
|
||||||
|
@ -67,6 +66,18 @@ QVariant BlueRockBackend::getWidgetData(QVariantMap params) {
|
||||||
QVariantMap BlueRockBackend::getParamsFromUrl(QString stringUrl) {
|
QVariantMap BlueRockBackend::getParamsFromUrl(QString stringUrl) {
|
||||||
stringUrl = stringUrl.replace("#!", "?");
|
stringUrl = stringUrl.replace("#!", "?");
|
||||||
QUrl url(stringUrl);
|
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());
|
QUrlQuery query(url.query());
|
||||||
|
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
|
@ -78,31 +89,42 @@ QVariantMap BlueRockBackend::getParamsFromUrl(QString stringUrl) {
|
||||||
params.insert(pair.first, pair.second);
|
params.insert(pair.first, pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return {{"valid", true},{"params",params}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlueRockBackend::shareResultsAsUrl(QString url) {
|
void BlueRockBackend::shareResultsAsUrl(QString url, QString compName) {
|
||||||
this->_shareUtils->shareText(url);
|
this->_shareUtils->shareText("Check out the results of " + compName + " over here:\n" + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlueRockBackend::shareResultsAsPoster(QString url) {
|
void BlueRockBackend::shareResultsAsPoster(QString url, QString compName) {
|
||||||
QPdfWriter writer("/tmp/test.pdf");
|
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||||
|
path += "/" + compName + ".pdf";
|
||||||
|
QPdfWriter writer(path);
|
||||||
writer.setPageSize(QPageSize(QPageSize::A4));
|
writer.setPageSize(QPageSize(QPageSize::A4));
|
||||||
writer.setPageMargins(QMargins(0, 0, 0, 0));
|
writer.setPageMargins(QMargins(0, 0, 0, 0));
|
||||||
writer.setResolution(600);
|
writer.setResolution(600);
|
||||||
|
|
||||||
QPainter painter(&writer);
|
QPainter painter(&writer);
|
||||||
painter.drawText(QRect(0, 0, 1980, 100),Qt::AlignHCenter|Qt::AlignBottom,
|
QPixmap background(":/PosterTemplate.png");
|
||||||
"Children's Health Checkup Form");
|
painter.drawPixmap(0,0, writer.width(), writer.height(), background);
|
||||||
QPixmap image(":/PosterTemplate.png");
|
|
||||||
painter.drawPixmap(0,0, writer.width(), writer.height(), image);
|
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();
|
painter.end();
|
||||||
|
|
||||||
|
int requestId;
|
||||||
|
this->_shareUtils->sendFile(path, compName, "application/pdf", requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------
|
// ------------------------
|
||||||
// --- Helper functions ---
|
// --- Helper functions ---
|
||||||
// ------------------------
|
// ------------------------
|
||||||
|
|
||||||
QVariantMap BlueRockBackend::senddata(QUrl serviceUrl, QUrlQuery pdata)
|
QVariantMap BlueRockBackend::_senddata(QUrl serviceUrl, QUrlQuery pdata)
|
||||||
{
|
{
|
||||||
// create network manager
|
// create network manager
|
||||||
QNetworkAccessManager * networkManager = new QNetworkAccessManager();
|
QNetworkAccessManager * networkManager = new QNetworkAccessManager();
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include "QZXing.h"
|
||||||
|
|
||||||
#include "headers/bluerockbackend.h"
|
#include "headers/bluerockbackend.h"
|
||||||
#include "headers/appsettings.h"
|
#include "headers/appsettings.h"
|
||||||
|
@ -46,6 +47,9 @@ int main(int argc, char *argv[])
|
||||||
engine.rootContext()->setContextProperty("QT_DEBUG", false);
|
engine.rootContext()->setContextProperty("QT_DEBUG", false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QZXing::registerQMLTypes();
|
||||||
|
QZXing::registerQMLImageProvider(engine);
|
||||||
|
|
||||||
engine.rootContext()->setContextProperty("APP_VERSION", APP_VERSION);
|
engine.rootContext()->setContextProperty("APP_VERSION", APP_VERSION);
|
||||||
|
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||||
|
|
Loading…
Reference in a new issue