| @@ -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<String>, grantResults: IntArray): Boolean { | |||
| if (id == Shared.CAMERA_REQUEST_ID && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | |||
| Shared.cameraPermissionContinuation?.run() | |||
| return true | |||
| } | |||
| return false | |||
| @@ -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<BarcodeFormat>() | |||
| 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<Int>, 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<ResultPoint>) {} | |||
| @@ -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<Int>, 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) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -5,5 +5,4 @@ import android.app.Activity | |||
| object Shared { | |||
| const val CAMERA_REQUEST_ID = 513469796 | |||
| var activity: Activity? = null | |||
| var cameraPermissionContinuation: Runnable? = null | |||
| } | |||
| @@ -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<QRViewExample> { | |||
| Barcode result; | |||
| var flashState = flashOn; | |||
| var cameraState = frontCamera; | |||
| QRViewController controller; | |||
| final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); | |||
| @@ -64,44 +58,33 @@ class _QRViewExampleState extends State<QRViewExample> { | |||
| 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<QRViewExample> { | |||
| ); | |||
| } | |||
| 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<QRViewExample> { | |||
| } | |||
| void _onQRViewCreated(QRViewController controller) { | |||
| this.controller = controller; | |||
| setState(() { | |||
| this.controller = controller; | |||
| }); | |||
| controller.scannedDataStream.listen((scanData) { | |||
| setState(() { | |||
| result = scanData; | |||
| @@ -15,6 +15,22 @@ public class QRView:NSObject,FlutterPlatformView { | |||
| var channel: FlutterMethodChannel | |||
| var cameraFacing: MTBCamera | |||
| var allowedBarcodeTypes: Array<AVMetadataObject.ObjectType> = [] | |||
| 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<String, Any>){ | |||
| 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<Int>, 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<Int>, _ 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)) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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'; | |||
| @@ -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 = <String, BarcodeFormat>{ | |||
| '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<int> 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<StatefulWidget> createState() => _QRViewState(); | |||
| @@ -201,7 +116,8 @@ class _QRViewState extends State<QRView> { | |||
| _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 = <String, BarcodeFormat>{ | |||
| '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<int>; | |||
| 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<Barcode> get scannedDataStream => _scanUpdateController.stream; | |||
| Future<void> _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<int>; | |||
| 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<void> _startScan( | |||
| @@ -267,24 +229,86 @@ class QRViewController { | |||
| return _channel.invokeMethod('startScan'); | |||
| } | |||
| Future<CameraFacing> 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<CameraFacing> 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<bool> 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<void> 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<void> 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<void> resumeCamera() async { | |||
| try { | |||
| await _channel.invokeMethod('resumeCamera'); | |||
| } on PlatformException catch (e) { | |||
| throw CameraException(e.code, e.message); | |||
| } | |||
| } | |||
| Future<void> showNativeAlertDialog() async { | |||
| try { | |||
| await _channel.invokeMethod('showNativeAlertDialog'); | |||
| } on PlatformException catch (e) { | |||
| throw CameraException(e.code, e.message); | |||
| } | |||
| } | |||
| Future<void> setAllowedBarcodeTypes(List<BarcodeFormat> list) async { | |||
| try { | |||
| await _channel.invokeMethod('setAllowedBarcodeFormats', | |||
| list?.map((e) => e.asInt())?.toList() ?? []); | |||
| } on PlatformException catch (e) { | |||
| throw CameraException(e.code, e.message); | |||
| } | |||
| } | |||
| Future<SystemFeatures> getSystemFeatures() async { | |||
| try { | |||
| var features = | |||
| await _channel.invokeMapMethod<String, dynamic>('getSystemFeatures'); | |||
| return SystemFeatures.fromJson(features); | |||
| } on PlatformException catch (e) { | |||
| throw CameraException(e.code, e.message); | |||
| } | |||
| } | |||
| /// Disposes the barcode stream. | |||
| @@ -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<int> rawBytes; | |||
| } | |||
| @@ -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'; | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| enum CameraFacing { | |||
| /// Shows back facing camera. | |||
| back, | |||
| /// Shows front facing camera. | |||
| front | |||
| } | |||
| @@ -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)'; | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| class SystemFeatures { | |||
| SystemFeatures(this.hasFlash, this.hasBackCamera, this.hasFrontCamera); | |||
| factory SystemFeatures.fromJson(Map<String, dynamic> features) => | |||
| SystemFeatures( | |||
| features['hasFlash'] ?? false, | |||
| features['hasBackCamera'] ?? false, | |||
| features['hasFrontCamera'] ?? false); | |||
| final bool hasFlash; | |||
| final bool hasFrontCamera; | |||
| final bool hasBackCamera; | |||
| } | |||
| @@ -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: | |||