diff --git a/GraftMobileClient/core/config.h b/GraftMobileClient/core/config.h index 55cd1d41..9182d7f1 100644 --- a/GraftMobileClient/core/config.h +++ b/GraftMobileClient/core/config.h @@ -5,7 +5,8 @@ static const QString scUrl("%1/dapi"); -namespace MainnetConfiguration { +namespace MainnetConfiguration +{ static const QString scConfigTitle("Mainnet"); static const QStringList scHttpSeedSupernodes{"mainnet-seed.graft.network:18900" @@ -19,7 +20,8 @@ static const QStringList scHttpsSeedSupernodes{"mainnet-seed.graft.network:18943 static const QString scDAPIVersion("1.0G"); } -namespace TestnetConfiguration { +namespace TestnetConfiguration +{ static const QString scConfigTitle("Public Testnet"); static const QStringList scHttpSeedSupernodes{"testnet-pub-seed.graft.network:28900" @@ -33,7 +35,8 @@ static const QStringList scHttpsSeedSupernodes{"testnet-pub-seed.graft.network:2 static const QString scDAPIVersion("1.0F"); } -namespace ExperimentalTestnetConfiguration { +namespace ExperimentalTestnetConfiguration +{ static const QString scConfigTitle("Public RTA Testnet"); static const QStringList scHttpSeedSupernodes{"testnet-rta-seed.graft.network:28900" diff --git a/GraftMobileClient/ios/ios.pri b/GraftMobileClient/ios/ios.pri index 4e2a5ed7..8a3e1ea4 100644 --- a/GraftMobileClient/ios/ios.pri +++ b/GraftMobileClient/ios/ios.pri @@ -20,5 +20,13 @@ contains(DEFINES, WALLET_BUILD) { DISTFILES += \ $$PWD/wallet/Info.plist + + HEADERS += \ + $$PWD/wallet/permissiondelegate.h \ + $$PWD/wallet/ioscamerapermission.h + + SOURCES += \ + $$PWD/wallet/permissiondelegate.mm \ + $$PWD/wallet/ioscamerapermission.cpp } diff --git a/GraftMobileClient/ios/wallet/ioscamerapermission.cpp b/GraftMobileClient/ios/wallet/ioscamerapermission.cpp new file mode 100644 index 00000000..f3d98a98 --- /dev/null +++ b/GraftMobileClient/ios/wallet/ioscamerapermission.cpp @@ -0,0 +1,35 @@ +#include "ioscamerapermission.h" +#include "permissiondelegate.h" + +#include + +IOSCameraPermission::IOSCameraPermission(QObject *parent) + : QObject(parent) + ,mTimer(-1) +{ +} + +bool IOSCameraPermission::hasPermission() +{ + if (!PermissionDelegate::isCameraAuthorised()) + { + mTimer = startTimer(50); + return false; + } + return true; +} + +void IOSCameraPermission::stopTimer() +{ + killTimer(mTimer); +} + +void IOSCameraPermission::timerEvent(QTimerEvent *event) +{ + if ((event->timerId() == mTimer) && PermissionDelegate::isCameraAuthorised()) + { + emit hasCameraPermission(true); + stopTimer(); + } +} + diff --git a/GraftMobileClient/ios/wallet/ioscamerapermission.h b/GraftMobileClient/ios/wallet/ioscamerapermission.h new file mode 100644 index 00000000..8297ff0f --- /dev/null +++ b/GraftMobileClient/ios/wallet/ioscamerapermission.h @@ -0,0 +1,25 @@ +#ifndef IOSCAMERAPERMISSION_H +#define IOSCAMERAPERMISSION_H + +#include + +class IOSCameraPermission : public QObject +{ + Q_OBJECT +public: + explicit IOSCameraPermission(QObject *parent = nullptr); + + Q_INVOKABLE bool hasPermission(); + Q_INVOKABLE void stopTimer(); + +signals: + void hasCameraPermission(bool result); + +protected: + void timerEvent(QTimerEvent *event) override; + +private: + int mTimer; +}; + +#endif // IOSCAMERAPERMISSION_H diff --git a/GraftMobileClient/ios/wallet/permissiondelegate.h b/GraftMobileClient/ios/wallet/permissiondelegate.h new file mode 100644 index 00000000..9a8d7a50 --- /dev/null +++ b/GraftMobileClient/ios/wallet/permissiondelegate.h @@ -0,0 +1,9 @@ +#ifndef PERMISSIONDELEGATE_H +#define PERMISSIONDELEGATE_H + +class PermissionDelegate +{ +public: + static bool isCameraAuthorised(); +}; +#endif // PERMISSIONDELEGATE_H diff --git a/GraftMobileClient/ios/wallet/permissiondelegate.mm b/GraftMobileClient/ios/wallet/permissiondelegate.mm new file mode 100644 index 00000000..d6f88ccb --- /dev/null +++ b/GraftMobileClient/ios/wallet/permissiondelegate.mm @@ -0,0 +1,10 @@ +#include "permissiondelegate.h" + +#include +#include + +bool PermissionDelegate::isCameraAuthorised() +{ + NSString *mediaType = AVMediaTypeVideo; + return [AVCaptureDevice authorizationStatusForMediaType:mediaType] == AVAuthorizationStatusAuthorized; +} diff --git a/GraftMobileClient/main.cpp b/GraftMobileClient/main.cpp index d747f954..537f7e38 100644 --- a/GraftMobileClient/main.cpp +++ b/GraftMobileClient/main.cpp @@ -41,6 +41,12 @@ static_assert(false, "QTBUG-65820 in Android Debug builds"); #include #endif +#ifdef WALLET_BUILD +#if defined(Q_OS_IOS) +#include "ios/wallet/ioscamerapermission.h" +#endif +#endif + #ifdef POS_BUILD #if defined(Q_OS_ANDROID) || defined (Q_OS_IOS) #include "imagepicker.h" @@ -109,6 +115,12 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty(QStringLiteral("PaymentProductModel"), client.paymentProductModel()); engine.rootContext()->setContextProperty(QStringLiteral("GraftClient"), &client); + +#if defined(Q_OS_IOS) + IOSCameraPermission cameraPermission; + engine.rootContext()->setContextProperty(QStringLiteral("IOSCameraPermission"), &cameraPermission); +#endif + engine.load(QUrl(QLatin1String("qrc:/wallet/main.qml"))); #endif if (engine.rootObjects().isEmpty()) diff --git a/GraftMobileClient/resources/LicenseAgreementScreen.qml b/GraftMobileClient/resources/LicenseAgreementScreen.qml index cdf69896..ee326e63 100644 --- a/GraftMobileClient/resources/LicenseAgreementScreen.qml +++ b/GraftMobileClient/resources/LicenseAgreementScreen.qml @@ -126,7 +126,6 @@ BaseScreen { "SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.") } } - } WideActionButton { diff --git a/GraftMobileClient/resources/QRScanningView.qml b/GraftMobileClient/resources/QRScanningView.qml deleted file mode 100644 index 7c25cff0..00000000 --- a/GraftMobileClient/resources/QRScanningView.qml +++ /dev/null @@ -1,91 +0,0 @@ -import QtQuick 2.9 -import QtMultimedia 5.9 -import QtQuick.Controls 2.2 -import QtGraphicalEffects 1.0 -import QZXing 2.3 - -Item { - property string lastTag: "" - - signal qrCodeDetected(string message) - - onVisibleChanged: { - if (visible) { - camera.start() - } else { - camera.stop() - } - } - - Camera { - id: camera - focus { - focusMode: Camera.FocusContinuous - focusPointMode: Camera.FocusPointCustom - customFocusPoint: Qt.point(0.5, 0.5) // Focus relative to the frame center - } - } - - VideoOutput { - id: videoOutput - anchors.fill: parent - source: camera - autoOrientation: true - focus: visible - fillMode: VideoOutput.PreserveAspectCrop - filters: [ zxingFilter ] - } - - Rectangle { - anchors.fill: parent - id: captureZone - color: "transparent" - - Rectangle { - width: parent.width * 0.75 - height: width - anchors.centerIn: parent - color: "green" - opacity: 0.7 - } - } - - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.5 - } - - OpacityMask { - anchors.fill: parent - source: videoOutput - maskSource: captureZone - } - - QZXingFilter { - id: zxingFilter - captureRect: { - var rect = Qt.rect(0, 0, 1, 1) - var normalizedRect = videoOutput.mapNormalizedRectToItem(rect) - return videoOutput.mapRectToSource(normalizedRect) - } - - decoder { - enabledDecoders: QZXing.DecoderFormat_QR_CODE - tryHarder: false - onTagFound: { - if (lastTag != tag) { - lastTag = tag - console.log(tag + " | " + " | " + decoder.charSet()) - camera.stop() - qrCodeDetected(tag) - } - } - } - } - - function resetView() { - camera.start() - lastTag = "" - } -} diff --git a/GraftMobileClient/resources/SelectImageButton.qml b/GraftMobileClient/resources/SelectImageButton.qml index 956a30eb..2c455144 100644 --- a/GraftMobileClient/resources/SelectImageButton.qml +++ b/GraftMobileClient/resources/SelectImageButton.qml @@ -1,6 +1,6 @@ import QtQuick 2.9 -import QtQuick.Controls.Material 2.2 import QtQuick.Controls 2.2 +import QtQuick.Controls.Material 2.2 import QtQuick.Layouts 1.3 Button { diff --git a/GraftMobileClient/resources/SelectImageDialog.qml b/GraftMobileClient/resources/SelectImageDialog.qml index bc2f0903..1961d926 100644 --- a/GraftMobileClient/resources/SelectImageDialog.qml +++ b/GraftMobileClient/resources/SelectImageDialog.qml @@ -1,6 +1,6 @@ import QtQuick 2.9 -import QtQuick.Controls.Material 2.2 import QtQuick.Controls 2.2 +import QtQuick.Controls.Material 2.2 import QtQuick.Layouts 1.3 Popup { diff --git a/GraftMobileClient/resources/SendCoinScreen.qml b/GraftMobileClient/resources/SendCoinScreen.qml index b232cc16..c986cdda 100644 --- a/GraftMobileClient/resources/SendCoinScreen.qml +++ b/GraftMobileClient/resources/SendCoinScreen.qml @@ -5,6 +5,7 @@ import QtQuick.Controls 2.2 import com.device.platform 1.0 import org.graft 1.0 import "components" +import "wallet" BaseScreen { id: sendCoinScreen @@ -126,6 +127,7 @@ BaseScreen { } QRScanningView { + id: qRScanningView onQrCodeDetected: { receiversAddress.text = message changeBehaviorButton() @@ -141,6 +143,7 @@ BaseScreen { function changeBehaviorButton() { stackLayout.currentIndex = 0 + qRScanningView.stopScanningView() } function checkingData() { diff --git a/GraftMobileClient/resources/android/wallet/QRScanningView.qml b/GraftMobileClient/resources/android/wallet/QRScanningView.qml new file mode 100644 index 00000000..be4b5e6b --- /dev/null +++ b/GraftMobileClient/resources/android/wallet/QRScanningView.qml @@ -0,0 +1,136 @@ +import QtQuick 2.9 +import QtMultimedia 5.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import QZXing 2.3 + +Item { + property string lastTag: "" + + signal qrCodeDetected(string message) + + state: "scanScreen" + onVisibleChanged: { + if (visible) { + camera.start() + } else { + camera.stop() + } + } + + Item { + id: scanScreen + anchors.fill: parent + visible: false + + Camera { + id: camera + focus { + focusMode: Camera.FocusContinuous + focusPointMode: Camera.FocusPointCustom + customFocusPoint: Qt.point(0.5, 0.5) // Focus relative to the frame center + } + onCameraStateChanged: { + if (cameraState === Camera.UnloadedState) { + camera.start() + } + } + } + + VideoOutput { + id: videoOutput + anchors.fill: parent + source: camera + autoOrientation: true + focus: visible + fillMode: VideoOutput.PreserveAspectCrop + filters: [ zxingFilter ] + } + + Rectangle { + anchors.fill: parent + id: captureZone + color: "transparent" + + Rectangle { + width: parent.width * 0.75 + height: width + anchors.centerIn: parent + color: "green" + opacity: 0.7 + } + } + + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.5 + } + + OpacityMask { + anchors.fill: parent + source: videoOutput + maskSource: captureZone + } + + QZXingFilter { + id: zxingFilter + captureRect: { + var rect = Qt.rect(0, 0, 1, 1) + var normalizedRect = videoOutput.mapNormalizedRectToItem(rect) + return videoOutput.mapRectToSource(normalizedRect) + } + + decoder { + enabledDecoders: QZXing.DecoderFormat_QR_CODE + tryHarder: false + onTagFound: { + if (lastTag !== tag) { + lastTag = tag + console.log(tag + " | " + " | " + decoder.charSet()) + camera.stop() + qrCodeDetected(tag) + } + } + } + } + } + + Item { + id: messagesScreen + implicitHeight: 110 + implicitWidth: parent.width - 60 + anchors.centerIn: parent + visible: false + + Label { + anchors.fill: parent + wrapMode: Label.WordWrap + horizontalAlignment: Label.AlignHCenter + font.pixelSize: 16 + color: "#A8A8A8" + text: qsTr("You haven't permission for the camera. Please, turn on camera permission " + + "in settings of the application.") + } + } + + states: [ + State { + name: "scanScreen" + PropertyChanges { target: scanScreen; visible: true } + PropertyChanges { target: messagesScreen; visible: false } + }, + + State { + name: "messagesScreen" + PropertyChanges { target: scanScreen; visible: false } + PropertyChanges { target: messagesScreen; visible: true } + when: camera.cameraStatus === 0 && camera.cameraState === 0 + } + ] + + function resetView() { + camera.start() + lastTag = "" + } +} diff --git a/GraftMobileClient/resources/android_qml.qrc b/GraftMobileClient/resources/android_qml.qrc index 7c2c4ff4..36aeb901 100644 --- a/GraftMobileClient/resources/android_qml.qrc +++ b/GraftMobileClient/resources/android_qml.qrc @@ -23,5 +23,6 @@ android/wallet/BalanceScreen.qml android/wallet/PaymentConfirmationScreen.qml android/wallet/SettingsScreen.qml + android/wallet/QRScanningView.qml diff --git a/GraftMobileClient/resources/general_qml.qrc b/GraftMobileClient/resources/general_qml.qrc index 2c7a9cc1..61bf869c 100644 --- a/GraftMobileClient/resources/general_qml.qrc +++ b/GraftMobileClient/resources/general_qml.qrc @@ -1,7 +1,6 @@ qtquickcontrols2.conf - QRScanningView.qml BaseHeader.qml CartItem.qml GraftApplicationWindow.qml diff --git a/GraftMobileClient/resources/ios/wallet/BalanceScreen.qml b/GraftMobileClient/resources/ios/wallet/BalanceScreen.qml index a65a9e51..ead3bf1a 100644 --- a/GraftMobileClient/resources/ios/wallet/BalanceScreen.qml +++ b/GraftMobileClient/resources/ios/wallet/BalanceScreen.qml @@ -1,8 +1,8 @@ import QtQuick 2.9 import QtQuick.Layouts 1.3 +import org.graft 1.0 import "../" import "../components" -import org.graft 1.0 BaseBalanceScreen { diff --git a/GraftMobileClient/resources/ios/wallet/QRScanningView.qml b/GraftMobileClient/resources/ios/wallet/QRScanningView.qml new file mode 100644 index 00000000..1c7ca405 --- /dev/null +++ b/GraftMobileClient/resources/ios/wallet/QRScanningView.qml @@ -0,0 +1,151 @@ +import QtQuick 2.9 +import QtMultimedia 5.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import QZXing 2.3 +import com.device.platform 1.0 + +Item { + property string lastTag: "" + + signal qrCodeDetected(string message) + + state: "messagesScreen" + onVisibleChanged: { + if (visible) { + camera.start() + } else { + camera.stop() + } + } + + Connections { + target: Detector.isPlatform(Platform.Desktop) ? null : IOSCameraPermission + onHasCameraPermission: state = "scanScreen" + } + + Item { + id: scanScreen + anchors.fill: parent + visible: false + + Camera { + id: camera + focus { + focusMode: Camera.FocusContinuous + focusPointMode: Camera.FocusPointCustom + customFocusPoint: Qt.point(0.5, 0.5) // Focus relative to the frame center + } + } + + VideoOutput { + id: videoOutput + anchors.fill: parent + source: camera + autoOrientation: true + focus: visible + fillMode: VideoOutput.PreserveAspectCrop + filters: [ zxingFilter ] + } + + Rectangle { + anchors.fill: parent + id: captureZone + color: "transparent" + + Rectangle { + width: parent.width * 0.75 + height: width + anchors.centerIn: parent + color: "green" + opacity: 0.7 + } + } + + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.5 + } + + OpacityMask { + anchors.fill: parent + source: videoOutput + maskSource: captureZone + } + + QZXingFilter { + id: zxingFilter + captureRect: { + var rect = Qt.rect(0, 0, 1, 1) + var normalizedRect = videoOutput.mapNormalizedRectToItem(rect) + return videoOutput.mapRectToSource(normalizedRect) + } + + decoder { + enabledDecoders: QZXing.DecoderFormat_QR_CODE + tryHarder: false + onTagFound: { + if (lastTag !== tag) { + lastTag = tag + console.log(tag + " | " + " | " + decoder.charSet()) + camera.stop() + qrCodeDetected(tag) + } + } + } + } + } + + Item { + id: messagesScreen + implicitHeight: 110 + implicitWidth: parent.width - 60 + anchors.centerIn: parent + visible: false + + Label { + anchors.fill: parent + wrapMode: Label.WordWrap + horizontalAlignment: Label.AlignHCenter + verticalAlignment: Label.AlignVCenter + font.pixelSize: 16 + color: "#A8A8A8" + text: QtMultimedia.availableCameras.length > 0 ? qsTr("You haven't permission " + + "for the camera. Please, turn on camera permission in settings of the " + + "application.") : qsTr("The application can't find camera on this device. To " + + "use QR-code scanning option, please, connect camera to your device.") + } + } + + states: [ + State { + name: "scanScreen" + PropertyChanges { target: scanScreen; visible: true } + PropertyChanges { target: messagesScreen; visible: false } + when: scanning() + }, + + State { + name: "messagesScreen" + PropertyChanges { target: scanScreen; visible: false } + PropertyChanges { target: messagesScreen; visible: true } + } + ] + + function scanning() { + if (Detector.isDesktop()) { + return QtMultimedia.availableCameras.length > 0 + } + return IOSCameraPermission.hasPermission() + } + + function stopScanningView() { + IOSCameraPermission.stopTimer() + } + + function resetView() { + camera.start() + lastTag = "" + } +} diff --git a/GraftMobileClient/resources/ios_qml.qrc b/GraftMobileClient/resources/ios_qml.qrc index a00ce5bf..1c055aad 100644 --- a/GraftMobileClient/resources/ios_qml.qrc +++ b/GraftMobileClient/resources/ios_qml.qrc @@ -21,5 +21,6 @@ ios/wallet/BalanceScreen.qml ios/wallet/PaymentConfirmationScreen.qml ios/wallet/SettingsScreen.qml + ios/wallet/QRScanningView.qml diff --git a/GraftMobileClient/resources/wallet/QRScanningScreen.qml b/GraftMobileClient/resources/wallet/QRScanningScreen.qml index 835b0c09..4147ccd9 100644 --- a/GraftMobileClient/resources/wallet/QRScanningScreen.qml +++ b/GraftMobileClient/resources/wallet/QRScanningScreen.qml @@ -1,5 +1,6 @@ import QtQuick 2.9 import QtQuick.Dialogs 1.2 +import com.device.platform 1.0 import "../components" import "../" @@ -7,6 +8,8 @@ BaseScreen { id: qrScanning title: qsTr("Pay") + specialBackMode: Detector.isPlatform(Platform.IOS) ? pop : goBack + Connections { target: GraftClient @@ -30,4 +33,9 @@ BaseScreen { anchors.fill: parent onQrCodeDetected: GraftClient.getPOSData(message) } + + function pop() { + qRScanningView.stopScanningView() + goBack() + } }