From e6b74ac752d118ddd8bf96ed172e9a83f8d9ba11 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Wed, 23 Dec 2020 11:24:01 +0100 Subject: [PATCH 1/8] Added possibility to choose facing camera on start. Moved the size listener to plugin side. --- .../net/touchcapture/qr/flutterqr/QRView.kt | 23 ++- .../qr/flutterqr/QRViewFactory.kt | 5 +- example/.gitignore | 1 + example/ios/Runner.xcodeproj/project.pbxproj | 2 - example/lib/main.dart | 31 ++-- ios/Classes/QRView.swift | 119 ++++++------ ios/Classes/QRViewFactory.swift | 4 +- lib/src/qr_code_scanner.dart | 169 +++++++++++------- 8 files changed, 211 insertions(+), 143 deletions(-) diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt index 48329c5..945cb37 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt @@ -17,7 +17,7 @@ import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.platform.PlatformView -class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) : +class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, private val params: HashMap) : PlatformView, MethodChannel.MethodCallHandler { private var isTorchOn: Boolean = false @@ -66,6 +66,12 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when(call.method){ + "startScan" -> { + startScan() + } + "stopScan" -> { + stopScan() + } "flipCamera" -> { flipCamera() } @@ -127,14 +133,16 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) private fun initBarCodeView(): BarcodeView? { if (barcodeView == null) { - barcodeView = createBarCodeView() + barcodeView = BarcodeView(Shared.activity) + if (params["cameraFacing"] as Int == 1) { + barcodeView?.cameraSettings?.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT + } } return barcodeView } - private fun createBarCodeView(): BarcodeView { - val barcode = BarcodeView(Shared.activity) - barcode.decodeContinuous( + private fun startScan() { + barcodeView?.decodeContinuous( object : BarcodeCallback { override fun barcodeResult(result: BarcodeResult) { val code = mapOf( @@ -147,7 +155,10 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) override fun possibleResultPoints(resultPoints: List) {} } ) - return barcode + } + + private fun stopScan() { + barcodeView?.stopDecoding() } private fun hasCameraPermission(): Boolean { diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt index a797084..e81303d 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt @@ -10,8 +10,9 @@ import io.flutter.plugin.platform.PlatformViewFactory class QRViewFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { - override fun create(context: Context, id: Int, obj: Any?): PlatformView { - return QRView(messenger, id, context) + override fun create(context: Context, id: Int, args: Any?): PlatformView { + val params = args as HashMap + return QRView(messenger, id, context, params) } } \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore index 47e0b4d..4736dd6 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -9,6 +9,7 @@ .buildlog/ .history .svn/ +.last_build_id # IntelliJ related *.iml diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index d28e5f5..d3ce8b9 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -264,13 +264,11 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", ); diff --git a/example/lib/main.dart b/example/lib/main.dart index beb8d51..72fdd08 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -154,25 +154,18 @@ class _QRViewExampleState extends State { : 300.0; // To ensure the Scanner view is properly sizes after rotation // we need to listen for Flutter SizeChanged notification and update controller - return NotificationListener( - onNotification: (notification) { - Future.microtask( - () => controller?.updateDimensions(qrKey, scanArea: scanArea)); - return false; - }, - child: SizeChangedLayoutNotifier( - key: const Key('qr-size-notifier'), - child: QRView( - key: qrKey, - onQRViewCreated: _onQRViewCreated, - overlay: QrScannerOverlayShape( - borderColor: Colors.red, - borderRadius: 10, - borderLength: 30, - borderWidth: 10, - cutOutSize: scanArea, - ), - ))); + return QRView( + key: qrKey, + cameraFacing: CameraFacing.front, + onQRViewCreated: _onQRViewCreated, + overlay: QrScannerOverlayShape( + borderColor: Colors.red, + borderRadius: 10, + borderLength: 30, + borderWidth: 10, + cutOutSize: scanArea, + ), + ); } void _onQRViewCreated(QRViewController controller) { diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index 298f7a9..3e36441 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -13,67 +13,28 @@ public class QRView:NSObject,FlutterPlatformView { var scanner: MTBBarcodeScanner? var registrar: FlutterPluginRegistrar var channel: FlutterMethodChannel + var cameraFacing: MTBCamera - public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64){ + public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary){ self.registrar = registrar previewView = UIView(frame: frame) + cameraFacing = MTBCamera.init(rawValue: UInt(Int(params["cameraFacing"] as! Double))) ?? MTBCamera.back channel = FlutterMethodChannel(name: "net.touchcapture.qr.flutterqr/qrview_\(id)", binaryMessenger: registrar.messenger()) } - func isCameraAvailable(success: Bool) -> Void { - if success { - do { - try scanner?.startScanning(resultBlock: { [weak self] codes in - if let codes = codes { - for code in codes { - var typeString: String; - switch(code.type) { - case AVMetadataObject.ObjectType.aztec: - typeString = "AZTEC" - case AVMetadataObject.ObjectType.code39: - typeString = "CODE_39" - case AVMetadataObject.ObjectType.code93: - typeString = "CODE_93" - case AVMetadataObject.ObjectType.code128: - typeString = "CODE_128" - case AVMetadataObject.ObjectType.dataMatrix: - typeString = "DATA_MATRIX" - case AVMetadataObject.ObjectType.ean8: - typeString = "EAN_8" - case AVMetadataObject.ObjectType.ean13: - typeString = "EAN_13" - case AVMetadataObject.ObjectType.itf14: - typeString = "ITF" - case AVMetadataObject.ObjectType.pdf417: - typeString = "PDF_417" - case AVMetadataObject.ObjectType.qr: - typeString = "QR_CODE" - case AVMetadataObject.ObjectType.upce: - typeString = "UPC_E" - default: - return - } - guard let stringValue = code.stringValue else { continue } - let result = ["code": stringValue, "type": typeString] - self?.channel.invokeMethod("onRecognizeQR", arguments: result) - } - } - }) - } catch { - NSLog("Unable to start scanning") - } - } else { - UIAlertView(title: "Scanning Unavailable", message: "This app does not have permission to access the camera", delegate: nil, cancelButtonTitle: nil, otherButtonTitles: "Ok").show() - } + deinit { + scanner?.stopScanning() } public func view() -> UIView { channel.setMethodCallHandler({ - [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in + [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in switch(call.method){ case "setDimensions": let arguments = call.arguments as! Dictionary self?.setDimensions(width: arguments["width"] ?? 0, height: arguments["height"] ?? 0, scanArea: arguments["scanArea"] ?? 0) + case "startScan": + self?.startScan(result) case "flipCamera": self?.flipCamera() case "toggleFlash": @@ -106,9 +67,67 @@ public class QRView:NSObject,FlutterPlatformView { self.scanner?.scanRect = CGRect(x: Double(midX) - (scanArea / 2), y: Double(midY) - (scanArea / 2), width: scanArea, height: scanArea) } } - - - MTBBarcodeScanner.requestCameraPermission(success: isCameraAvailable) + } + } + + func startScan(_ result: @escaping FlutterResult) -> Void { + scanner = MTBBarcodeScanner(previewView: previewView) + + MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in + if permissionGranted { + do { + try self.scanner?.startScanning(with: self.cameraFacing, resultBlock: { [weak self] codes in + if let codes = codes { + for code in codes { + var typeString: String; + switch(code.type) { + case AVMetadataObject.ObjectType.aztec: + typeString = "AZTEC" + case AVMetadataObject.ObjectType.code39: + typeString = "CODE_39" + case AVMetadataObject.ObjectType.code93: + typeString = "CODE_93" + case AVMetadataObject.ObjectType.code128: + typeString = "CODE_128" + case AVMetadataObject.ObjectType.dataMatrix: + typeString = "DATA_MATRIX" + case AVMetadataObject.ObjectType.ean8: + typeString = "EAN_8" + case AVMetadataObject.ObjectType.ean13: + typeString = "EAN_13" + case AVMetadataObject.ObjectType.itf14: + typeString = "ITF" + case AVMetadataObject.ObjectType.pdf417: + typeString = "PDF_417" + case AVMetadataObject.ObjectType.qr: + typeString = "QR_CODE" + case AVMetadataObject.ObjectType.upce: + typeString = "UPC_E" + default: + return + } + guard let stringValue = code.stringValue else { continue } + let result = ["code": stringValue, "type": typeString] + self?.channel.invokeMethod("onRecognizeQR", arguments: result) + } + } + }) + } catch { + let error = FlutterError(code: "unknown-error", message: "Unable to start scanning", details: nil) + result(error) + } + } else { + let error = FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil) + result(error) + } + }) + } + + func stopScan(){ + if let sc: MTBBarcodeScanner = scanner { + if sc.isScanning() { + sc.stopScanning() + } } } diff --git a/ios/Classes/QRViewFactory.swift b/ios/Classes/QRViewFactory.swift index fff745b..6f851f6 100644 --- a/ios/Classes/QRViewFactory.swift +++ b/ios/Classes/QRViewFactory.swift @@ -17,8 +17,8 @@ public class QRViewFactory: NSObject, FlutterPlatformViewFactory { } public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { - let dictionary = args as! Dictionary - return QRView(withFrame: CGRect(x: 0, y: 0, width: dictionary["width"] ?? 0, height: dictionary["height"] ?? 0), withRegistrar: registrar!,withId: viewId) + let params = args as! Dictionary + return QRView(withFrame: frame, withRegistrar: registrar!,withId: viewId, params: params) } public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 7a60428..be42b6e 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -7,6 +7,14 @@ import 'package:qr_code_scanner/qr_code_scanner.dart'; typedef QRViewCreatedCallback = void Function(QRViewController); +enum CameraFacing { + /// Shows back facing camera. + back, + + /// Shows front facing camera. + front +} + enum BarcodeFormat { /// Aztec 2D barcode format. aztec, @@ -80,6 +88,11 @@ const _formatNames = { 'UPC_EAN_EXTENSION': BarcodeFormat.upcEanExtension, }; +/// The [Barcode] object holds information about the barcode or qr code. +/// +/// [code] is the content of the barcode. +/// [format] displays which type the code is. +/// Only for Android, [rawBytes] gives a list of bytes of the result. class Barcode { Barcode(this.code, this.format, this.rawBytes); @@ -90,60 +103,84 @@ class Barcode { final List rawBytes; } +/// The [QRView] is the view where the camera and the barcode scanner gets displayed. class QRView extends StatefulWidget { const QRView({ @required Key key, @required this.onQRViewCreated, this.overlay, this.overlayMargin = EdgeInsets.zero, + this.cameraFacing = CameraFacing.back, }) : assert(key != null), assert(onQRViewCreated != null), super(key: key); final QRViewCreatedCallback onQRViewCreated; - final ShapeBorder overlay; final EdgeInsetsGeometry overlayMargin; + final CameraFacing cameraFacing; @override State createState() => _QRViewState(); } class _QRViewState extends State { + + var _channel; + @override Widget build(BuildContext context) { + return NotificationListener( + onNotification: onNotification, + child: SizeChangedLayoutNotifier( + child: (widget.overlay != null) ? + _getPlatformQrViewWithOverlay() : + _getPlatformQrView(), + ), + ); + } + + bool onNotification(notification) { + Future.microtask(() => { + QRViewController.updateDimensions( + widget.key, + _channel, + scanArea: widget.overlay != null ? + (widget.overlay as QrScannerOverlayShape).cutOutSize : 0.0) + }); + return false; + } + + Widget _getPlatformQrViewWithOverlay() { return Stack( children: [ - _getPlatformQrView(widget.key), - if (widget.overlay != null) - Container( - padding: widget.overlayMargin, - decoration: ShapeDecoration( - shape: widget.overlay, - ), - ) - else - Container(), + _getPlatformQrView(), + Container( + padding: widget.overlayMargin, + decoration: ShapeDecoration( + shape: widget.overlay, + ), + ) ], ); } - Widget _getPlatformQrView(GlobalKey key) { + Widget _getPlatformQrView() { Widget _platformQrView; switch (defaultTargetPlatform) { case TargetPlatform.android: _platformQrView = AndroidView( viewType: 'net.touchcapture.qr.flutterqr/qrview', onPlatformViewCreated: _onPlatformViewCreated, + creationParams: _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), + creationParamsCodec: StandardMessageCodec(), ); break; case TargetPlatform.iOS: _platformQrView = UiKitView( viewType: 'net.touchcapture.qr.flutterqr/qrview', onPlatformViewCreated: _onPlatformViewCreated, - creationParams: - _CreationParams.fromWidget(MediaQuery.of(context).size.width, 400) - .toMap(), + creationParams: _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), creationParamsCodec: StandardMessageCodec(), ); break; @@ -155,101 +192,109 @@ class _QRViewState extends State { } void _onPlatformViewCreated(int id) { - if (widget.onQRViewCreated == null) { - return; - } - // We pass the cutout size so that the scanner respects the scan area. var cutOutSize = 0.0; if (widget.overlay != null) { cutOutSize = (widget.overlay as QrScannerOverlayShape).cutOutSize; } - widget.onQRViewCreated(QRViewController._(id, widget.key, cutOutSize)); - } -} + _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id'); -class _CreationParams { - _CreationParams({this.width, this.height}); + // Start scan after creation of the view + final controller = QRViewController._(_channel, widget.key, cutOutSize) + .._startScan(widget.key, cutOutSize); - static _CreationParams fromWidget(double width, double height) { - return _CreationParams( - width: width, - height: height, - ); + // Initialize the controller for controlling the QRView + if (widget.onQRViewCreated != null) { + widget.onQRViewCreated(controller); + } } +} + +class _QrCameraSettings { + _QrCameraSettings({ + this.cameraFacing, + }); - final double width; - final double height; + final CameraFacing cameraFacing; Map toMap() { return { - 'width': width, - 'height': height, + 'cameraFacing': cameraFacing.index, }; } + } class QRViewController { - QRViewController._(int id, GlobalKey qrKey, double scanArea) - : _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') { - updateDimensions(qrKey, scanArea: scanArea); - _channel.setMethodCallHandler( - (call) async { - switch (call.method) { - case scanMethodCall: - if (call.arguments != null) { - final args = call.arguments as Map; - final code = args['code'] as String; - final rawType = args['type'] as String; - // Raw bytes are only supported by Android. - final rawBytes = args['rawBytes'] as List; - final format = _formatNames[rawType]; - if (format != null) { - final barcode = Barcode(code, format, rawBytes); - _scanUpdateController.sink.add(barcode); - } else { - throw Exception('Unexpected barcode type $rawType'); - } - } - } - }, - ); + QRViewController._(MethodChannel channel, GlobalKey qrKey, double scanArea) + : _channel = channel { + _channel.setMethodCallHandler(_onMethodCall); } - static const scanMethodCall = 'onRecognizeQR'; - final MethodChannel _channel; - final StreamController _scanUpdateController = StreamController(); Stream get scannedDataStream => _scanUpdateController.stream; + Future _onMethodCall(MethodCall call) async { + switch (call.method) { + case 'onRecognizeQR': + if (call.arguments != null) { + final args = call.arguments as Map; + final code = args['code'] as String; + final rawType = args['type'] as String; + // Raw bytes are only supported by Android. + final rawBytes = args['rawBytes'] as List; + final format = _formatNames[rawType]; + if (format != null) { + final barcode = Barcode(code, format, rawBytes); + _scanUpdateController.sink.add(barcode); + } else { + throw Exception('Unexpected barcode type $rawType'); + } + } + } + } + + /// Starts the barcode scanner + Future _startScan(GlobalKey key, double cutOutSize, ) async { + // We need to update the dimension before the scan is started. + QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize); + return _channel.invokeMethod('startScan'); + } + + /// Flips the camera between available modes void flipCamera() { _channel.invokeMethod('flipCamera'); } + /// Toggles the flashlight between available modes void toggleFlash() { _channel.invokeMethod('toggleFlash'); } + /// Pauses barcode scanning void pauseCamera() { _channel.invokeMethod('pauseCamera'); } + /// Resumes barcode scanning void resumeCamera() { _channel.invokeMethod('resumeCamera'); } + /// Disposes the barcode stream. void dispose() { _scanUpdateController.close(); } - void updateDimensions(GlobalKey key, {double scanArea}) { + /// Updates the view dimensions for iOS. + static void updateDimensions(GlobalKey key, MethodChannel channel, {double scanArea}) { if (defaultTargetPlatform == TargetPlatform.iOS) { final RenderBox renderBox = key.currentContext.findRenderObject(); - _channel.invokeMethod('setDimensions', { + channel.invokeMethod('setDimensions', { 'width': renderBox.size.width, 'height': renderBox.size.height, 'scanArea': scanArea ?? 0 From 74f41169e30452cccc89721774626edb9cf2371f Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Mon, 28 Dec 2020 19:57:50 +0100 Subject: [PATCH 2/8] dartfrmt --- lib/src/qr_code_scanner.dart | 37 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index be42b6e..e6f3c3b 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -125,7 +125,6 @@ class QRView extends StatefulWidget { } class _QRViewState extends State { - var _channel; @override @@ -133,21 +132,20 @@ class _QRViewState extends State { return NotificationListener( onNotification: onNotification, child: SizeChangedLayoutNotifier( - child: (widget.overlay != null) ? - _getPlatformQrViewWithOverlay() : - _getPlatformQrView(), + child: (widget.overlay != null) + ? _getPlatformQrViewWithOverlay() + : _getPlatformQrView(), ), ); } bool onNotification(notification) { Future.microtask(() => { - QRViewController.updateDimensions( - widget.key, - _channel, - scanArea: widget.overlay != null ? - (widget.overlay as QrScannerOverlayShape).cutOutSize : 0.0) - }); + QRViewController.updateDimensions(widget.key, _channel, + scanArea: widget.overlay != null + ? (widget.overlay as QrScannerOverlayShape).cutOutSize + : 0.0) + }); return false; } @@ -172,7 +170,8 @@ class _QRViewState extends State { _platformQrView = AndroidView( viewType: 'net.touchcapture.qr.flutterqr/qrview', onPlatformViewCreated: _onPlatformViewCreated, - creationParams: _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), + creationParams: + _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), creationParamsCodec: StandardMessageCodec(), ); break; @@ -180,7 +179,8 @@ class _QRViewState extends State { _platformQrView = UiKitView( viewType: 'net.touchcapture.qr.flutterqr/qrview', onPlatformViewCreated: _onPlatformViewCreated, - creationParams: _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), + creationParams: + _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), creationParamsCodec: StandardMessageCodec(), ); break; @@ -202,7 +202,7 @@ class _QRViewState extends State { // Start scan after creation of the view final controller = QRViewController._(_channel, widget.key, cutOutSize) - .._startScan(widget.key, cutOutSize); + .._startScan(widget.key, cutOutSize); // Initialize the controller for controlling the QRView if (widget.onQRViewCreated != null) { @@ -213,7 +213,7 @@ class _QRViewState extends State { class _QrCameraSettings { _QrCameraSettings({ - this.cameraFacing, + this.cameraFacing, }); final CameraFacing cameraFacing; @@ -223,7 +223,6 @@ class _QrCameraSettings { 'cameraFacing': cameraFacing.index, }; } - } class QRViewController { @@ -259,7 +258,10 @@ class QRViewController { } /// Starts the barcode scanner - Future _startScan(GlobalKey key, double cutOutSize, ) async { + Future _startScan( + GlobalKey key, + double cutOutSize, + ) async { // We need to update the dimension before the scan is started. QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize); return _channel.invokeMethod('startScan'); @@ -291,7 +293,8 @@ class QRViewController { } /// Updates the view dimensions for iOS. - static void updateDimensions(GlobalKey key, MethodChannel channel, {double scanArea}) { + static void updateDimensions(GlobalKey key, MethodChannel channel, + {double scanArea}) { if (defaultTargetPlatform == TargetPlatform.iOS) { final RenderBox renderBox = key.currentContext.findRenderObject(); channel.invokeMethod('setDimensions', { From d1ed2052498c96e940acd0f79d1d22e6c2d474fb Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Wed, 30 Dec 2020 16:03:01 +0100 Subject: [PATCH 3/8] Added future to functions. Changed min SDK to 2.6.0. --- .../qr/flutterqr/FlutterQrPlugin.kt | 2 +- .../net/touchcapture/qr/flutterqr/QRView.kt | 209 +++++++++---- .../net/touchcapture/qr/flutterqr/Shared.kt | 1 - example/lib/main.dart | 77 ++--- ios/Classes/QRView.swift | 120 +++++++- lib/qr_code_scanner.dart | 3 + lib/src/qr_code_scanner.dart | 280 ++++++++++-------- lib/src/types/barcode.dart | 16 + lib/src/types/barcode_format.dart | 121 ++++++++ lib/src/types/camera.dart | 7 + lib/src/types/camera_exception.dart | 14 + lib/src/types/features.dart | 13 + pubspec.yaml | 2 +- 13 files changed, 615 insertions(+), 250 deletions(-) create mode 100644 lib/src/types/barcode.dart create mode 100644 lib/src/types/barcode_format.dart create mode 100644 lib/src/types/camera.dart create mode 100644 lib/src/types/camera_exception.dart create mode 100644 lib/src/types/features.dart diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt index 53df56e..4210815 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt @@ -7,6 +7,7 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.PluginRegistry import io.flutter.plugin.platform.PlatformViewRegistry @@ -63,7 +64,6 @@ class FlutterQrPlugin : FlutterPlugin, ActivityAware { inner class CameraRequestPermissionsListener : PluginRegistry.RequestPermissionsResultListener { override fun onRequestPermissionsResult(id: Int, permissions: Array, grantResults: IntArray): Boolean { if (id == Shared.CAMERA_REQUEST_ID && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Shared.cameraPermissionContinuation?.run() return true } return false diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt index 945cb37..5d01244 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt @@ -10,6 +10,7 @@ import android.view.View import com.google.zxing.ResultPoint import android.hardware.Camera.CameraInfo import android.os.Build +import com.google.zxing.BarcodeFormat import com.journeyapps.barcodescanner.BarcodeCallback import com.journeyapps.barcodescanner.BarcodeResult import com.journeyapps.barcodescanner.BarcodeView @@ -22,8 +23,22 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, private var isTorchOn: Boolean = false private var barcodeView: BarcodeView? = null - private var requestingPermission = false private val channel: MethodChannel + var allowedBarcodeTypes = mutableListOf() + + private val qrCodeTypes = mapOf( + 0 to BarcodeFormat.AZTEC, + 1 to BarcodeFormat.CODE_128, + 2 to BarcodeFormat.CODE_39, + 3 to BarcodeFormat.CODE_93, + 4 to BarcodeFormat.DATA_MATRIX, + 5 to BarcodeFormat.EAN_13, + 6 to BarcodeFormat.EAN_8, + 7 to BarcodeFormat.ITF, + 8 to BarcodeFormat.PDF_417, + 9 to BarcodeFormat.QR_CODE, + 10 to BarcodeFormat.UPC_E + ) init { checkAndRequestPermission(null) @@ -65,68 +80,121 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - when(call.method){ - "startScan" -> { - startScan() - } - "stopScan" -> { - stopScan() - } - "flipCamera" -> { - flipCamera() - } - "toggleFlash" -> { - toggleFlash() - } - "pauseCamera" -> { - pauseCamera() - } - "resumeCamera" -> { - resumeCamera() - } + when(call.method) { + "startScan" -> startScan() + "stopScan" -> stopScan() + "flipCamera" -> flipCamera(result) + "toggleFlash" -> toggleFlash(result) + "pauseCamera" -> pauseCamera(result) + "resumeCamera" -> resumeCamera(result) + "requestPermissions" -> checkAndRequestPermission(result) + "getCameraInfo" -> getCameraInfo(result) + "getFlashInfo" -> getFlashInfo(result) +// "showNativeAlertDialog" -> showNativeAlertDialog(result) + "getSystemFeatures" -> getSystemFeatures(result) + "setAllowedBarcodeFormats" -> setBarcodeFormats(call.arguments as List, result) + else -> result.notImplemented() } } - private fun flipCamera() { - barcodeView?.pause() - val settings = barcodeView?.cameraSettings + private fun getCameraInfo(result: MethodChannel.Result) { + if (barcodeView == null) { + return barCodeViewNotSet(result) + } + result.success(barcodeView!!.cameraSettings.requestedCameraId) + } + + private fun flipCamera(result: MethodChannel.Result) { + if (barcodeView == null) { + return barCodeViewNotSet(result) + } + barcodeView!!.pause() + val settings = barcodeView!!.cameraSettings - if(settings?.requestedCameraId == CameraInfo.CAMERA_FACING_FRONT) + if(settings.requestedCameraId == CameraInfo.CAMERA_FACING_FRONT) settings.requestedCameraId = CameraInfo.CAMERA_FACING_BACK else - settings?.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT + settings.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT - barcodeView?.cameraSettings = settings - barcodeView?.resume() + barcodeView!!.cameraSettings = settings + barcodeView!!.resume() + result.success(settings.requestedCameraId) } - private fun toggleFlash() { + private fun getFlashInfo(result: MethodChannel.Result) { + if (barcodeView == null) { + return barCodeViewNotSet(result) + } + result.success(isTorchOn) + } + + private fun toggleFlash(result: MethodChannel.Result) { + if (barcodeView == null) { + return barCodeViewNotSet(result) + } + if (hasFlash()) { - barcodeView?.setTorch(!isTorchOn) + barcodeView!!.setTorch(!isTorchOn) isTorchOn = !isTorchOn + result.success(isTorchOn) + } else { + result.error("404", "This device doesn't support flash", null) } } - private fun pauseCamera() { + private fun pauseCamera(result: MethodChannel.Result) { + if (barcodeView == null) { + return barCodeViewNotSet(result) + } if (barcodeView!!.isPreviewActive) { - barcodeView?.pause() + barcodeView!!.pause() } + result.success(true) } - private fun resumeCamera() { + private fun resumeCamera(result: MethodChannel.Result) { + if (barcodeView == null) { + return barCodeViewNotSet(result) + } if (!barcodeView!!.isPreviewActive) { - barcodeView?.resume() + barcodeView!!.resume() } + result.success(true) } private fun hasFlash(): Boolean { - return context.packageManager - .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) + return hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) + } + + private fun hasBackCamera(): Boolean { + return hasSystemFeature(PackageManager.FEATURE_CAMERA) + } + + private fun hasFrontCamera(): Boolean { + return hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) + } + + private fun hasSystemFeature(feature: String): Boolean { + return Shared.activity!!.packageManager + .hasSystemFeature(feature) + } + + private fun barCodeViewNotSet(result: MethodChannel.Result) { + result.error("404", "No barcode view found", null) } override fun getView(): View { return initBarCodeView()?.apply { + if (!hasBackCamera()) { + if (!hasFrontCamera()) { + // No camera available! + } else { + this.cameraSettings.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT + } + } else { + this.cameraSettings.requestedCameraId = CameraInfo.CAMERA_FACING_BACK + } resume() }!! } @@ -145,11 +213,14 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, barcodeView?.decodeContinuous( object : BarcodeCallback { override fun barcodeResult(result: BarcodeResult) { - val code = mapOf( - "code" to result.text, - "type" to result.barcodeFormat.name, - "rawBytes" to result.rawBytes) - channel.invokeMethod("onRecognizeQR", code) + if (allowedBarcodeTypes.size == 0 || allowedBarcodeTypes.contains(result.barcodeFormat)) { + val code = mapOf( + "code" to result.text, + "type" to result.barcodeFormat.name, + "rawBytes" to result.rawBytes) + channel.invokeMethod("onRecognizeQR", code) + } + } override fun possibleResultPoints(resultPoints: List) {} @@ -161,35 +232,55 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, barcodeView?.stopDecoding() } + private fun getSystemFeatures(result: MethodChannel.Result) { + try { + result.success(mapOf("hasFrontCamera" to hasFrontCamera(), + "hasBackCamera" to hasBackCamera(), "hasFlash" to hasFlash(), + "activeCamera" to barcodeView?.cameraSettings?.requestedCameraId)) + } catch (e: Exception) { + result.error(null, null, null) + } + } + + private fun setBarcodeFormats(arguments: List, result: MethodChannel.Result) { + try { + allowedBarcodeTypes.clear() + arguments.forEach { + allowedBarcodeTypes.add(qrCodeTypes[it]!!) + } + result.success(true) + } catch (e: java.lang.Exception) { + result.error(null, null, null) + } + } + +// private fun showNativeAlertDialog(result: MethodChannel.Result) { +// AlertDialog.Builder(context) +// .setTitle("Scanning Unavailable") +// .setMessage("This app does not have permission to access the camera") +// .setPositiveButton(R.string.ok, null) +// .setCancelable(false) +// .setIcon(R.drawable.ic_dialog_alert) +// .show() +// result.success(true) +// } + private fun hasCameraPermission(): Boolean { return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Shared.activity?.checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED } private fun checkAndRequestPermission(result: MethodChannel.Result?) { - if (Shared.cameraPermissionContinuation != null) { - result?.error("cameraPermission", "Camera permission request ongoing", null) - } - - Shared.cameraPermissionContinuation = Runnable { - Shared.cameraPermissionContinuation = null - if (!hasCameraPermission()) { - result?.error( - "cameraPermission", "MediaRecorderCamera permission not granted", null) - return@Runnable - } - } - - requestingPermission = false - if (hasCameraPermission()) { - Shared.cameraPermissionContinuation?.run() - } else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - requestingPermission = true + when { + hasCameraPermission() -> result?.success(true) + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { Shared.activity?.requestPermissions( arrayOf(Manifest.permission.CAMERA), Shared.CAMERA_REQUEST_ID) } + else -> { + result?.error("cameraPermission", "Platform Version to low for camera permission check", null) + } } } } diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt index 24bc4ab..a5a663d 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt @@ -5,5 +5,4 @@ import android.app.Activity object Shared { const val CAMERA_REQUEST_ID = 513469796 var activity: Activity? = null - var cameraPermissionContinuation: Runnable? = null } \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 72fdd08..719d582 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,11 +6,6 @@ import 'package:qr_code_scanner/qr_code_scanner.dart'; void main() => runApp(MaterialApp(home: QRViewExample())); -const flashOn = 'FLASH ON'; -const flashOff = 'FLASH OFF'; -const frontCamera = 'FRONT CAMERA'; -const backCamera = 'BACK CAMERA'; - class QRViewExample extends StatefulWidget { const QRViewExample({ Key key, @@ -21,9 +16,8 @@ class QRViewExample extends StatefulWidget { } class _QRViewExampleState extends State { + Barcode result; - var flashState = flashOn; - var cameraState = frontCamera; QRViewController controller; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); @@ -64,44 +58,33 @@ class _QRViewExampleState extends State { Container( margin: EdgeInsets.all(8), child: RaisedButton( - onPressed: () { - if (controller != null) { - controller.toggleFlash(); - if (_isFlashOn(flashState)) { - setState(() { - flashState = flashOff; - }); - } else { - setState(() { - flashState = flashOn; - }); - } - } - }, - child: - Text(flashState, style: TextStyle(fontSize: 20)), - ), + onPressed: () => setState(() { + controller?.toggleFlash(); + }), + child: FutureBuilder( + future: controller?.getFlashStatus(), + builder: (context, snapshot) { + return Text('Flash: ${snapshot.data}'); + }, + )), ), Container( margin: EdgeInsets.all(8), child: RaisedButton( - onPressed: () { - if (controller != null) { - controller.flipCamera(); - if (_isBackCamera(cameraState)) { - setState(() { - cameraState = frontCamera; - }); - } else { - setState(() { - cameraState = backCamera; - }); - } - } - }, - child: - Text(cameraState, style: TextStyle(fontSize: 20)), - ), + onPressed: () => setState(() { + controller?.flipCamera(); + }), + child: FutureBuilder( + future: controller?.getCameraInfo(), + builder: (context, snapshot) { + if (snapshot.data != null) { + return Text( + 'Camera facing ${describeEnum(snapshot.data)}'); + } else { + return Text('loading'); + } + }, + )), ) ], ), @@ -138,14 +121,6 @@ class _QRViewExampleState extends State { ); } - bool _isFlashOn(String current) { - return flashOn == current; - } - - bool _isBackCamera(String current) { - return backCamera == current; - } - Widget _buildQrView(BuildContext context) { // For this example we check how width or tall the device is and change the scanArea and overlay accordingly. var scanArea = (MediaQuery.of(context).size.width < 400 || @@ -169,7 +144,9 @@ class _QRViewExampleState extends State { } void _onQRViewCreated(QRViewController controller) { - this.controller = controller; + setState(() { + this.controller = controller; + }); controller.scannedDataStream.listen((scanData) { setState(() { result = scanData; diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index 3e36441..2e72929 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -15,6 +15,22 @@ public class QRView:NSObject,FlutterPlatformView { var channel: FlutterMethodChannel var cameraFacing: MTBCamera + var allowedBarcodeTypes: Array = [] + + var QRCodeTypes = [ + 0: AVMetadataObject.ObjectType.aztec, + 1: AVMetadataObject.ObjectType.code128, + 2: AVMetadataObject.ObjectType.code39, + 3: AVMetadataObject.ObjectType.code93, + 4: AVMetadataObject.ObjectType.dataMatrix, + 5: AVMetadataObject.ObjectType.ean13, + 6: AVMetadataObject.ObjectType.ean8, + 7: AVMetadataObject.ObjectType.interleaved2of5, + 8: AVMetadataObject.ObjectType.pdf417, + 9: AVMetadataObject.ObjectType.qr, + 10: AVMetadataObject.ObjectType.upce + ] + public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary){ self.registrar = registrar previewView = UIView(frame: frame) @@ -36,13 +52,23 @@ public class QRView:NSObject,FlutterPlatformView { case "startScan": self?.startScan(result) case "flipCamera": - self?.flipCamera() + self?.flipCamera(result) case "toggleFlash": - self?.toggleFlash() + self?.toggleFlash(result) case "pauseCamera": - self?.pauseCamera() + self?.pauseCamera(result) case "resumeCamera": - self?.resumeCamera() + self?.resumeCamera(result) + case "getCameraInfo": + self?.getCameraInfo(result) + case "getFlashInfo": + self?.getFlashInfo(result) + case "showNativeAlertDialog": + self?.showNativeAlertDialog(result) + case "getSystemFeatures": + self?.getSystemFeatures(result) + case "setAllowedBarcodeFormats": + self?.setBarcodeFormats(call.arguments as! Array, result) default: result(FlutterMethodNotImplemented) return @@ -108,7 +134,10 @@ public class QRView:NSObject,FlutterPlatformView { } guard let stringValue = code.stringValue else { continue } let result = ["code": stringValue, "type": typeString] - self?.channel.invokeMethod("onRecognizeQR", arguments: result) + if self!.allowedBarcodeTypes.count == 0 || self!.allowedBarcodeTypes.contains(code.type) { + self?.channel.invokeMethod("onRecognizeQR", arguments: result) + } + } } }) @@ -131,35 +160,106 @@ public class QRView:NSObject,FlutterPlatformView { } } - func flipCamera(){ + func getCameraInfo(_ result: @escaping FlutterResult) -> Void { + if let sc: MTBBarcodeScanner = scanner { + result(sc.camera.rawValue) + } else { + let error = FlutterError(code: "cameraInformationError", message: "Could not get camera information", details: nil) + result(error) + } + } + + func flipCamera(_ result: @escaping FlutterResult){ if let sc: MTBBarcodeScanner = scanner { if sc.hasOppositeCamera() { sc.flipCamera() } + return result(sc.camera.rawValue) } + return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) } - func toggleFlash(){ + func getFlashInfo(_ result: @escaping FlutterResult) -> Void { + if let sc: MTBBarcodeScanner = scanner { + result(sc.torchMode.rawValue != 0) + } else { + let error = FlutterError(code: "cameraInformationError", message: "Could not get flash information", details: nil) + result(error) + } + } + + func toggleFlash(_ result: @escaping FlutterResult){ if let sc: MTBBarcodeScanner = scanner { if sc.hasTorch() { sc.toggleTorch() + return result(sc.torchMode == MTBTorchMode(rawValue: 1)) } + return result(FlutterError(code: "404", message: "This device doesn\'t support flash", details: nil)) } + return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) } - func pauseCamera() { + func pauseCamera(_ result: @escaping FlutterResult) { if let sc: MTBBarcodeScanner = scanner { if sc.isScanning() { sc.freezeCapture() } + return result(true) } + return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) } - func resumeCamera() { + func resumeCamera(_ result: @escaping FlutterResult) { if let sc: MTBBarcodeScanner = scanner { if !sc.isScanning() { sc.unfreezeCapture() } + return result(true) + } + return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) + } + + func showNativeAlertDialog(_ result: @escaping FlutterResult) -> Void { + UIAlertView(title: "Scanning Unavailable", message: "This app does not have permission to access the camera", delegate: nil, cancelButtonTitle: nil, otherButtonTitles: "Ok").show() + return result(true) + } + + func getSystemFeatures(_ result: @escaping FlutterResult) -> Void { + if let sc: MTBBarcodeScanner = scanner { + var hasBackCameraVar = false + var hasFrontCameraVar = false + let camera = sc.camera + + if(camera == MTBCamera(rawValue: 0)){ + hasBackCameraVar = true + if sc.hasOppositeCamera() { + hasFrontCameraVar = true + } + }else{ + hasFrontCameraVar = true + if sc.hasOppositeCamera() { + hasBackCameraVar = true + } + } + return result([ + "hasFrontCamera": hasFrontCameraVar, + "hasBackCamera": hasBackCameraVar, + "hasFlash": sc.hasTorch(), + "activeCamera": camera.rawValue + ]) + } + return result(FlutterError(code: "404", message: nil, details: nil)) + } + + func setBarcodeFormats(_ arguments: Array, _ result: @escaping FlutterResult){ + do{ + allowedBarcodeTypes.removeAll() + try arguments.forEach { arg in + allowedBarcodeTypes.append(try QRCodeTypes[arg]!) + } + result(true) + }catch{ + result(FlutterError(code: "404", message: nil, details: nil)) } } -} + } diff --git a/lib/qr_code_scanner.dart b/lib/qr_code_scanner.dart index bc2f4d9..9df6d69 100644 --- a/lib/qr_code_scanner.dart +++ b/lib/qr_code_scanner.dart @@ -1,2 +1,5 @@ export 'src/qr_code_scanner.dart'; export 'src/qr_scanner_overlay_shape.dart'; +export 'src/types/barcode.dart'; +export 'src/types/camera.dart'; +export 'src/types/features.dart'; diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index e6f3c3b..427dffc 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -3,105 +3,16 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart'; -typedef QRViewCreatedCallback = void Function(QRViewController); - -enum CameraFacing { - /// Shows back facing camera. - back, - - /// Shows front facing camera. - front -} - -enum BarcodeFormat { - /// Aztec 2D barcode format. - aztec, - - /// CODABAR 1D format. - codabar, - - /// Code 39 1D format. - code39, - - /// Code 93 1D format. - code93, - - /// Code 128 1D format. - code128, - - /// Data Matrix 2D barcode format. - dataMatrix, - - /// EAN-8 1D format. - ean8, - - /// EAN-13 1D format. - ean13, - - /// ITF (Interleaved Two of Five) 1D format. - itf, - - /// MaxiCode 2D barcode format. - maxicode, - - /// PDF417 format. - pdf417, - - /// QR Code 2D barcode format. - qrcode, - - /// RSS 14 - rss14, - - /// RSS EXPANDED - rssExpanded, - - /// UPC-A 1D format. - upcA, - - /// UPC-E 1D format. - upcE, - - /// UPC/EAN extension format. Not a stand-alone format. - upcEanExtension -} - -const _formatNames = { - 'AZTEC': BarcodeFormat.aztec, - 'CODABAR': BarcodeFormat.codabar, - 'CODE_39': BarcodeFormat.code39, - 'CODE_93': BarcodeFormat.code93, - 'CODE_128': BarcodeFormat.code128, - 'DATA_MATRIX': BarcodeFormat.dataMatrix, - 'EAN_8': BarcodeFormat.ean8, - 'EAN_13': BarcodeFormat.ean13, - 'ITF': BarcodeFormat.itf, - 'MAXICODE': BarcodeFormat.maxicode, - 'PDF_417': BarcodeFormat.pdf417, - 'QR_CODE': BarcodeFormat.qrcode, - 'RSS_14': BarcodeFormat.rss14, - 'RSS_EXPANDED': BarcodeFormat.rssExpanded, - 'UPC_A': BarcodeFormat.upcA, - 'UPC_E': BarcodeFormat.upcE, - 'UPC_EAN_EXTENSION': BarcodeFormat.upcEanExtension, -}; - -/// The [Barcode] object holds information about the barcode or qr code. -/// -/// [code] is the content of the barcode. -/// [format] displays which type the code is. -/// Only for Android, [rawBytes] gives a list of bytes of the result. -class Barcode { - Barcode(this.code, this.format, this.rawBytes); - - final String code; - final BarcodeFormat format; +import 'qr_scanner_overlay_shape.dart'; +import 'types/barcode.dart'; +import 'types/barcode_format.dart'; +import 'types/camera.dart'; +import 'types/camera_exception.dart'; +import 'types/features.dart'; - /// Raw bytes are only supported by Android. - final List rawBytes; -} +typedef QRViewCreatedCallback = void Function(QRViewController); +typedef PermissionSetCallback = void Function(QRViewController, bool); /// The [QRView] is the view where the camera and the barcode scanner gets displayed. class QRView extends StatefulWidget { @@ -111,6 +22,8 @@ class QRView extends StatefulWidget { this.overlay, this.overlayMargin = EdgeInsets.zero, this.cameraFacing = CameraFacing.back, + this.onPermissionSet, + this.showNativeAlertDialog = false, }) : assert(key != null), assert(onQRViewCreated != null), super(key: key); @@ -119,6 +32,8 @@ class QRView extends StatefulWidget { final ShapeBorder overlay; final EdgeInsetsGeometry overlayMargin; final CameraFacing cameraFacing; + final PermissionSetCallback onPermissionSet; + final bool showNativeAlertDialog; @override State createState() => _QRViewState(); @@ -201,7 +116,8 @@ class _QRViewState extends State { _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id'); // Start scan after creation of the view - final controller = QRViewController._(_channel, widget.key, cutOutSize) + final controller = QRViewController._(_channel, widget.key, cutOutSize, + widget.onPermissionSet, widget.showNativeAlertDialog) .._startScan(widget.key, cutOutSize); // Initialize the controller for controlling the QRView @@ -225,10 +141,70 @@ class _QrCameraSettings { } } +const _formatNames = { + 'AZTEC': BarcodeFormat.aztec, + 'CODABAR': BarcodeFormat.codabar, + 'CODE_39': BarcodeFormat.code39, + 'CODE_93': BarcodeFormat.code93, + 'CODE_128': BarcodeFormat.code128, + 'DATA_MATRIX': BarcodeFormat.dataMatrix, + 'EAN_8': BarcodeFormat.ean8, + 'EAN_13': BarcodeFormat.ean13, + 'ITF': BarcodeFormat.itf, + 'MAXICODE': BarcodeFormat.maxicode, + 'PDF_417': BarcodeFormat.pdf417, + 'QR_CODE': BarcodeFormat.qrcode, + 'RSS_14': BarcodeFormat.rss14, + 'RSS_EXPANDED': BarcodeFormat.rssExpanded, + 'UPC_A': BarcodeFormat.upcA, + 'UPC_E': BarcodeFormat.upcE, + 'UPC_EAN_EXTENSION': BarcodeFormat.upcEanExtension, +}; + class QRViewController { - QRViewController._(MethodChannel channel, GlobalKey qrKey, double scanArea) - : _channel = channel { - _channel.setMethodCallHandler(_onMethodCall); + QRViewController._( + MethodChannel channel, + GlobalKey qrKey, + double scanArea, + PermissionSetCallback onPermissionSet, + bool showNativeAlertDialogOnError, + ) : _channel = channel { + _channel.setMethodCallHandler((call) async { + switch (call.method) { + case 'onRecognizeQR': + if (call.arguments != null) { + final args = call.arguments as Map; + final code = args['code'] as String; + final rawType = args['type'] as String; + // Raw bytes are only supported by Android. + final rawBytes = args['rawBytes'] as List; + final format = _formatNames[rawType]; + if (format != null) { + final barcode = Barcode(code, format, rawBytes); + _scanUpdateController.sink.add(barcode); + } else { + throw Exception('Unexpected barcode type $rawType'); + } + } + break; + case 'onPermissionSet': + await getSystemFeatures(); // if we have no permission all features will not be avaible + if (call.arguments != null) { + if (call.arguments as bool) { + _hasPermissions = true; + } else { + _hasPermissions = false; + if (showNativeAlertDialogOnError) { + await showNativeAlertDialog(); + } + } + if (onPermissionSet != null) { + onPermissionSet(this, call.arguments as bool); + } + } + break; + } + }); } final MethodChannel _channel; @@ -237,25 +213,11 @@ class QRViewController { Stream get scannedDataStream => _scanUpdateController.stream; - Future _onMethodCall(MethodCall call) async { - switch (call.method) { - case 'onRecognizeQR': - if (call.arguments != null) { - final args = call.arguments as Map; - final code = args['code'] as String; - final rawType = args['type'] as String; - // Raw bytes are only supported by Android. - final rawBytes = args['rawBytes'] as List; - final format = _formatNames[rawType]; - if (format != null) { - final barcode = Barcode(code, format, rawBytes); - _scanUpdateController.sink.add(barcode); - } else { - throw Exception('Unexpected barcode type $rawType'); - } - } - } - } + SystemFeatures _features; + bool _hasPermissions; + + SystemFeatures get systemFeatures => _features; + bool get hasPermissions => _hasPermissions; /// Starts the barcode scanner Future _startScan( @@ -267,24 +229,86 @@ class QRViewController { return _channel.invokeMethod('startScan'); } + Future getCameraInfo() async { + try { + return CameraFacing + .values[await _channel.invokeMethod('getCameraInfo') as int]; + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + /// Flips the camera between available modes - void flipCamera() { - _channel.invokeMethod('flipCamera'); + Future flipCamera() async { + try { + return CameraFacing + .values[await _channel.invokeMethod('flipCamera') as int]; + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + /// Get flashlight status + Future getFlashStatus() async { + try { + return await _channel.invokeMethod('getFlashInfo'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } } /// Toggles the flashlight between available modes - void toggleFlash() { - _channel.invokeMethod('toggleFlash'); + Future toggleFlash() async { + try { + await _channel.invokeMethod('toggleFlash') as bool; + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } } /// Pauses barcode scanning - void pauseCamera() { - _channel.invokeMethod('pauseCamera'); + Future pauseCamera() async { + try { + await _channel.invokeMethod('pauseCamera'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } } /// Resumes barcode scanning - void resumeCamera() { - _channel.invokeMethod('resumeCamera'); + Future resumeCamera() async { + try { + await _channel.invokeMethod('resumeCamera'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + Future showNativeAlertDialog() async { + try { + await _channel.invokeMethod('showNativeAlertDialog'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + Future setAllowedBarcodeTypes(List list) async { + try { + await _channel.invokeMethod('setAllowedBarcodeFormats', + list?.map((e) => e.asInt())?.toList() ?? []); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + Future getSystemFeatures() async { + try { + var features = + await _channel.invokeMapMethod('getSystemFeatures'); + return SystemFeatures.fromJson(features); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } } /// Disposes the barcode stream. diff --git a/lib/src/types/barcode.dart b/lib/src/types/barcode.dart new file mode 100644 index 0000000..bf2f101 --- /dev/null +++ b/lib/src/types/barcode.dart @@ -0,0 +1,16 @@ +import 'barcode_format.dart'; + +/// The [Barcode] object holds information about the barcode or qr code. +/// +/// [code] is the content of the barcode. +/// [format] displays which type the code is. +/// Only for Android, [rawBytes] gives a list of bytes of the result. +class Barcode { + Barcode(this.code, this.format, this.rawBytes); + + final String code; + final BarcodeFormat format; + + /// Raw bytes are only supported by Android. + final List rawBytes; +} \ No newline at end of file diff --git a/lib/src/types/barcode_format.dart b/lib/src/types/barcode_format.dart new file mode 100644 index 0000000..8ae0716 --- /dev/null +++ b/lib/src/types/barcode_format.dart @@ -0,0 +1,121 @@ +enum BarcodeFormat { + /// Aztec 2D barcode format. + aztec, + + /// CODABAR 1D format. + codabar, + + /// Code 39 1D format. + code39, + + /// Code 93 1D format. + code93, + + /// Code 128 1D format. + code128, + + /// Data Matrix 2D barcode format. + dataMatrix, + + /// EAN-8 1D format. + ean8, + + /// EAN-13 1D format. + ean13, + + /// ITF (Interleaved Two of Five) 1D format. + itf, + + /// MaxiCode 2D barcode format. + maxicode, + + /// PDF417 format. + pdf417, + + /// QR Code 2D barcode format. + qrcode, + + /// RSS 14 + rss14, + + /// RSS EXPANDED + rssExpanded, + + /// UPC-A 1D format. + upcA, + + /// UPC-E 1D format. + upcE, + + /// UPC/EAN extension format. Not a stand-alone format. + upcEanExtension +} + +extension BarcodeTypesExtension on BarcodeFormat { + int asInt() { + return index; + } + + BarcodeFormat fromString(String format) { + + } + + String get formatName { + switch (this) { + + case BarcodeFormat.aztec: + return 'AZTEC'; + break; + case BarcodeFormat.codabar: + return 'CODABAR'; + break; + case BarcodeFormat.code39: + return 'CODE_39'; + break; + case BarcodeFormat.code93: + return 'CODE_93'; + break; + case BarcodeFormat.code128: + return 'CODE_128'; + break; + case BarcodeFormat.dataMatrix: + return 'DATA_MATRIX'; + break; + case BarcodeFormat.ean8: + return 'EAN_8'; + break; + case BarcodeFormat.ean13: + return 'EAN_13'; + break; + case BarcodeFormat.itf: + return 'ITF'; + break; + case BarcodeFormat.maxicode: + return 'MAXICODE'; + break; + case BarcodeFormat.pdf417: + return 'PDF_417'; + break; + case BarcodeFormat.qrcode: + return 'QR_CODE'; + break; + case BarcodeFormat.rss14: + return 'RSS14'; + break; + case BarcodeFormat.rssExpanded: + return 'RSS_EXPANDED'; + break; + case BarcodeFormat.upcA: + return 'UPC_A'; + break; + case BarcodeFormat.upcE: + return 'UPC_E'; + break; + case BarcodeFormat.upcEanExtension: + return 'UPC_EAN_EXTENSION'; + break; + } + return 'NOT_VALID'; + } + +} \ No newline at end of file diff --git a/lib/src/types/camera.dart b/lib/src/types/camera.dart new file mode 100644 index 0000000..6abbef5 --- /dev/null +++ b/lib/src/types/camera.dart @@ -0,0 +1,7 @@ +enum CameraFacing { + /// Shows back facing camera. + back, + + /// Shows front facing camera. + front +} \ No newline at end of file diff --git a/lib/src/types/camera_exception.dart b/lib/src/types/camera_exception.dart new file mode 100644 index 0000000..84a7881 --- /dev/null +++ b/lib/src/types/camera_exception.dart @@ -0,0 +1,14 @@ +/// This is thrown when the plugin reports an error. +class CameraException implements Exception { + /// Creates a new camera exception with the given error code and description. + CameraException(this.code, this.description); + + /// Error code. + String code; + + /// Textual description of the error. + String description; + + @override + String toString() => 'CameraException($code, $description)'; +} \ No newline at end of file diff --git a/lib/src/types/features.dart b/lib/src/types/features.dart new file mode 100644 index 0000000..ebe92d5 --- /dev/null +++ b/lib/src/types/features.dart @@ -0,0 +1,13 @@ +class SystemFeatures { + SystemFeatures(this.hasFlash, this.hasBackCamera, this.hasFrontCamera); + + factory SystemFeatures.fromJson(Map features) => + SystemFeatures( + features['hasFlash'] ?? false, + features['hasBackCamera'] ?? false, + features['hasFrontCamera'] ?? false); + + final bool hasFlash; + final bool hasFrontCamera; + final bool hasBackCamera; +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 1fe733a..88f840a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://juliuscanute.com repository: https://github.com/juliuscanute/qr_code_scanner environment: - sdk: ">=2.3.0 <3.0.0" + sdk: ">=2.6.0 <3.0.0" flutter: ^1.10.0 dependencies: From d10cc603bfdd577ff88a7930c2185f4a4bf6059c Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Wed, 30 Dec 2020 16:06:44 +0100 Subject: [PATCH 4/8] Added possibility to choose facing camera on start. Moved the size listener to plugin side. --- example/lib/main.dart | 1 - lib/src/types/barcode.dart | 2 +- lib/src/types/barcode_format.dart | 8 ++------ lib/src/types/camera.dart | 2 +- lib/src/types/camera_exception.dart | 2 +- lib/src/types/features.dart | 2 +- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 719d582..20b42ec 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -16,7 +16,6 @@ class QRViewExample extends StatefulWidget { } class _QRViewExampleState extends State { - Barcode result; QRViewController controller; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); diff --git a/lib/src/types/barcode.dart b/lib/src/types/barcode.dart index bf2f101..9bfffdb 100644 --- a/lib/src/types/barcode.dart +++ b/lib/src/types/barcode.dart @@ -13,4 +13,4 @@ class Barcode { /// Raw bytes are only supported by Android. final List rawBytes; -} \ No newline at end of file +} diff --git a/lib/src/types/barcode_format.dart b/lib/src/types/barcode_format.dart index 8ae0716..39a2a5e 100644 --- a/lib/src/types/barcode_format.dart +++ b/lib/src/types/barcode_format.dart @@ -56,13 +56,10 @@ extension BarcodeTypesExtension on BarcodeFormat { return index; } - BarcodeFormat fromString(String format) { - - } + BarcodeFormat fromString(String format) {} String get formatName { switch (this) { - case BarcodeFormat.aztec: return 'AZTEC'; break; @@ -117,5 +114,4 @@ extension BarcodeTypesExtension on BarcodeFormat { } return 'NOT_VALID'; } - -} \ No newline at end of file +} diff --git a/lib/src/types/camera.dart b/lib/src/types/camera.dart index 6abbef5..532f3b4 100644 --- a/lib/src/types/camera.dart +++ b/lib/src/types/camera.dart @@ -4,4 +4,4 @@ enum CameraFacing { /// Shows front facing camera. front -} \ No newline at end of file +} diff --git a/lib/src/types/camera_exception.dart b/lib/src/types/camera_exception.dart index 84a7881..740f4b6 100644 --- a/lib/src/types/camera_exception.dart +++ b/lib/src/types/camera_exception.dart @@ -11,4 +11,4 @@ class CameraException implements Exception { @override String toString() => 'CameraException($code, $description)'; -} \ No newline at end of file +} diff --git a/lib/src/types/features.dart b/lib/src/types/features.dart index ebe92d5..0c3618a 100644 --- a/lib/src/types/features.dart +++ b/lib/src/types/features.dart @@ -10,4 +10,4 @@ class SystemFeatures { final bool hasFlash; final bool hasFrontCamera; final bool hasBackCamera; -} \ No newline at end of file +} From 5a3771a5085a6e8b24f1b63b77c3d31261f35045 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Tue, 5 Jan 2021 14:10:25 +0100 Subject: [PATCH 5/8] Added documentation. Added string to barcode format function. --- lib/src/qr_code_scanner.dart | 40 +++++++++----------- lib/src/types/barcode_format.dart | 61 ++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 24 deletions(-) diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 427dffc..f41c857 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -14,7 +14,8 @@ import 'types/features.dart'; typedef QRViewCreatedCallback = void Function(QRViewController); typedef PermissionSetCallback = void Function(QRViewController, bool); -/// The [QRView] is the view where the camera and the barcode scanner gets displayed. +/// The [QRView] is the view where the camera +/// and the barcode scanner gets displayed. class QRView extends StatefulWidget { const QRView({ @required Key key, @@ -28,11 +29,26 @@ class QRView extends StatefulWidget { assert(onQRViewCreated != null), super(key: key); + /// [onQRViewCreated] gets called when the view is created final QRViewCreatedCallback onQRViewCreated; + + /// Use [overlay] to provide an overlay for the view. + /// This can be used to create a certain scan area. final ShapeBorder overlay; + + /// Use [overlayMargin] to provide a margin to [overlay] final EdgeInsetsGeometry overlayMargin; + + /// Set which camera to use on startup. + /// + /// [cameraFacing] can either be CameraFacing.front or CameraFacing.back. + /// Defaults to CameraFacing.back final CameraFacing cameraFacing; + + /// Calls the provided [onPermissionSet] callback when the permission is set. final PermissionSetCallback onPermissionSet; + + /// Gives the possibility to show a dialog. final bool showNativeAlertDialog; @override @@ -141,26 +157,6 @@ class _QrCameraSettings { } } -const _formatNames = { - 'AZTEC': BarcodeFormat.aztec, - 'CODABAR': BarcodeFormat.codabar, - 'CODE_39': BarcodeFormat.code39, - 'CODE_93': BarcodeFormat.code93, - 'CODE_128': BarcodeFormat.code128, - 'DATA_MATRIX': BarcodeFormat.dataMatrix, - 'EAN_8': BarcodeFormat.ean8, - 'EAN_13': BarcodeFormat.ean13, - 'ITF': BarcodeFormat.itf, - 'MAXICODE': BarcodeFormat.maxicode, - 'PDF_417': BarcodeFormat.pdf417, - 'QR_CODE': BarcodeFormat.qrcode, - 'RSS_14': BarcodeFormat.rss14, - 'RSS_EXPANDED': BarcodeFormat.rssExpanded, - 'UPC_A': BarcodeFormat.upcA, - 'UPC_E': BarcodeFormat.upcE, - 'UPC_EAN_EXTENSION': BarcodeFormat.upcEanExtension, -}; - class QRViewController { QRViewController._( MethodChannel channel, @@ -178,7 +174,7 @@ class QRViewController { final rawType = args['type'] as String; // Raw bytes are only supported by Android. final rawBytes = args['rawBytes'] as List; - final format = _formatNames[rawType]; + final format = BarcodeTypesExtension.fromString(rawType); if (format != null) { final barcode = Barcode(code, format, rawBytes); _scanUpdateController.sink.add(barcode); diff --git a/lib/src/types/barcode_format.dart b/lib/src/types/barcode_format.dart index 39a2a5e..645d448 100644 --- a/lib/src/types/barcode_format.dart +++ b/lib/src/types/barcode_format.dart @@ -56,7 +56,63 @@ extension BarcodeTypesExtension on BarcodeFormat { return index; } - BarcodeFormat fromString(String format) {} + static BarcodeFormat fromString(String format) { + switch (format) { + case 'AZTEC': + return BarcodeFormat.aztec; + break; + case 'CODABAR': + return BarcodeFormat.codabar; + break; + case 'CODE_39': + return BarcodeFormat.code39; + break; + case 'CODE_93': + return BarcodeFormat.code93; + break; + case 'CODE_128': + return BarcodeFormat.code128; + break; + case 'DATA_MATRIX': + return BarcodeFormat.dataMatrix; + break; + case 'EAN_8': + return BarcodeFormat.ean8; + break; + case 'EAN_13': + return BarcodeFormat.ean13; + break; + case 'ITF': + return BarcodeFormat.itf; + break; + case 'MAXICODE': + return BarcodeFormat.maxicode; + break; + case 'PDF_417': + return BarcodeFormat.pdf417; + break; + case 'QR_CODE': + return BarcodeFormat.qrcode; + break; + case 'RSS14': + return BarcodeFormat.rss14; + break; + case 'RSS_EXPANDED': + return BarcodeFormat.rssExpanded; + break; + case 'UPC_A': + return BarcodeFormat.upcA; + break; + case 'UPC_E': + return BarcodeFormat.upcE; + break; + case 'UPC_EAN_EXTENSION': + return BarcodeFormat.upcEanExtension; + break; + default: + return null; + } + } String get formatName { switch (this) { @@ -111,7 +167,8 @@ extension BarcodeTypesExtension on BarcodeFormat { case BarcodeFormat.upcEanExtension: return 'UPC_EAN_EXTENSION'; break; + default: + return 'NOT_VALID'; } - return 'NOT_VALID'; } } From 06d0db5ed568b864e91e4158e11f6be44c7bafb4 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Sun, 10 Jan 2021 21:49:25 +0100 Subject: [PATCH 6/8] Updated allowedFormats method. --- .../qr/flutterqr/FlutterQrPlugin.kt | 1 - .../net/touchcapture/qr/flutterqr/QRView.kt | 56 +++------- .../qr/flutterqr/QRViewFactory.kt | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 68 ++++++------ example/lib/main.dart | 1 + ios/Classes/QRView.swift | 102 ++++++++---------- lib/qr_code_scanner.dart | 2 + lib/src/qr_code_scanner.dart | 40 ++----- 8 files changed, 105 insertions(+), 167 deletions(-) diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt index 4210815..958a8da 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt @@ -7,7 +7,6 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.PluginRegistry import io.flutter.plugin.platform.PlatformViewRegistry diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt index 5d01244..91ad6f3 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt @@ -3,7 +3,6 @@ package net.touchcapture.qr.flutterqr import android.Manifest import android.app.Activity import android.app.Application -import android.content.Context import android.content.pm.PackageManager import android.os.Bundle import android.view.View @@ -18,27 +17,12 @@ import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.platform.PlatformView -class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, private val params: HashMap) : +class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap) : PlatformView, MethodChannel.MethodCallHandler { private var isTorchOn: Boolean = false private var barcodeView: BarcodeView? = null private val channel: MethodChannel - var allowedBarcodeTypes = mutableListOf() - - private val qrCodeTypes = mapOf( - 0 to BarcodeFormat.AZTEC, - 1 to BarcodeFormat.CODE_128, - 2 to BarcodeFormat.CODE_39, - 3 to BarcodeFormat.CODE_93, - 4 to BarcodeFormat.DATA_MATRIX, - 5 to BarcodeFormat.EAN_13, - 6 to BarcodeFormat.EAN_8, - 7 to BarcodeFormat.ITF, - 8 to BarcodeFormat.PDF_417, - 9 to BarcodeFormat.QR_CODE, - 10 to BarcodeFormat.UPC_E - ) init { checkAndRequestPermission(null) @@ -81,7 +65,7 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when(call.method) { - "startScan" -> startScan() + "startScan" -> startScan(call.arguments as? List, result) "stopScan" -> stopScan() "flipCamera" -> flipCamera(result) "toggleFlash" -> toggleFlash(result) @@ -90,9 +74,7 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, "requestPermissions" -> checkAndRequestPermission(result) "getCameraInfo" -> getCameraInfo(result) "getFlashInfo" -> getFlashInfo(result) -// "showNativeAlertDialog" -> showNativeAlertDialog(result) "getSystemFeatures" -> getSystemFeatures(result) - "setAllowedBarcodeFormats" -> setBarcodeFormats(call.arguments as List, result) else -> result.notImplemented() } } @@ -209,7 +191,16 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, return barcodeView } - private fun startScan() { + private fun startScan(arguments: List?, result: MethodChannel.Result) { + val allowedBarcodeTypes = mutableListOf() + try { + arguments?.forEach { + allowedBarcodeTypes.add(BarcodeFormat.values()[it]) + } + } catch (e: java.lang.Exception) { + result.error(null, null, null) + } + barcodeView?.decodeContinuous( object : BarcodeCallback { override fun barcodeResult(result: BarcodeResult) { @@ -242,29 +233,6 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, } } - private fun setBarcodeFormats(arguments: List, result: MethodChannel.Result) { - try { - allowedBarcodeTypes.clear() - arguments.forEach { - allowedBarcodeTypes.add(qrCodeTypes[it]!!) - } - result.success(true) - } catch (e: java.lang.Exception) { - result.error(null, null, null) - } - } - -// private fun showNativeAlertDialog(result: MethodChannel.Result) { -// AlertDialog.Builder(context) -// .setTitle("Scanning Unavailable") -// .setMessage("This app does not have permission to access the camera") -// .setPositiveButton(R.string.ok, null) -// .setCancelable(false) -// .setIcon(R.drawable.ic_dialog_alert) -// .show() -// result.success(true) -// } - private fun hasCameraPermission(): Boolean { return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Shared.activity?.checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt index e81303d..e16df1d 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt @@ -12,7 +12,7 @@ class QRViewFactory(private val messenger: BinaryMessenger) : override fun create(context: Context, id: Int, args: Any?): PlatformView { val params = args as HashMap - return QRView(messenger, id, context, params) + return QRView(messenger, id, params) } } \ No newline at end of file diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index d3ce8b9..7cef5f2 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,11 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 4FA3805501F593094264678B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75B64521967C76C845268E71 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - F50BD2AA150CE43D961DBB69 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,15 +30,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 11A772D5271EABFB7473B52C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 08F42BD28C1585A831B4FDA2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4D86354DC939411D3F7E41EA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 75B64521967C76C845268E71 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 96DCE88542EFC8C39614159F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -46,7 +45,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DB8991B7B046D94B2E68CEAD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + EF8A0739582463E296AD6CB1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,21 +54,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F50BD2AA150CE43D961DBB69 /* Pods_Runner.framework in Frameworks */, + 4FA3805501F593094264678B /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 509AC3E235E568B6DDAAEC7D /* Frameworks */ = { - isa = PBXGroup; - children = ( - C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -87,7 +79,7 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, D7C58E57CAF69D9A14CC212F /* Pods */, - 509AC3E235E568B6DDAAEC7D /* Frameworks */, + AF031A634C9742F16BF4F89A /* Frameworks */, ); sourceTree = ""; }; @@ -122,12 +114,20 @@ name = "Supporting Files"; sourceTree = ""; }; + AF031A634C9742F16BF4F89A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 75B64521967C76C845268E71 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; D7C58E57CAF69D9A14CC212F /* Pods */ = { isa = PBXGroup; children = ( - 4D86354DC939411D3F7E41EA /* Pods-Runner.debug.xcconfig */, - 11A772D5271EABFB7473B52C /* Pods-Runner.release.xcconfig */, - 96DCE88542EFC8C39614159F /* Pods-Runner.profile.xcconfig */, + DB8991B7B046D94B2E68CEAD /* Pods-Runner.debug.xcconfig */, + EF8A0739582463E296AD6CB1 /* Pods-Runner.release.xcconfig */, + 08F42BD28C1585A831B4FDA2 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -139,14 +139,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 854A11C1BE2493D5A6BD85DB /* [CP] Check Pods Manifest.lock */, + 7BEA526A4A0DFAAC4945EC09 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - FDE6A3C9913AC5840FF42544 /* [CP] Embed Pods Frameworks */, + 8DB6578C615972DBF2DFAA0D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -221,7 +221,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 854A11C1BE2493D5A6BD85DB /* [CP] Check Pods Manifest.lock */ = { + 7BEA526A4A0DFAAC4945EC09 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -243,39 +243,41 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 8DB6578C615972DBF2DFAA0D /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", + "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", ); - name = "Run Script"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; - FDE6A3C9913AC5840FF42544 /* [CP] Embed Pods Frameworks */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", - "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", ); - name = "[CP] Embed Pods Frameworks"; + name = "Run Script"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/example/lib/main.dart b/example/lib/main.dart index 20b42ec..13072f0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -132,6 +132,7 @@ class _QRViewExampleState extends State { key: qrKey, cameraFacing: CameraFacing.front, onQRViewCreated: _onQRViewCreated, + formatsAllowed: [BarcodeFormat.qrcode], overlay: QrScannerOverlayShape( borderColor: Colors.red, borderRadius: 10, diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index 2e72929..daff69d 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -15,20 +15,18 @@ public class QRView:NSObject,FlutterPlatformView { var channel: FlutterMethodChannel var cameraFacing: MTBCamera - var allowedBarcodeTypes: Array = [] - var QRCodeTypes = [ 0: AVMetadataObject.ObjectType.aztec, - 1: AVMetadataObject.ObjectType.code128, 2: AVMetadataObject.ObjectType.code39, 3: AVMetadataObject.ObjectType.code93, - 4: AVMetadataObject.ObjectType.dataMatrix, - 5: AVMetadataObject.ObjectType.ean13, + 4: AVMetadataObject.ObjectType.code128, + 5: AVMetadataObject.ObjectType.dataMatrix, 6: AVMetadataObject.ObjectType.ean8, - 7: AVMetadataObject.ObjectType.interleaved2of5, - 8: AVMetadataObject.ObjectType.pdf417, - 9: AVMetadataObject.ObjectType.qr, - 10: AVMetadataObject.ObjectType.upce + 7: AVMetadataObject.ObjectType.ean13, + 8: AVMetadataObject.ObjectType.interleaved2of5, + 10: AVMetadataObject.ObjectType.pdf417, + 11: AVMetadataObject.ObjectType.qr, + 15: AVMetadataObject.ObjectType.upce ] public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary){ @@ -50,7 +48,7 @@ public class QRView:NSObject,FlutterPlatformView { let arguments = call.arguments as! Dictionary self?.setDimensions(width: arguments["width"] ?? 0, height: arguments["height"] ?? 0, scanArea: arguments["scanArea"] ?? 0) case "startScan": - self?.startScan(result) + self?.startScan(call.arguments as! Array, result) case "flipCamera": self?.flipCamera(result) case "toggleFlash": @@ -63,12 +61,8 @@ public class QRView:NSObject,FlutterPlatformView { self?.getCameraInfo(result) case "getFlashInfo": self?.getFlashInfo(result) - case "showNativeAlertDialog": - self?.showNativeAlertDialog(result) case "getSystemFeatures": self?.getSystemFeatures(result) - case "setAllowedBarcodeFormats": - self?.setBarcodeFormats(call.arguments as! Array, result) default: result(FlutterMethodNotImplemented) return @@ -96,45 +90,51 @@ public class QRView:NSObject,FlutterPlatformView { } } - func startScan(_ result: @escaping FlutterResult) -> Void { + func startScan(_ arguments: Array, _ result: @escaping FlutterResult) -> Void { scanner = MTBBarcodeScanner(previewView: previewView) + var allowedBarcodeTypes: Array = [] + arguments.forEach { arg in + allowedBarcodeTypes.append( QRCodeTypes[arg]!) + } + MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in if permissionGranted { do { try self.scanner?.startScanning(with: self.cameraFacing, resultBlock: { [weak self] codes in if let codes = codes { for code in codes { - var typeString: String; - switch(code.type) { - case AVMetadataObject.ObjectType.aztec: - typeString = "AZTEC" - case AVMetadataObject.ObjectType.code39: - typeString = "CODE_39" - case AVMetadataObject.ObjectType.code93: - typeString = "CODE_93" - case AVMetadataObject.ObjectType.code128: - typeString = "CODE_128" - case AVMetadataObject.ObjectType.dataMatrix: - typeString = "DATA_MATRIX" - case AVMetadataObject.ObjectType.ean8: - typeString = "EAN_8" - case AVMetadataObject.ObjectType.ean13: - typeString = "EAN_13" - case AVMetadataObject.ObjectType.itf14: - typeString = "ITF" - case AVMetadataObject.ObjectType.pdf417: - typeString = "PDF_417" - case AVMetadataObject.ObjectType.qr: - typeString = "QR_CODE" - case AVMetadataObject.ObjectType.upce: - typeString = "UPC_E" - default: - return - } +// var typeString: String; +// switch(code.type) { +// case AVMetadataObject.ObjectType.aztec: +// typeString = "AZTEC" +// case AVMetadataObject.ObjectType.code39: +// typeString = "CODE_39" +// case AVMetadataObject.ObjectType.code93: +// typeString = "CODE_93" +// case AVMetadataObject.ObjectType.code128: +// typeString = "CODE_128" +// case AVMetadataObject.ObjectType.dataMatrix: +// typeString = "DATA_MATRIX" +// case AVMetadataObject.ObjectType.ean8: +// typeString = "EAN_8" +// case AVMetadataObject.ObjectType.ean13: +// typeString = "EAN_13" +// case AVMetadataObject.ObjectType.itf14: +// typeString = "ITF" +// case AVMetadataObject.ObjectType.pdf417: +// typeString = "PDF_417" +// case AVMetadataObject.ObjectType.qr: +// typeString = "QR_CODE" +// case AVMetadataObject.ObjectType.upce: +// typeString = "UPC_E" +// default: +// return +// } guard let stringValue = code.stringValue else { continue } + let typeString = code.type.rawValue let result = ["code": stringValue, "type": typeString] - if self!.allowedBarcodeTypes.count == 0 || self!.allowedBarcodeTypes.contains(code.type) { + if allowedBarcodeTypes.count == 0 || allowedBarcodeTypes.contains(code.type) { self?.channel.invokeMethod("onRecognizeQR", arguments: result) } @@ -218,11 +218,6 @@ public class QRView:NSObject,FlutterPlatformView { } return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) } - - func showNativeAlertDialog(_ result: @escaping FlutterResult) -> Void { - UIAlertView(title: "Scanning Unavailable", message: "This app does not have permission to access the camera", delegate: nil, cancelButtonTitle: nil, otherButtonTitles: "Ok").show() - return result(true) - } func getSystemFeatures(_ result: @escaping FlutterResult) -> Void { if let sc: MTBBarcodeScanner = scanner { @@ -251,15 +246,4 @@ public class QRView:NSObject,FlutterPlatformView { return result(FlutterError(code: "404", message: nil, details: nil)) } - func setBarcodeFormats(_ arguments: Array, _ result: @escaping FlutterResult){ - do{ - allowedBarcodeTypes.removeAll() - try arguments.forEach { arg in - allowedBarcodeTypes.append(try QRCodeTypes[arg]!) - } - result(true) - }catch{ - result(FlutterError(code: "404", message: nil, details: nil)) - } - } } diff --git a/lib/qr_code_scanner.dart b/lib/qr_code_scanner.dart index 9df6d69..4220613 100644 --- a/lib/qr_code_scanner.dart +++ b/lib/qr_code_scanner.dart @@ -1,5 +1,7 @@ export 'src/qr_code_scanner.dart'; export 'src/qr_scanner_overlay_shape.dart'; export 'src/types/barcode.dart'; +export 'src/types/barcode_format.dart'; export 'src/types/camera.dart'; +export 'src/types/camera_exception.dart'; export 'src/types/features.dart'; diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index f41c857..bb509e7 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -24,7 +24,7 @@ class QRView extends StatefulWidget { this.overlayMargin = EdgeInsets.zero, this.cameraFacing = CameraFacing.back, this.onPermissionSet, - this.showNativeAlertDialog = false, + this.formatsAllowed, }) : assert(key != null), assert(onQRViewCreated != null), super(key: key); @@ -48,8 +48,8 @@ class QRView extends StatefulWidget { /// Calls the provided [onPermissionSet] callback when the permission is set. final PermissionSetCallback onPermissionSet; - /// Gives the possibility to show a dialog. - final bool showNativeAlertDialog; + /// Use [formatsAllowed] to specify which formats needs to be scanned. + final List formatsAllowed; @override State createState() => _QRViewState(); @@ -133,8 +133,8 @@ class _QRViewState extends State { // Start scan after creation of the view final controller = QRViewController._(_channel, widget.key, cutOutSize, - widget.onPermissionSet, widget.showNativeAlertDialog) - .._startScan(widget.key, cutOutSize); + widget.onPermissionSet) + .._startScan(widget.key, cutOutSize, widget.formatsAllowed); // Initialize the controller for controlling the QRView if (widget.onQRViewCreated != null) { @@ -162,8 +162,7 @@ class QRViewController { MethodChannel channel, GlobalKey qrKey, double scanArea, - PermissionSetCallback onPermissionSet, - bool showNativeAlertDialogOnError, + PermissionSetCallback onPermissionSet ) : _channel = channel { _channel.setMethodCallHandler((call) async { switch (call.method) { @@ -190,9 +189,6 @@ class QRViewController { _hasPermissions = true; } else { _hasPermissions = false; - if (showNativeAlertDialogOnError) { - await showNativeAlertDialog(); - } } if (onPermissionSet != null) { onPermissionSet(this, call.arguments as bool); @@ -219,12 +215,14 @@ class QRViewController { Future _startScan( GlobalKey key, double cutOutSize, - ) async { + List barcodeFormats) async { // We need to update the dimension before the scan is started. QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize); - return _channel.invokeMethod('startScan'); + return _channel.invokeMethod('startScan', + barcodeFormats?.map((e) => e.asInt())?.toList() ?? []); } + /// Gets information about which camera is active. Future getCameraInfo() async { try { return CameraFacing @@ -280,23 +278,7 @@ class QRViewController { } } - Future showNativeAlertDialog() async { - try { - await _channel.invokeMethod('showNativeAlertDialog'); - } on PlatformException catch (e) { - throw CameraException(e.code, e.message); - } - } - - Future setAllowedBarcodeTypes(List list) async { - try { - await _channel.invokeMethod('setAllowedBarcodeFormats', - list?.map((e) => e.asInt())?.toList() ?? []); - } on PlatformException catch (e) { - throw CameraException(e.code, e.message); - } - } - + /// Returns which features are available on device. Future getSystemFeatures() async { try { var features = From f3b3452570e24a0f11706384624992b93e850bf8 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Sun, 10 Jan 2021 21:54:53 +0100 Subject: [PATCH 7/8] Added possibility to choose facing camera on start. Moved the size listener to plugin side. --- lib/src/qr_code_scanner.dart | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index bb509e7..eec48b1 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -132,8 +132,8 @@ class _QRViewState extends State { _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id'); // Start scan after creation of the view - final controller = QRViewController._(_channel, widget.key, cutOutSize, - widget.onPermissionSet) + final controller = QRViewController._( + _channel, widget.key, cutOutSize, widget.onPermissionSet) .._startScan(widget.key, cutOutSize, widget.formatsAllowed); // Initialize the controller for controlling the QRView @@ -158,12 +158,9 @@ class _QrCameraSettings { } class QRViewController { - QRViewController._( - MethodChannel channel, - GlobalKey qrKey, - double scanArea, - PermissionSetCallback onPermissionSet - ) : _channel = channel { + QRViewController._(MethodChannel channel, GlobalKey qrKey, double scanArea, + PermissionSetCallback onPermissionSet) + : _channel = channel { _channel.setMethodCallHandler((call) async { switch (call.method) { case 'onRecognizeQR': @@ -212,14 +209,12 @@ class QRViewController { bool get hasPermissions => _hasPermissions; /// Starts the barcode scanner - Future _startScan( - GlobalKey key, - double cutOutSize, + Future _startScan(GlobalKey key, double cutOutSize, List barcodeFormats) async { // We need to update the dimension before the scan is started. QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize); - return _channel.invokeMethod('startScan', - barcodeFormats?.map((e) => e.asInt())?.toList() ?? []); + return _channel.invokeMethod( + 'startScan', barcodeFormats?.map((e) => e.asInt())?.toList() ?? []); } /// Gets information about which camera is active. From 513482b27319855501fe601ac7240152fa930e5e Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Mon, 11 Jan 2021 10:04:01 +0100 Subject: [PATCH 8/8] Updated iOS part to support code filtering. Updated minSDK for example. --- example/ios/Runner.xcodeproj/project.pbxproj | 8 +-- example/pubspec.yaml | 2 +- ios/Classes/QRView.swift | 57 ++++++++++---------- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 7cef5f2..3d2b2af 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -168,7 +168,7 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = 5LSCTACHT8; + DevelopmentTeam = RCH2VG82SH; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; @@ -372,7 +372,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 5LSCTACHT8; + DEVELOPMENT_TEAM = RCH2VG82SH; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -510,7 +510,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 5LSCTACHT8; + DEVELOPMENT_TEAM = RCH2VG82SH; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -541,7 +541,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 5LSCTACHT8; + DEVELOPMENT_TEAM = RCH2VG82SH; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 6df5ea4..929acf1 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the flutter_qr plugin. publish_to: 'none' environment: - sdk: ">=2.3.0 <3.0.0" + sdk: ">=2.6.0 <3.0.0" dependencies: flutter: diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index daff69d..e2c65b6 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -104,36 +104,35 @@ public class QRView:NSObject,FlutterPlatformView { try self.scanner?.startScanning(with: self.cameraFacing, resultBlock: { [weak self] codes in if let codes = codes { for code in codes { -// var typeString: String; -// switch(code.type) { -// case AVMetadataObject.ObjectType.aztec: -// typeString = "AZTEC" -// case AVMetadataObject.ObjectType.code39: -// typeString = "CODE_39" -// case AVMetadataObject.ObjectType.code93: -// typeString = "CODE_93" -// case AVMetadataObject.ObjectType.code128: -// typeString = "CODE_128" -// case AVMetadataObject.ObjectType.dataMatrix: -// typeString = "DATA_MATRIX" -// case AVMetadataObject.ObjectType.ean8: -// typeString = "EAN_8" -// case AVMetadataObject.ObjectType.ean13: -// typeString = "EAN_13" -// case AVMetadataObject.ObjectType.itf14: -// typeString = "ITF" -// case AVMetadataObject.ObjectType.pdf417: -// typeString = "PDF_417" -// case AVMetadataObject.ObjectType.qr: -// typeString = "QR_CODE" -// case AVMetadataObject.ObjectType.upce: -// typeString = "UPC_E" -// default: -// return -// } + var typeString: String; + switch(code.type) { + case AVMetadataObject.ObjectType.aztec: + typeString = "AZTEC" + case AVMetadataObject.ObjectType.code39: + typeString = "CODE_39" + case AVMetadataObject.ObjectType.code93: + typeString = "CODE_93" + case AVMetadataObject.ObjectType.code128: + typeString = "CODE_128" + case AVMetadataObject.ObjectType.dataMatrix: + typeString = "DATA_MATRIX" + case AVMetadataObject.ObjectType.ean8: + typeString = "EAN_8" + case AVMetadataObject.ObjectType.ean13: + typeString = "EAN_13" + case AVMetadataObject.ObjectType.itf14: + typeString = "ITF" + case AVMetadataObject.ObjectType.pdf417: + typeString = "PDF_417" + case AVMetadataObject.ObjectType.qr: + typeString = "QR_CODE" + case AVMetadataObject.ObjectType.upce: + typeString = "UPC_E" + default: + return + } guard let stringValue = code.stringValue else { continue } - let typeString = code.type.rawValue - let result = ["code": stringValue, "type": typeString] + let result = ["code": stringValue, "type": typeString] if allowedBarcodeTypes.count == 0 || allowedBarcodeTypes.contains(code.type) { self?.channel.invokeMethod("onRecognizeQR", arguments: result) }