diff --git a/client/client.pro b/client/client.pro index 1cc9dbd2..b6b00330 100644 --- a/client/client.pro +++ b/client/client.pro @@ -296,15 +296,17 @@ ios { platforms/ios/bigint.h \ platforms/ios/bigintipv6addr.h \ platforms/ios/ipaddress.h \ - platforms/ios/ipaddressrange.h - + platforms/ios/ipaddressrange.h \ + platforms/ios/QRCodeReader.h + SOURCES += \ protocols/ios_vpnprotocol.mm \ platforms/ios/iosnotificationhandler.mm \ platforms/ios/json.cpp \ platforms/ios/iosglue.mm \ platforms/ios/ipaddress.cpp \ - platforms/ios/ipaddressrange.cpp + platforms/ios/ipaddressrange.cpp \ + platforms/ios/QRCodeReader.mm Q_ENABLE_BITCODE.value = NO Q_ENABLE_BITCODE.name = ENABLE_BITCODE diff --git a/client/main.cpp b/client/main.cpp index 9cdab7eb..b1419f18 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -37,6 +37,8 @@ #include "QZXing.h" +#include "platforms/ios/QRCodeReader.h" + #include "debug.h" #include "defines.h" @@ -167,6 +169,7 @@ int main(int argc, char *argv[]) declareQmlContainerEnum(); qmlRegisterType("PageType", 1, 0, "PageType"); + qmlRegisterType("QRCodeReader", 1, 0, "QRCodeReader"); QScopedPointer containerProps(new ContainerProps); qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", containerProps.get()); diff --git a/client/platforms/ios/QRCodeReader.h b/client/platforms/ios/QRCodeReader.h new file mode 100644 index 00000000..bc8c9925 --- /dev/null +++ b/client/platforms/ios/QRCodeReader.h @@ -0,0 +1,28 @@ +#ifndef QRCODEREADER_H +#define QRCODEREADER_H + +#include +#include + +class QRCodeReader: public QObject { + Q_OBJECT + +public: + QRCodeReader(); + + QRect cameraSize(); + +public slots: + void startReading(); + void stopReading(); + void setCameraSize(QRect value); + +signals: + void codeReaded(QString code); + +private: + void* m_qrCodeReader; + QRect m_cameraSize; +}; + +#endif // QRCODEREADER_H diff --git a/client/platforms/ios/QRCodeReader.mm b/client/platforms/ios/QRCodeReader.mm new file mode 100644 index 00000000..85f71ba6 --- /dev/null +++ b/client/platforms/ios/QRCodeReader.mm @@ -0,0 +1,109 @@ +#include "QRCodeReader.h" + +#import +#import + +@interface QRCodeReaderImpl : UIViewController +@end + +@interface QRCodeReaderImpl () +@property (nonatomic) QRCodeReader* qrCodeReader; +@property (nonatomic, strong) AVCaptureSession *captureSession; +@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewPlayer; +@end + + +@implementation QRCodeReaderImpl + +- (void)viewDidLoad { + [super viewDidLoad]; + + _captureSession = nil; +} + +- (void)setQrCodeReader: (QRCodeReader*)value { + _qrCodeReader = value; +} + +- (BOOL)startReading { + NSError *error; + + AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo]; + AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice: captureDevice error: &error]; + + if(!deviceInput) { + NSLog(@"Error %@", error.localizedDescription); + return NO; + } + + _captureSession = [[AVCaptureSession alloc]init]; + [_captureSession addInput:deviceInput]; + + AVCaptureMetadataOutput *capturedMetadataOutput = [[AVCaptureMetadataOutput alloc] init]; + [_captureSession addOutput:capturedMetadataOutput]; + + dispatch_queue_t dispatchQueue; + dispatchQueue = dispatch_queue_create("myQueue", NULL); + [capturedMetadataOutput setMetadataObjectsDelegate: self queue: dispatchQueue]; + [capturedMetadataOutput setMetadataObjectTypes: [NSArray arrayWithObject:AVMetadataObjectTypeQRCode]]; + + _videoPreviewPlayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession: _captureSession]; + + CGFloat tabBarHeight = 20.0; + QRect cameraRect = _qrCodeReader->cameraSize(); + CGRect cameraCGRect = CGRectMake(cameraRect.x(), + cameraRect.y() + tabBarHeight, + cameraRect.width(), + cameraRect.height()); + + [_videoPreviewPlayer setVideoGravity: AVLayerVideoGravityResizeAspect]; + [_videoPreviewPlayer setFrame: cameraCGRect]; + + CALayer* layer = [UIApplication sharedApplication].keyWindow.layer; + [layer addSublayer: _videoPreviewPlayer]; + + [_captureSession startRunning]; + + return YES; +} + +- (void)stopReading { + [_captureSession stopRunning]; + _captureSession = nil; + + [_videoPreviewPlayer removeFromSuperlayer]; +} + +- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection { + + if (metadataObjects != nil && metadataObjects.count > 0) { + AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0]; + + if ([[metadataObject type] isEqualToString: AVMetadataObjectTypeQRCode]) { + _qrCodeReader->emit codeReaded([metadataObject stringValue].UTF8String); + } + } +} + +@end + +QRCodeReader::QRCodeReader() { + m_qrCodeReader = [[QRCodeReaderImpl alloc] init]; + [m_qrCodeReader setQrCodeReader: this]; +} + +QRect QRCodeReader::cameraSize() { + return m_cameraSize; +} + +void QRCodeReader::setCameraSize(QRect value) { + m_cameraSize = value; +} + +void QRCodeReader::startReading() { + [m_qrCodeReader startReading]; +} + +void QRCodeReader::stopReading() { + [m_qrCodeReader stopReading]; +} diff --git a/client/ui/qml/Pages/PageQrDecoder.qml b/client/ui/qml/Pages/PageQrDecoder.qml index 6506b7fa..ead2dde3 100644 --- a/client/ui/qml/Pages/PageQrDecoder.qml +++ b/client/ui/qml/Pages/PageQrDecoder.qml @@ -3,6 +3,7 @@ import QtQuick.Controls 2.12 import PageEnum 1.0 import QtMultimedia 5.5 import QZXing 3.2 +import QRCodeReader 1.0 import "./" import "../Controls" @@ -51,108 +52,126 @@ PageBase { Item { anchors.fill: parent - Camera - { - id:camera - focus { - focusMode: CameraFocus.FocusContinuous - focusPointMode: CameraFocus.FocusPointAuto + + QRCodeReader { + id: qrCodeReader + + onCodeReaded: { + QrDecoderLogic.onDetectedQrCode(code) } + + Component.onCompleted: { + qrCodeReader.setCameraSize(Qt.rect(loader.x, + loader.y, + loader.width, + loader.height)) + qrCodeReader.startReading() + } + Component.onDestruction: qrCodeReader.stopReading() } - VideoOutput - { - id: videoOutput - source: camera - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - autoOrientation: true - fillMode: VideoOutput.PreserveAspectFit - filters: [ zxingFilter ] +// Camera +// { +// id:camera +// focus { +// focusMode: CameraFocus.FocusContinuous +// focusPointMode: CameraFocus.FocusPointAuto +// } +// } + +// VideoOutput +// { +// id: videoOutput +// source: camera +// anchors.top: parent.top +// anchors.bottom: parent.bottom +// anchors.left: parent.left +// anchors.right: parent.right +// autoOrientation: true +// fillMode: VideoOutput.PreserveAspectFit +// filters: [ zxingFilter ] - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width *0.15 - height: videoOutput.contentRect.height - x: (videoOutput.width - videoOutput.contentRect.width)/2 - anchors.verticalCenter: videoOutput.verticalCenter - } +// Rectangle { +// color: "black" +// opacity: 0.5 +// width: videoOutput.contentRect.width * 0.15 +// height: videoOutput.contentRect.height +// x: (videoOutput.width - videoOutput.contentRect.width)/2 +// anchors.verticalCenter: videoOutput.verticalCenter +// } - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width *0.15 - height: videoOutput.contentRect.height - x: videoOutput.width/2 + videoOutput.contentRect.width/2 - videoOutput.contentRect.width *0.15 - anchors.verticalCenter: videoOutput.verticalCenter - } +// Rectangle { +// color: "black" +// opacity: 0.5 +// width: videoOutput.contentRect.width * 0.15 +// height: videoOutput.contentRect.height +// x: videoOutput.width/2 + videoOutput.contentRect.width/2 - videoOutput.contentRect.width * 0.15 +// anchors.verticalCenter: videoOutput.verticalCenter +// } - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width *0.7 - height: videoOutput.contentRect.height *0.15 - x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width *0.15 - y: (videoOutput.height - videoOutput.contentRect.height)/2 - } +// Rectangle { +// color: "black" +// opacity: 0.5 +// width: videoOutput.contentRect.width * 0.7 +// height: videoOutput.contentRect.height * 0.15 +// x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15 +// y: (videoOutput.height - videoOutput.contentRect.height)/2 +// } - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width *0.7 - height: videoOutput.contentRect.height *0.15 - x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width *0.15 - y: videoOutput.height/2 + videoOutput.contentRect.height/2 - videoOutput.contentRect.height *0.15 - } +// Rectangle { +// color: "black" +// opacity: 0.5 +// width: videoOutput.contentRect.width * 0.7 +// height: videoOutput.contentRect.height * 0.15 +// x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15 +// y: videoOutput.height/2 + videoOutput.contentRect.height/2 - videoOutput.contentRect.height * 0.15 +// } - LabelType { - width: parent.width - text: qsTr("Decoded QR chunks " + QrDecoderLogic.receivedChunksCount + "/" + QrDecoderLogic.totalChunksCount) - horizontalAlignment: Text.AlignLeft - visible: QrDecoderLogic.totalChunksCount > 0 - anchors.horizontalCenter: videoOutput.horizontalCenter - y: videoOutput.height/2 + videoOutput.contentRect.height/2 - } - } +// LabelType { +// width: parent.width +// text: qsTr("Decoded QR chunks " + QrDecoderLogic.receivedChunksCount + "/" + QrDecoderLogic.totalChunksCount) +// horizontalAlignment: Text.AlignLeft +// visible: QrDecoderLogic.totalChunksCount > 0 +// anchors.horizontalCenter: videoOutput.horizontalCenter +// y: videoOutput.height/2 + videoOutput.contentRect.height/2 +// } +// } - QZXingFilter - { - id: zxingFilter - orientation: videoOutput.orientation - captureRect: { - // setup bindings - videoOutput.contentRect; - videoOutput.sourceRect; - return videoOutput.mapRectToSource(videoOutput.mapNormalizedRectToItem(Qt.rect( - 0.15, 0.15, 0.7, 0.7 //0, 0, 1.0, 1.0 - ))); - } +// QZXingFilter +// { +// id: zxingFilter +// orientation: videoOutput.orientation +// captureRect: { +// // setup bindings +// videoOutput.contentRect; +// videoOutput.sourceRect; +// return videoOutput.mapRectToSource(videoOutput.mapNormalizedRectToItem(Qt.rect( +// 0.15, 0.15, 0.7, 0.7 //0, 0, 1.0, 1.0 +// ))); +// } - decoder { - enabledDecoders: QZXing.DecoderFormat_QR_CODE +// decoder { +// enabledDecoders: QZXing.DecoderFormat_QR_CODE - onTagFound: { - QrDecoderLogic.onDetectedQrCode(tag) - } +// onTagFound: { +// QrDecoderLogic.onDetectedQrCode(tag) +// } - tryHarder: true - } +// tryHarder: true +// } - property int framesDecoded: 0 - property real timePerFrameDecode: 0 +// property int framesDecoded: 0 +// property real timePerFrameDecode: 0 - onDecodingFinished: - { - timePerFrameDecode = (decodeTime + framesDecoded * timePerFrameDecode) / (framesDecoded + 1); - framesDecoded++; - if(succeeded) - console.log("frame finished: " + succeeded, decodeTime, timePerFrameDecode, framesDecoded); - } - } +// onDecodingFinished: +// { +// timePerFrameDecode = (decodeTime + framesDecoded * timePerFrameDecode) / (framesDecoded + 1); +// framesDecoded++; +// if(succeeded) +// console.log("frame finished: " + succeeded, decodeTime, timePerFrameDecode, framesDecoded); +// } +// } }