diff --git a/OmobiDisplayApp/OmobiDisplayApp.pro b/OmobiDisplayApp/OmobiDisplayApp.pro index 0ec8ca4..c436c0b 100644 --- a/OmobiDisplayApp/OmobiDisplayApp.pro +++ b/OmobiDisplayApp/OmobiDisplayApp.pro @@ -1,7 +1,8 @@ QT += quick bluetooth quickcontrols2 CONFIG += c++11 -TARGET = OmobiDisplayApp +TARGET = ItsblueLedDisplayController +VERSION = 1.0.0 TEMPLATE = app @@ -41,18 +42,24 @@ CONFIG += QBluetoothLeUart_QML include($$PWD/QBluetoothLeUart/QBluetoothLeUart.pri) DISTFILES += \ - test.qmodel + android/AndroidManifest.xml -STATECHARTS += - -contains(ANDROID_TARGET_ARCH,armeabi-v7a) { - ANDROID_ABIS = \ - armeabi-v7a -} - -ANDROID_ABIS = armeabi-v7a +android { +QT += androidextras +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android contains(ANDROID_TARGET_ARCH,) { ANDROID_ABIS = \ armeabi-v7a } + +contains(ANDROID_TARGET_ARCH,armeabi-v7a) { + ANDROID_ABIS = \ + armeabi-v7a +} +} + +contains(ANDROID_TARGET_ARCH,armeabi-v7a) { + ANDROID_ABIS = \ + armeabi-v7a +} diff --git a/OmobiDisplayApp/QBluetoothLeUart b/OmobiDisplayApp/QBluetoothLeUart index 76e4575..fba59b3 160000 --- a/OmobiDisplayApp/QBluetoothLeUart +++ b/OmobiDisplayApp/QBluetoothLeUart @@ -1 +1 @@ -Subproject commit 76e457593e889885fd410fdbcdd659706a1eceb8 +Subproject commit fba59b37770ab1ede351ec70119d2da328be6a06 diff --git a/OmobiDisplayApp/android/AndroidManifest.xml b/OmobiDisplayApp/android/AndroidManifest.xml new file mode 100644 index 0000000..f91454a --- /dev/null +++ b/OmobiDisplayApp/android/AndroidManifest.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OmobiDisplayApp/main.cpp b/OmobiDisplayApp/main.cpp index 79e3c05..bdef8bc 100644 --- a/OmobiDisplayApp/main.cpp +++ b/OmobiDisplayApp/main.cpp @@ -3,19 +3,33 @@ #include #include #include +#include + +#ifdef Q_OS_ANDROID +#include +#endif #include "omobidisplaybackend.h" #include "omobidisplaytextmodel.h" +/*void permissionCallback(const QtAndroid::PermissionResultMap& results) { + for(QtAndroid::PermissionResult result : results) { + qWarning() << "Permission Callback Result:" << (result == QtAndroid::PermissionResult::Granted); + } +}*/ + int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setOrganizationName("Itsblue"); + QCoreApplication::setOrganizationDomain("itsblue.de"); + QCoreApplication::setApplicationName("Itsblue smart display"); + QGuiApplication app(argc, argv); QTranslator translator; translator.load(":/" + QLocale::system().name() + ".qm"); - translator.load(":/de.qm"); app.installTranslator(&translator); qmlRegisterType("de.itsblue.omobidisplayapp", 1, 0, "OmobiDisplayBackend"); diff --git a/OmobiDisplayApp/omobidisplaybackend.cpp b/OmobiDisplayApp/omobidisplaybackend.cpp index 393dc51..cc96875 100644 --- a/OmobiDisplayApp/omobidisplaybackend.cpp +++ b/OmobiDisplayApp/omobidisplaybackend.cpp @@ -9,6 +9,8 @@ OmobiDisplayBackend::OmobiDisplayBackend(QObject *parent) : QObject(parent) this->displayBrightness = -1; this->waitingCommands = 0; + this->settings = new QSettings(); + this->keepAliveTimer = new QTimer(this); this->keepAliveTimer->setInterval(5000); this->keepAliveTimer->setSingleShot(false); @@ -34,10 +36,24 @@ void OmobiDisplayBackend::startScanning() { void OmobiDisplayBackend::authenticate(QString code) { // tell display to send over existing model data this->setState(Authenticating); - QString combinedCode = this->bleClient->getCurrentDevice()->getAddress().toUpper() + code; - QString secret = QCryptographicHash::hash(combinedCode.toUtf8(), QCryptographicHash::Sha256).toHex(); - this->sendBluetoothCommand(AuthorizeSessionCommand, QVariantMap{{"secret", secret}}); + if(code.length() == 64) + this->lastDisplaySecret = code; + else if(code.length() == 4) { + QString combinedCode = this->bleClient->getCurrentDevice()->getAddress().toUpper() + code; + this->lastDisplaySecret = QCryptographicHash::hash(combinedCode.toUtf8(), QCryptographicHash::Sha256).toHex(); + } + + this->sendBluetoothCommand(AuthenticateCommand, QVariantMap{{"secret", this->lastDisplaySecret}}); +} + +void OmobiDisplayBackend::handleBluetoothScanningError(QBluetoothLeUartClient::BluetoothScanError error) { + if(error == QBluetoothLeUartClient::LocationPermissionDeniedError) { +#ifdef Q_OS_ANDROID + // try to get permission + //QtAndroid::requestPermissions({"android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"}, NULL); +#endif + } } void OmobiDisplayBackend::handleBluetoothStateChange(QBluetoothLeUartClient::BluetoothLeUartClientState state){ @@ -46,6 +62,14 @@ void OmobiDisplayBackend::handleBluetoothStateChange(QBluetoothLeUartClient::Blu this->setState(Idle); break; } + case QBluetoothLeUartClient::AdapterTurnedOff: { + this->setState(BluetoothOff); + break; + } + case QBluetoothLeUartClient::LocationPermissionDenied: { + this->setState(LocationPermissionDenied); + break; + } case QBluetoothLeUartClient::Scanning: { this->setState(Scanning); break; @@ -79,9 +103,10 @@ void OmobiDisplayBackend::handleBluetoothStateChange(QBluetoothLeUartClient::Blu } void OmobiDisplayBackend::handleBluetoothDeviceConected() { - this->setState(AuthenticationRequired); - this->authenticate("1234"); - // TODO: stuff + if(this->settings->contains(this->bleClient->getCurrentDevice()->getAddress())) + this->authenticate(this->settings->value(this->bleClient->getCurrentDevice()->getAddress()).toString()); + else + this->setState(AuthenticationRequired); } void OmobiDisplayBackend::handleFoundNewDevice(QBluetoothLeUartDevice* device) { @@ -118,7 +143,7 @@ void OmobiDisplayBackend::sendBluetoothCommand(OmobiDisplayCommand command, QVar QJsonDocument doc = QJsonDocument::fromVariant(commandMap); - qDebug() << "Sending command: \n" << qPrintable(doc.toJson(QJsonDocument::Indented)); + //qDebug() << "Sending command: \n" << qPrintable(doc.toJson(QJsonDocument::Indented)); this->waitingCommands ++; @@ -131,13 +156,13 @@ void OmobiDisplayBackend::sendBluetoothCommand(OmobiDisplayCommand command, QVar void OmobiDisplayBackend::sendBluetoothKeepAlive() { QJsonDocument doc = QJsonDocument::fromVariant(QVariantMap{{"header", KeepAliveCommand}}); - qDebug() << "Sending keep alive: \n" << qPrintable(doc.toJson(QJsonDocument::Indented)); + //qDebug() << "Sending keep alive: \n" << qPrintable(doc.toJson(QJsonDocument::Indented)); this->bleClient->sendData(doc.toJson(QJsonDocument::Compact)); } void OmobiDisplayBackend::handleBluetoothDataReceived(QString s){ - qDebug() << "New data: \n" << qPrintable(s); + //qDebug() << "New data: \n" << qPrintable(s); QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(s.toUtf8(), &parseError); @@ -150,12 +175,15 @@ void OmobiDisplayBackend::handleBluetoothDataReceived(QString s){ OmobiDisplayStatusCode status = OmobiDisplayStatusCode(doc.toVariant().toMap()["status"].toInt()); switch (header) { - case AuthorizeSessionCommand: { + case AuthenticateCommand: { if(status != Success) { this->setState(AuthenticationRequired); + this->lastDisplaySecret = ""; return; } + this->settings->setValue(this->bleClient->getCurrentDevice()->getAddress(), this->lastDisplaySecret); + this->waitingCommands = 0; this->setState(Initing); this->sendBluetoothCommand(GetAllTextSetsCommand); diff --git a/OmobiDisplayApp/omobidisplaybackend.h b/OmobiDisplayApp/omobidisplaybackend.h index 8da8338..22692be 100644 --- a/OmobiDisplayApp/omobidisplaybackend.h +++ b/OmobiDisplayApp/omobidisplaybackend.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,8 @@ public: enum OmobiDisplayAppState { Idle, + BluetoothOff, + LocationPermissionDenied, Scanning, ReadyToConnect, Connecting, @@ -35,7 +38,7 @@ public: private: enum OmobiDisplayCommand { - AuthorizeSessionCommand = 0, + AuthenticateCommand = 0, KeepAliveCommand = 1, GetAllTextSetsCommand = 10, GetTextSetParameterCommand = 11, @@ -59,6 +62,9 @@ private: QList> textSetsBuffer; int displayBrightness; + QSettings* settings; + QString lastDisplaySecret; + public slots: Q_INVOKABLE void startScanning(); Q_INVOKABLE void authenticate(QString secret); @@ -74,6 +80,7 @@ private slots: void handleBluetoothStateChange(QBluetoothLeUartClient::BluetoothLeUartClientState state); void handleFoundNewDevice(QBluetoothLeUartDevice* device); void handleBluetoothDeviceConected(); + void handleBluetoothScanningError(QBluetoothLeUartClient::BluetoothScanError error); void handleDisplayTextModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector()); void handleDisplayTextModelRowsInserted(const QModelIndex &parent, int first, int last); diff --git a/OmobiDisplayApp/omobidisplaytextmodel.cpp b/OmobiDisplayApp/omobidisplaytextmodel.cpp index d8ed150..169d15d 100644 --- a/OmobiDisplayApp/omobidisplaytextmodel.cpp +++ b/OmobiDisplayApp/omobidisplaytextmodel.cpp @@ -9,7 +9,6 @@ OmobiDisplayTextModel::OmobiDisplayTextModel(QObject* parent) : QAbstractListMod connect(this, &OmobiDisplayTextModel::rowsRemoved, this, &OmobiDisplayTextModel::rowCountChanged); } - int OmobiDisplayTextModel::rowCount(const QModelIndex &) const { return this->texts.length(); diff --git a/OmobiDisplayApp/ressources/qml/ConnectPage.qml b/OmobiDisplayApp/ressources/qml/ConnectPage.qml index a8d229c..7a8b9f7 100644 --- a/OmobiDisplayApp/ressources/qml/ConnectPage.qml +++ b/OmobiDisplayApp/ressources/qml/ConnectPage.qml @@ -16,6 +16,11 @@ Page { title: qsTr("Available displays") + signal opened() + + onOpened: { + } + ColumnLayout { id: mainLayout anchors { @@ -114,6 +119,7 @@ Page { } Item { + id: noDisplaysItem anchors.centerIn: parent width: Math.min(parent.height, parent.width) @@ -167,6 +173,123 @@ Page { text: parseInt(root.state) === OmobiDisplayBackend.Scanning ? qsTr("Still scanning"):qsTr("No displays found") } } + + + Item { + id: bluetoothOffItem + anchors.centerIn: parent + + width: Math.min(parent.height, parent.width) + height: Math.min(parent.height, parent.width) + + opacity: 0 + + Behavior on opacity { + NumberAnimation {} + } + + + Text { + id: bluetoothOffIcon + anchors { + top: parent.top + topMargin: parent.height * 0.1 + horizontalCenter: parent.horizontalCenter + } + anchors.verticalCenterOffset: text === "..." ? -height * 0.25:0 + color: "lightgrey" + font.pixelSize: parent.height * 0.4 + font.family: fontAwesomeBrands.name + text: "\uf294" + } + + + Text { + id: bluetoothOffText + anchors { + top: bluetoothOffIcon.bottom + topMargin: bluetoothOffIcon.height * 0.15 + horizontalCenter: parent.horizontalCenter + } + + font.bold: true + font.pixelSize: bluetoothOffIcon.height * 0.15 + + color: Qt.darker("lightgrey", 1.1) + text: qsTr("Bluetooth is turned off") + } + } + + Item { + id: noPermissionItem + anchors.centerIn: parent + + width: Math.min(parent.height, parent.width) + height: Math.min(parent.height, parent.width) + + opacity: 0 + + Behavior on opacity { + NumberAnimation {} + } + + + Text { + id: noPermissionIcon + anchors { + top: parent.top + topMargin: parent.height * 0 + horizontalCenter: parent.horizontalCenter + } + anchors.verticalCenterOffset: text === "..." ? -height * 0.25:0 + color: "lightgrey" + font.pixelSize: parent.height * 0.4 + font.family: fontAwesome.name + text: "\uf3ed" + } + Text { + id: noPermissionText + anchors { + top: noPermissionIcon.bottom + topMargin: noPermissionIcon.height * 0.15 + horizontalCenter: parent.horizontalCenter + } + + width: parent.width * 0.9 + + font.bold: true + font.pixelSize: noPermissionIcon.height * 0.15 + + horizontalAlignment: Text.AlignHCenter + + wrapMode: Text.Wrap + + color: Qt.darker("lightgrey", 1.1) + text: qsTr("Error:\nLocation permission denied!") + } + + Text { + id: noPermissionDetailText + anchors { + top: noPermissionText.bottom + topMargin: noPermissionText.height * 0.15 + horizontalCenter: parent.horizontalCenter + } + + width: parent.width * 0.9 + + font.bold: true + font.pixelSize: noPermissionText.font.pixelSize * 0.7 + + horizontalAlignment: Text.AlignHCenter + + wrapMode: Text.Wrap + + color: Qt.darker("lightgrey", 1.1) + text: qsTr("This app requires location permission in order for Bluetooth to work, it will not actually access your location.") + } + } + } } @@ -218,6 +341,44 @@ Page { statusText: qsTr("Tap here to scan") working: false } + + PropertyChanges { + target: noPermissionItem + opacity: 0 + } + }, + State { + name: OmobiDisplayBackend.BluetoothOff + PropertyChanges { + target: bluetoothOffItem + opacity: 1 + } + PropertyChanges { + target: root + statusText: qsTr("Bluetooth is turned off") + working: false + } + PropertyChanges { + target: noDisplaysItem + opacity: 0 + } + }, + State { + name: OmobiDisplayBackend.LocationPermissionDenied + + PropertyChanges { + target: noPermissionItem + opacity: 1 + } + PropertyChanges { + target: root + statusText: qsTr("Tap here to continue") + working: false + } + PropertyChanges { + target: noDisplaysItem + opacity: 0 + } }, State { name: OmobiDisplayBackend.Scanning @@ -227,6 +388,14 @@ Page { statusText: qsTr("Scanning...") working: true } + PropertyChanges { + target: bluetoothOffItem + opacity: 0 + } + PropertyChanges { + target: noPermissionItem + opacity: 0 + } }, State { name: OmobiDisplayBackend.ReadyToConnect diff --git a/OmobiDisplayApp/ressources/qml/ConnectedPage.qml b/OmobiDisplayApp/ressources/qml/ConnectedPage.qml index 9788b0b..ebd217d 100644 --- a/OmobiDisplayApp/ressources/qml/ConnectedPage.qml +++ b/OmobiDisplayApp/ressources/qml/ConnectedPage.qml @@ -15,6 +15,8 @@ Page { title: backend.bleClient.currentDevice === null ? "":backend.bleClient.currentDevice.name + signal opened() + function backButtonClicked() { backend.bleClient.disconnectFromDevice() } @@ -50,15 +52,17 @@ Page { Layout.fillHeight: true verticalAlignment: Text.AlignVCenter font.pixelSize: parent.height * 0.5 + font.family: fontAwesome.name text: "\uf186" } Slider { + id: brightnessSlider Layout.fillWidth: true Layout.fillHeight: true from: 0 - to: 10 + to: 255 stepSize: 1 value: backend.displayBrightness @@ -67,12 +71,19 @@ Page { if(!pressed) backend.displayBrightness = value } + + ToolTip { + parent: brightnessSlider.handle + visible: brightnessSlider.pressed + text: brightnessSlider.value + } } Text { Layout.fillHeight: true verticalAlignment: Text.AlignVCenter font.pixelSize: parent.height * 0.5 + font.family: fontAwesome.name text: "\uf185" } } diff --git a/OmobiDisplayApp/ressources/qml/SpinBoxDelegate.qml b/OmobiDisplayApp/ressources/qml/SpinBoxDelegate.qml index f8d8d98..4df3e3c 100644 --- a/OmobiDisplayApp/ressources/qml/SpinBoxDelegate.qml +++ b/OmobiDisplayApp/ressources/qml/SpinBoxDelegate.qml @@ -8,6 +8,7 @@ ItemDelegate { property string value: "" property alias from: spinBox.from property alias to: spinBox.to + property bool editable: false onClicked: { spinBox.value = control.value @@ -67,6 +68,7 @@ ItemDelegate { contentItem: SpinBox { id: spinBox value: control.value + editable: control.editable } onAccepted: { diff --git a/OmobiDisplayApp/ressources/qml/TextEditDialog.qml b/OmobiDisplayApp/ressources/qml/TextEditDialog.qml index f248110..66b6a9f 100644 --- a/OmobiDisplayApp/ressources/qml/TextEditDialog.qml +++ b/OmobiDisplayApp/ressources/qml/TextEditDialog.qml @@ -82,6 +82,8 @@ Dialog { SpinBoxDelegate { id: runtimeSpinBox Layout.fillWidth: true + editable: true + to: 3600 text: qsTr("Runtime (in s)") } @@ -107,7 +109,7 @@ Dialog { SpinBoxDelegate { id: scrollSpeedSpinBox Layout.fillWidth: true - from: 0 + from: 1 to: 10 text: qsTr("Scroll speed") } @@ -116,6 +118,7 @@ Dialog { id: scrollCountSpinBox Layout.fillWidth: true from: 0 + editable: true text: qsTr("Scroll count") } } diff --git a/OmobiDisplayApp/ressources/qml/main.qml b/OmobiDisplayApp/ressources/qml/main.qml index 31f4139..a9d6b27 100644 --- a/OmobiDisplayApp/ressources/qml/main.qml +++ b/OmobiDisplayApp/ressources/qml/main.qml @@ -46,7 +46,7 @@ ApplicationWindow { opacity: mainStack.currentItem.backButtonVisible ? 1:0 - font.styleName: fontAwesome.name + font.family: fontAwesome.name font.pixelSize: height * 0.6 Material.foreground: "black" @@ -63,7 +63,7 @@ ApplicationWindow { onClicked: mainStack.currentItem.backButtonClicked() } - Text { + Label { Layout.fillHeight: true Layout.fillWidth: true Layout.alignment: Layout.Center @@ -71,6 +71,7 @@ ApplicationWindow { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + color: "black" text: mainStack.currentItem.title } @@ -82,7 +83,7 @@ ApplicationWindow { opacity: mainStack.currentItem.actionButtonVisible ? 1:0 - font.styleName: fontAwesome.name + font.family: fontAwesome.name font.pixelSize: height * 0.4 Material.foreground: "black" @@ -102,7 +103,20 @@ ApplicationWindow { FontLoader { id: fontAwesome - source: "qrc:/fa5regular.woff" + source: "qrc:/fa5solid.woff" + + Component.onCompleted: { + console.log("Font name: " + fontAwesome.name) + } + } + + FontLoader { + id: fontAwesomeBrands + source: "qrc:/fa5brands.woff" + + Component.onCompleted: { + console.log("Font name: " + fontAwesome.name) + } } StackView { @@ -117,6 +131,10 @@ ApplicationWindow { mainStack.replace(currentComponent) } + onCurrentItemChanged: { + currentItem.opened() + } + initialItem: connectedPageComp replaceEnter: Transition { @@ -160,6 +178,22 @@ ApplicationWindow { } }, + State { + name: OmobiDisplayBackend.BluetoothOff + PropertyChanges { + target: mainStack + currentComponent: connectPageComp + } + }, + + State { + name: OmobiDisplayBackend.LocationPermissionDenied + PropertyChanges { + target: mainStack + currentComponent: connectPageComp + } + }, + State { name: OmobiDisplayBackend.Scanning PropertyChanges { diff --git a/OmobiDisplayApp/ressources/shared/fa5brands.woff b/OmobiDisplayApp/ressources/shared/fa5brands.woff new file mode 100644 index 0000000..e6bccbc Binary files /dev/null and b/OmobiDisplayApp/ressources/shared/fa5brands.woff differ diff --git a/OmobiDisplayApp/ressources/shared/fa5regular.woff b/OmobiDisplayApp/ressources/shared/fa5regular.woff deleted file mode 100644 index 24de566..0000000 Binary files a/OmobiDisplayApp/ressources/shared/fa5regular.woff and /dev/null differ diff --git a/OmobiDisplayApp/ressources/shared/fa5solid.woff b/OmobiDisplayApp/ressources/shared/fa5solid.woff new file mode 100644 index 0000000..105028f Binary files /dev/null and b/OmobiDisplayApp/ressources/shared/fa5solid.woff differ diff --git a/OmobiDisplayApp/ressources/shared/shared.qrc b/OmobiDisplayApp/ressources/shared/shared.qrc index 74f0f89..d1d683c 100644 --- a/OmobiDisplayApp/ressources/shared/shared.qrc +++ b/OmobiDisplayApp/ressources/shared/shared.qrc @@ -2,6 +2,7 @@ omobi.png itsblue.png - fa5regular.woff + fa5solid.woff + fa5brands.woff diff --git a/OmobiDisplayApp/ressources/translations/de.qm b/OmobiDisplayApp/ressources/translations/de.qm index 55c0917..981f93a 100644 Binary files a/OmobiDisplayApp/ressources/translations/de.qm and b/OmobiDisplayApp/ressources/translations/de.qm differ diff --git a/OmobiDisplayApp/ressources/translations/de.ts b/OmobiDisplayApp/ressources/translations/de.ts index 8ffc4b5..bce6f7c 100644 --- a/OmobiDisplayApp/ressources/translations/de.ts +++ b/OmobiDisplayApp/ressources/translations/de.ts @@ -76,7 +76,8 @@ Display name (needs restart) - Displayname\n(neutstart nötig) + Displayname +(neutstart nötig) Display code (4 digits) diff --git a/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h b/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h index 8ad72e3..fafbd58 100644 --- a/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h +++ b/vscode/OmobiLEDdisplayBluetooth/include/OmobiLedDisplay.h @@ -50,7 +50,7 @@ protected: private: enum OmobiDisplayCommand { - AuthorizeSessionCommand = 0, + AuthenticateCommand = 0, KeepAliveCommand = 1, GetAllTextSetsCommand = 10, GetTextSetParameterCommand = 11, diff --git a/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp b/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp index 57acb28..b479497 100644 --- a/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp +++ b/vscode/OmobiLEDdisplayBluetooth/src/OmobiLedDisplay.cpp @@ -53,7 +53,7 @@ void OmobiLedDisplay::onDataReceived(String dataString) else switch (requestHeader) { - case AuthorizeSessionCommand: + case AuthenticateCommand: { String combinedCode = this->bleServer->getDeviceAddress() + String(this->properties.deviceCode); String secret = this->sha256(combinedCode);