From f439cd5a612b265b4b301e58d818b5d3c5b3f7fe Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Sun, 7 Feb 2021 22:08:11 +0100 Subject: [PATCH 1/6] Fixed camera facing giving wrong value on startup on iOS --- .gitignore | 1 + ios/Classes/QRView.swift | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index f682f8d..aef8b20 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ unlinked_spec.ds **/ios/**/DerivedData/ **/ios/**/Icon? **/ios/**/Pods/ +**/ios/.symlinks/ **/ios/**/.symlinks/ **/ios/**/profile **/ios/**/xcuserdata diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index aa7aa69..99265f5 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -204,12 +204,7 @@ public class QRView:NSObject,FlutterPlatformView { func getCameraInfo(_ result: @escaping FlutterResult) -> Void { MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in if permissionGranted { - if let sc: MTBBarcodeScanner = self.scanner { - result(sc.camera.rawValue) - } else { - let error = FlutterError(code: "cameraInformationError", message: "Could not get camera information", details: nil) - result(error) - } + result(self.cameraFacing.rawValue) } else { return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) } @@ -222,6 +217,7 @@ public class QRView:NSObject,FlutterPlatformView { if let sc: MTBBarcodeScanner = self.scanner { if sc.hasOppositeCamera() { sc.flipCamera() + self.cameraFacing = sc.camera } return result(sc.camera.rawValue) } From ba67e47b7d657ec78c9b6b3cce8158fd4442e618 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Mon, 8 Feb 2021 08:51:41 +0100 Subject: [PATCH 2/6] Added onResume listener and updateDimensions call (#250) --- lib/src/lifecycle_event_handler.dart | 30 ++++++++++++++++++++++++++++ lib/src/qr_code_scanner.dart | 12 +++++++++++ 2 files changed, 42 insertions(+) create mode 100644 lib/src/lifecycle_event_handler.dart diff --git a/lib/src/lifecycle_event_handler.dart b/lib/src/lifecycle_event_handler.dart new file mode 100644 index 0000000..f6da736 --- /dev/null +++ b/lib/src/lifecycle_event_handler.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; + +class LifecycleEventHandler extends WidgetsBindingObserver { + LifecycleEventHandler({ + this.resumeCallBack, + this.suspendingCallBack, + }); + + final AsyncCallback resumeCallBack; + final AsyncCallback suspendingCallBack; + + @override + Future didChangeAppLifecycleState(AppLifecycleState state) async { + switch (state) { + case AppLifecycleState.resumed: + if (resumeCallBack != null) { + await resumeCallBack(); + } + break; + case AppLifecycleState.inactive: + case AppLifecycleState.paused: + case AppLifecycleState.detached: + if (suspendingCallBack != null) { + await suspendingCallBack(); + } + break; + } + } +} diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index ec28402..66243df 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'lifecycle_event_handler.dart'; import 'qr_scanner_overlay_shape.dart'; import 'types/barcode.dart'; import 'types/barcode_format.dart'; @@ -58,6 +59,17 @@ class QRView extends StatefulWidget { class _QRViewState extends State { var _channel; + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addObserver( + LifecycleEventHandler(resumeCallBack: () async => + QRViewController.updateDimensions(widget.key, _channel, + overlay: widget.overlay) + )); + } + @override Widget build(BuildContext context) { return NotificationListener( From 949ad5943f94f62d39ffe4c72202cd9db07f48d2 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Mon, 8 Feb 2021 09:48:13 +0100 Subject: [PATCH 3/6] Updated updateDimensions and permissionCheck (#251) --- ios/Classes/QRView.swift | 296 +++++++++++++++-------------------- lib/src/qr_code_scanner.dart | 10 +- 2 files changed, 126 insertions(+), 180 deletions(-) diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index 99265f5..16c3c0e 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -77,222 +77,170 @@ public class QRView:NSObject,FlutterPlatformView { return previewView } - func setDimensions(_ result: @escaping FlutterResult, width: Double, height: Double, scanArea: Double, scanAreaOffset: Double) -> Void { + func requestPermissions(_ result: @escaping FlutterResult) { MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in if permissionGranted { -// self?.channel.invokeMethod("onPermissionSet", arguments: true) - // First set the size of the preview area. - self.previewView.frame = CGRect(x: 0, y: 0, width: width, height: height) - - // Then set the size of the scan area. - let midX = self.view().bounds.midX - let midY = self.view().bounds.midY - - // Check if the scanner is already created. - if let sc: MTBBarcodeScanner = self.scanner { - if let previewLayer = sc.previewLayer { - previewLayer.frame = self.previewView.bounds; - } - if (scanArea != 0) { - sc.scanRect = CGRect(x: Double(midX) - (scanArea / 2), y: Double(midY) - (scanArea / 2), width: scanArea, height: scanArea) - - // Set offset if provided. - if (scanAreaOffset != 0) { - sc.scanRect = sc.scanRect.offsetBy(dx: 0, dy: CGFloat(scanAreaOffset)) - } - } - } else { - // Create a scanner view if it doesn't exist yet. - self.scanner = MTBBarcodeScanner(previewView: self.previewView) - - if (scanArea != 0) { - self.scanner?.didStartScanningBlock = { - self.scanner?.scanRect = CGRect(x: Double(midX) - (scanArea / 2), y: Double(midY) - (scanArea / 2), width: scanArea, height: scanArea) - - // Set offset if provided. - if (scanAreaOffset != 0) { - self.scanner?.scanRect = (self.scanner?.scanRect.offsetBy(dx: 0, dy: CGFloat(scanAreaOffset)))! - } - } - } - } - return result(width) + self.channel.invokeMethod("onPermissionSet", arguments: true) } else { return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) } }) } - func startScan(_ arguments: Array, _ result: @escaping FlutterResult) -> Void { - if (scanner == nil) { - scanner = MTBBarcodeScanner(previewView: previewView) + func setDimensions(_ result: @escaping FlutterResult, width: Double, height: Double, scanArea: Double, scanAreaOffset: Double) { + // First ask for permission + requestPermissions(result) + + // Then set the size of the preview area. + self.previewView.frame = CGRect(x: 0, y: 0, width: width, height: height) + + // Then set the size of the scan area. + let midX = self.view().bounds.midX + let midY = self.view().bounds.midY + + // Check if the scanner already exists, else create one. + if (self.scanner == nil) { + self.scanner = MTBBarcodeScanner(previewView: self.previewView) } + // Set the size of the preview. + if let previewLayer = self.scanner?.previewLayer { + previewLayer.frame = self.previewView.bounds; + } + + // Set scanArea if provided. + if (scanArea != 0) { + self.scanner?.didStartScanningBlock = { + self.scanner?.scanRect = CGRect(x: Double(midX) - (scanArea / 2), y: Double(midY) - (scanArea / 2), width: scanArea, height: scanArea) + + // Set offset if provided. + if (scanAreaOffset != 0) { + self.scanner?.scanRect = (self.scanner?.scanRect.offsetBy(dx: 0, dy: CGFloat(scanAreaOffset)))! + } + } + } + return result(width) + + } + + func startScan(_ arguments: Array, _ result: @escaping FlutterResult) { + // Check for allowed barcodes var allowedBarcodeTypes: Array = [] arguments.forEach { arg in allowedBarcodeTypes.append( QRCodeTypes[arg]!) } - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - self.channel.invokeMethod("onPermissionSet", arguments: true) - 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] - if allowedBarcodeTypes.count == 0 || allowedBarcodeTypes.contains(code.type) { - self?.channel.invokeMethod("onRecognizeQR", arguments: result) - } - - } + 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] + if allowedBarcodeTypes.count == 0 || allowedBarcodeTypes.contains(code.type) { + self?.channel.invokeMethod("onRecognizeQR", arguments: result) } - }) - } catch { - let error = FlutterError(code: "unknown-error", message: "Unable to start scanning", details: nil) - return result(error) + + } } - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) - } - }) + }) + } catch { + let error = FlutterError(code: "unknown-error", message: "Unable to start scanning", details: nil) + return result(error) + } } - func stopCamera(_ result: @escaping FlutterResult){ - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - if let sc: MTBBarcodeScanner = self.scanner { - if sc.isScanning() { - sc.stopScanning() - } - } - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) + func stopCamera(_ result: @escaping FlutterResult) { + if let sc: MTBBarcodeScanner = self.scanner { + if sc.isScanning() { + sc.stopScanning() } - }) + } } - func getCameraInfo(_ result: @escaping FlutterResult) -> Void { - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - result(self.cameraFacing.rawValue) - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) - } - }) + func getCameraInfo(_ result: @escaping FlutterResult) { + result(self.cameraFacing.rawValue) } - func flipCamera(_ result: @escaping FlutterResult){ - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - if let sc: MTBBarcodeScanner = self.scanner { - if sc.hasOppositeCamera() { - sc.flipCamera() - self.cameraFacing = sc.camera - } - return result(sc.camera.rawValue) - } - return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) + func flipCamera(_ result: @escaping FlutterResult) { + if let sc: MTBBarcodeScanner = self.scanner { + if sc.hasOppositeCamera() { + sc.flipCamera() + self.cameraFacing = sc.camera } - }) + return result(sc.camera.rawValue) + } + return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) } - func getFlashInfo(_ result: @escaping FlutterResult) -> Void { - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - if let sc: MTBBarcodeScanner = self.scanner { - result(sc.torchMode.rawValue != 0) - } else { - let error = FlutterError(code: "cameraInformationError", message: "Could not get flash information", details: nil) - result(error) - } - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) - } - }) + func getFlashInfo(_ result: @escaping FlutterResult) { + if let sc: MTBBarcodeScanner = self.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){ - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - if let sc: MTBBarcodeScanner = self.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)) - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) + if let sc: MTBBarcodeScanner = self.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(_ result: @escaping FlutterResult) { - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - if let sc: MTBBarcodeScanner = self.scanner { - if sc.isScanning() { - sc.freezeCapture() - } - return result(true) - } - return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) + if let sc: MTBBarcodeScanner = self.scanner { + if sc.isScanning() { + sc.freezeCapture() } - }) + return result(true) + } + return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) } func resumeCamera(_ result: @escaping FlutterResult) { - MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in - if permissionGranted { - if let sc: MTBBarcodeScanner = self.scanner { - if !sc.isScanning() { - sc.unfreezeCapture() - } - return result(true) - } - return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) - } else { - return result(FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil)) + if let sc: MTBBarcodeScanner = self.scanner { + if !sc.isScanning() { + sc.unfreezeCapture() } - }) + return result(true) + } + return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) } - func getSystemFeatures(_ result: @escaping FlutterResult) -> Void { + func getSystemFeatures(_ result: @escaping FlutterResult) { if let sc: MTBBarcodeScanner = scanner { var hasBackCameraVar = false var hasFrontCameraVar = false diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 66243df..49d07d3 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -62,12 +62,10 @@ class _QRViewState extends State { @override void initState() { super.initState(); - - WidgetsBinding.instance.addObserver( - LifecycleEventHandler(resumeCallBack: () async => - QRViewController.updateDimensions(widget.key, _channel, - overlay: widget.overlay) - )); + WidgetsBinding.instance.addObserver(LifecycleEventHandler( + resumeCallBack: () async => QRViewController.updateDimensions( + widget.key, _channel, + overlay: widget.overlay))); } @override From cd8db0f7cc96fbcae497fa0f7065756a5c2d55f7 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Mon, 8 Feb 2021 21:10:58 +0100 Subject: [PATCH 4/6] Add nullcheck and small delay to updateDimensions (#250) --- ios/Classes/QRView.swift | 12 ++++-------- lib/src/qr_code_scanner.dart | 24 +++++++++++++++++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index 16c3c0e..08402c6 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -32,6 +32,7 @@ public class QRView:NSObject,FlutterPlatformView { public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary){ self.registrar = registrar previewView = UIView(frame: frame) + scanner = MTBBarcodeScanner(previewView: previewView) cameraFacing = MTBCamera.init(rawValue: UInt(Int(params["cameraFacing"] as! Double))) ?? MTBCamera.back channel = FlutterMethodChannel(name: "net.touchcapture.qr.flutterqr/qrview_\(id)", binaryMessenger: registrar.messenger()) } @@ -97,17 +98,12 @@ public class QRView:NSObject,FlutterPlatformView { // Then set the size of the scan area. let midX = self.view().bounds.midX let midY = self.view().bounds.midY - - // Check if the scanner already exists, else create one. - if (self.scanner == nil) { - self.scanner = MTBBarcodeScanner(previewView: self.previewView) - } - + // Set the size of the preview. if let previewLayer = self.scanner?.previewLayer { - previewLayer.frame = self.previewView.bounds; + previewLayer.frame = self.previewView.bounds } - + // Set scanArea if provided. if (scanArea != 0) { self.scanner?.didStartScanningBlock = { diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 49d07d3..911242d 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -63,9 +63,13 @@ class _QRViewState extends State { void initState() { super.initState(); WidgetsBinding.instance.addObserver(LifecycleEventHandler( - resumeCallBack: () async => QRViewController.updateDimensions( - widget.key, _channel, - overlay: widget.overlay))); + resumeCallBack: () async => { + if (_channel != null) { + QRViewController.updateDimensions( + widget.key, _channel, + overlay: widget.overlay) + } + })); } @override @@ -82,9 +86,10 @@ class _QRViewState extends State { bool onNotification(notification) { Future.microtask(() => { - QRViewController.updateDimensions(widget.key, _channel, - overlay: widget.overlay) - }); + QRViewController.updateDimensions(widget.key, _channel, + overlay: widget.overlay) + }); + return false; } @@ -204,6 +209,7 @@ class QRViewController { Stream get scannedDataStream => _scanUpdateController.stream; + static bool _firstRun = true; SystemFeatures _features; bool _hasPermissions; @@ -309,8 +315,12 @@ class QRViewController { static Future updateDimensions(GlobalKey key, MethodChannel channel, {QrScannerOverlayShape overlay}) async { if (defaultTargetPlatform == TargetPlatform.iOS) { + if (_firstRun) { + _firstRun = false; + await Future.delayed(Duration(milliseconds: 300)); + } final RenderBox renderBox = key.currentContext.findRenderObject(); - try { + try { await channel.invokeMethod('setDimensions', { 'width': renderBox.size.width, 'height': renderBox.size.height, From ff33ea9fc01383d805187adcc91fa1682897451a Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Tue, 9 Feb 2021 14:10:58 +0100 Subject: [PATCH 5/6] Fixed permission callback on Android (#251) --- .../qr/flutterqr/FlutterQrPlugin.kt | 16 ++--- .../net/touchcapture/qr/flutterqr/QRView.kt | 65 ++++++++++++------- .../net/touchcapture/qr/flutterqr/Shared.kt | 4 ++ lib/src/qr_code_scanner.dart | 9 ++- 4 files changed, 56 insertions(+), 38 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 958a8da..f096d27 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt @@ -21,7 +21,7 @@ class FlutterQrPlugin : FlutterPlugin, ActivityAware { } private fun onAttachedToV1(registrar: PluginRegistry.Registrar) { - registrar.addRequestPermissionsResultListener(CameraRequestPermissionsListener()) + Shared.registrar = registrar onAttachedToEngines(registrar.platformViewRegistry(), registrar.messenger(), registrar.activity()) } @@ -45,27 +45,23 @@ class FlutterQrPlugin : FlutterPlugin, ActivityAware { override fun onAttachedToActivity(activityPluginBinding: ActivityPluginBinding) { Shared.activity = activityPluginBinding.activity - activityPluginBinding.addRequestPermissionsResultListener(CameraRequestPermissionsListener()) + Shared.binding = activityPluginBinding } override fun onDetachedFromActivityForConfigChanges() { Shared.activity = null + Shared.binding = null } override fun onReattachedToActivityForConfigChanges(activityPluginBinding: ActivityPluginBinding) { Shared.activity = activityPluginBinding.activity + Shared.binding = activityPluginBinding } override fun onDetachedFromActivity() { Shared.activity = null + Shared.binding = null } - 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) { - 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 e914f40..251cc6e 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt @@ -4,30 +4,40 @@ import android.Manifest import android.app.Activity import android.app.Application import android.content.pm.PackageManager -import android.os.Bundle -import android.view.View -import com.google.zxing.ResultPoint import android.hardware.Camera.CameraInfo import android.os.Build +import android.os.Bundle +import android.view.View import com.google.zxing.BarcodeFormat +import com.google.zxing.ResultPoint import com.journeyapps.barcodescanner.BarcodeCallback import com.journeyapps.barcodescanner.BarcodeResult import com.journeyapps.barcodescanner.BarcodeView import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.PluginRegistry import io.flutter.plugin.platform.PlatformView + + class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap) : - PlatformView, MethodChannel.MethodCallHandler { + PlatformView, MethodChannel.MethodCallHandler, PluginRegistry.RequestPermissionsResultListener { private var isTorchOn: Boolean = false private var isPaused: Boolean = false private var barcodeView: BarcodeView? = null - private val channel: MethodChannel + private val channel: MethodChannel = MethodChannel(messenger, "net.touchcapture.qr.flutterqr/qrview_$id") + private var permissionGranted: Boolean = false init { - checkAndRequestPermission(null) - channel = MethodChannel(messenger, "net.touchcapture.qr.flutterqr/qrview_$id") + if (Shared.binding != null) { + Shared.binding!!.addRequestPermissionsResultListener(this) + } + + if (Shared.registrar != null) { + Shared.registrar!!.addRequestPermissionsResultListener(this) + } + channel.setMethodCallHandler(this) Shared.activity?.application?.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks { override fun onActivityPaused(p0: Activity) { @@ -92,8 +102,6 @@ class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap?, result: MethodChannel.Result) { - if (!hasCameraPermission()) { - return cameraPermissionNotSet(result) - } - val allowedBarcodeTypes = mutableListOf() try { arguments?.forEach { @@ -254,7 +255,6 @@ class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap result?.success(true) Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { Shared.activity?.requestPermissions( arrayOf(Manifest.permission.CAMERA), @@ -265,5 +265,20 @@ class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap?, + grantResults: IntArray): Boolean { + + if (requestCode == Shared.CAMERA_REQUEST_ID && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + permissionGranted = true + channel.invokeMethod("onPermissionSet", true) + return true + } + permissionGranted = false + channel.invokeMethod("onPermissionSet", false) + return false + } + } 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 a5a663d..996df87 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt @@ -1,8 +1,12 @@ package net.touchcapture.qr.flutterqr import android.app.Activity +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import io.flutter.plugin.common.PluginRegistry object Shared { const val CAMERA_REQUEST_ID = 513469796 var activity: Activity? = null + var binding: ActivityPluginBinding? = null + var registrar: PluginRegistry.Registrar? = null } \ No newline at end of file diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 911242d..4fd9af8 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -140,7 +140,7 @@ class _QRViewState extends State { // Start scan after creation of the view final controller = - QRViewController._(_channel, widget.key, widget.onPermissionSet) + QRViewController._(_channel, widget.key, widget.onPermissionSet, widget.cameraFacing) .._startScan(widget.key, widget.overlay, widget.formatsAllowed); // Initialize the controller for controlling the QRView @@ -166,8 +166,8 @@ class _QrCameraSettings { class QRViewController { QRViewController._(MethodChannel channel, GlobalKey qrKey, - PermissionSetCallback onPermissionSet) - : _channel = channel { + PermissionSetCallback onPermissionSet, CameraFacing cameraFacing) + : _channel = channel, _cameraFacing = cameraFacing{ _channel.setMethodCallHandler((call) async { switch (call.method) { case 'onRecognizeQR': @@ -204,6 +204,7 @@ class QRViewController { } final MethodChannel _channel; + final CameraFacing _cameraFacing; final StreamController _scanUpdateController = StreamController(); @@ -232,6 +233,8 @@ class QRViewController { /// Gets information about which camera is active. Future getCameraInfo() async { try { + var cameraFacing = await _channel.invokeMethod('getCameraInfo') as int; + if (cameraFacing == -1) return _cameraFacing; return CameraFacing .values[await _channel.invokeMethod('getCameraInfo') as int]; } on PlatformException catch (e) { From a17ae80aed91a80ebddf06d9e641b94da20eb2f5 Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Tue, 9 Feb 2021 14:46:06 +0100 Subject: [PATCH 6/6] Updated permission callback and added requests when permission is not granted. (#251) --- .../net/touchcapture/qr/flutterqr/QRView.kt | 58 ++++++++++--------- lib/src/qr_code_scanner.dart | 29 +++++----- 2 files changed, 47 insertions(+), 40 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 251cc6e..c7355b3 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt @@ -41,13 +41,13 @@ class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap?, grantResults: IntArray): Boolean { - if (requestCode == Shared.CAMERA_REQUEST_ID && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (requestCode == Shared.CAMERA_REQUEST_ID && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { permissionGranted = true channel.invokeMethod("onPermissionSet", true) return true diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 4fd9af8..0e4fb8d 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -64,12 +64,12 @@ class _QRViewState extends State { super.initState(); WidgetsBinding.instance.addObserver(LifecycleEventHandler( resumeCallBack: () async => { - if (_channel != null) { - QRViewController.updateDimensions( - widget.key, _channel, - overlay: widget.overlay) - } - })); + if (_channel != null) + { + QRViewController.updateDimensions(widget.key, _channel, + overlay: widget.overlay) + } + })); } @override @@ -86,9 +86,9 @@ class _QRViewState extends State { bool onNotification(notification) { Future.microtask(() => { - QRViewController.updateDimensions(widget.key, _channel, - overlay: widget.overlay) - }); + QRViewController.updateDimensions(widget.key, _channel, + overlay: widget.overlay) + }); return false; } @@ -139,9 +139,9 @@ 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, widget.onPermissionSet, widget.cameraFacing) - .._startScan(widget.key, widget.overlay, widget.formatsAllowed); + final controller = QRViewController._( + _channel, widget.key, widget.onPermissionSet, widget.cameraFacing) + .._startScan(widget.key, widget.overlay, widget.formatsAllowed); // Initialize the controller for controlling the QRView if (widget.onQRViewCreated != null) { @@ -167,7 +167,8 @@ class _QrCameraSettings { class QRViewController { QRViewController._(MethodChannel channel, GlobalKey qrKey, PermissionSetCallback onPermissionSet, CameraFacing cameraFacing) - : _channel = channel, _cameraFacing = cameraFacing{ + : _channel = channel, + _cameraFacing = cameraFacing { _channel.setMethodCallHandler((call) async { switch (call.method) { case 'onRecognizeQR': @@ -323,7 +324,7 @@ class QRViewController { await Future.delayed(Duration(milliseconds: 300)); } final RenderBox renderBox = key.currentContext.findRenderObject(); - try { + try { await channel.invokeMethod('setDimensions', { 'width': renderBox.size.width, 'height': renderBox.size.height,