Julian Steenbakker
Committed by GitHub

Merge pull request #42 from juliansteenbakker/barcode-formats-allowed

Barcode formats allowed
@@ -3,6 +3,9 @@ You can provide a path to controller.analyzeImage(path) in order to scan a local @@ -3,6 +3,9 @@ You can provide a path to controller.analyzeImage(path) in order to scan a local
3 Check out the example app to see how you can use the image_picker plugin to retrieve a photo from 3 Check out the example app to see how you can use the image_picker plugin to retrieve a photo from
4 the gallery. Please keep in mind that this feature is only supported on Android and iOS. 4 the gallery. Please keep in mind that this feature is only supported on Android and iOS.
5 5
  6 +Another feature that has been added is a format selector!
  7 +Just keep in mind that iOS for now only supports 1 selected barcode.
  8 +
6 ## 0.1.3 9 ## 0.1.3
7 * Fixed crash after asking permission. [#29](https://github.com/juliansteenbakker/mobile_scanner/issues/29) 10 * Fixed crash after asking permission. [#29](https://github.com/juliansteenbakker/mobile_scanner/issues/29)
8 * Upgraded cameraX from 1.1.0-beta01 to 1.1.0-beta02 11 * Upgraded cameraX from 1.1.0-beta01 to 1.1.0-beta02
1 package dev.steenbakker.mobile_scanner 1 package dev.steenbakker.mobile_scanner
2 2
3 import com.google.mlkit.vision.barcode.BarcodeScannerOptions 3 import com.google.mlkit.vision.barcode.BarcodeScannerOptions
4 -import java.util.ArrayList  
5 4
6 enum class BarcodeFormats(val intValue: Int) { 5 enum class BarcodeFormats(val intValue: Int) {
  6 + UNKNOWN(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UNKNOWN),
7 ALL_FORMATS(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ALL_FORMATS), CODE_128(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_128), CODE_39( 7 ALL_FORMATS(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ALL_FORMATS), CODE_128(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_128), CODE_39(
8 com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_39 8 com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_39
9 ), 9 ),
@@ -17,76 +17,4 @@ enum class BarcodeFormats(val intValue: Int) { @@ -17,76 +17,4 @@ enum class BarcodeFormats(val intValue: Int) {
17 com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_E 17 com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_E
18 ), 18 ),
19 PDF417(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_PDF417), AZTEC(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_AZTEC); 19 PDF417(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_PDF417), AZTEC(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_AZTEC);
20 -  
21 - companion object {  
22 - private var formatsMap: MutableMap<String, Int>? = null  
23 -  
24 - /**  
25 - * Return the integer value resuling from OR-ing all of the values  
26 - * of the supplied strings.  
27 - *  
28 - *  
29 - * Note that if ALL_FORMATS is defined as well as other values, ALL_FORMATS  
30 - * will be ignored (following how it would work with just OR-ing the ints).  
31 - *  
32 - * @param strings - list of strings representing the various formats  
33 - * @return integer value corresponding to OR of all the values.  
34 - */  
35 - fun intFromStringList(strings: List<String>?): Int {  
36 - if (strings == null) return ALL_FORMATS.intValue  
37 - var `val` = 0  
38 - for (string in strings) {  
39 - val asInt = formatsMap!![string]  
40 - if (asInt != null) {  
41 - `val` = `val` or asInt  
42 - }  
43 - }  
44 - return `val`  
45 - }  
46 -  
47 - fun optionsFromStringList(strings: List<String>?): BarcodeScannerOptions {  
48 - if (strings == null) {  
49 - return BarcodeScannerOptions.Builder().setBarcodeFormats(ALL_FORMATS.intValue)  
50 - .build()  
51 - }  
52 - val ints: MutableList<Int> = ArrayList(strings.size)  
53 - run {  
54 - var i = 0  
55 - val l = strings.size  
56 - while (i < l) {  
57 - val integer =  
58 - formatsMap!![strings[i]]  
59 - if (integer != null) {  
60 - ints.add(integer)  
61 - }  
62 - ++i  
63 - }  
64 - }  
65 - if (ints.size == 0) {  
66 - return BarcodeScannerOptions.Builder().setBarcodeFormats(ALL_FORMATS.intValue)  
67 - .build()  
68 - }  
69 - if (ints.size == 1) {  
70 - return BarcodeScannerOptions.Builder().setBarcodeFormats(ints[0]).build()  
71 - }  
72 - val first = ints[0]  
73 - val rest = IntArray(ints.size - 1)  
74 - var i = 0  
75 - for (e in ints.subList(1, ints.size)) {  
76 - rest[i++] = e  
77 - }  
78 - return BarcodeScannerOptions.Builder()  
79 - .setBarcodeFormats(first, *rest).build()  
80 - }  
81 -  
82 - init {  
83 - val values = values()  
84 - formatsMap =  
85 - HashMap<String, Int>(values.size * 4 / 3)  
86 - for (value in values) {  
87 - formatsMap!![value.name] =  
88 - value.intValue  
89 - }  
90 - }  
91 - }  
92 } 20 }
@@ -128,11 +128,18 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -128,11 +128,18 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
128 val facing: Int = call.argument<Int>("facing") ?: 0 128 val facing: Int = call.argument<Int>("facing") ?: 0
129 val ratio: Int? = call.argument<Int>("ratio") 129 val ratio: Int? = call.argument<Int>("ratio")
130 val torch: Boolean = call.argument<Boolean>("torch") ?: false 130 val torch: Boolean = call.argument<Boolean>("torch") ?: false
131 - val formatStrings: List<String>? = call.argument<List<String>>("formats") 131 + val formats: List<Int>? = call.argument<List<Int>>("formats")
132 132
133 - if (formatStrings != null) {  
134 - val options: BarcodeScannerOptions = BarcodeFormats.optionsFromStringList(formatStrings)  
135 - scanner = BarcodeScanning.getClient(options) 133 + if (formats != null) {
  134 + val formatsList: MutableList<Int> = mutableListOf()
  135 + for (index in formats) {
  136 + formatsList.add(BarcodeFormats.values()[index].intValue)
  137 + }
  138 + scanner = if (formatsList.size == 1) {
  139 + BarcodeScanning.getClient( BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first()).build());
  140 + } else {
  141 + BarcodeScanning.getClient( BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first(), *formatsList.subList(1, formatsList.size).toIntArray() ).build());
  142 + }
136 } 143 }
137 144
138 val future = ProcessCameraProvider.getInstance(activity) 145 val future = ProcessCameraProvider.getInstance(activity)
@@ -17,6 +17,7 @@ class _BarcodeScannerWithControllerState @@ -17,6 +17,7 @@ class _BarcodeScannerWithControllerState
17 17
18 MobileScannerController controller = MobileScannerController( 18 MobileScannerController controller = MobileScannerController(
19 torchEnabled: true, 19 torchEnabled: true,
  20 + // formats: [BarcodeFormat.qrCode]
20 // facing: CameraFacing.front, 21 // facing: CameraFacing.front,
21 ); 22 );
22 23
@@ -26,7 +26,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -26,7 +26,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
26 var analyzing: Bool = false 26 var analyzing: Bool = false
27 var position = AVCaptureDevice.Position.back 27 var position = AVCaptureDevice.Position.back
28 28
29 - let scanner = BarcodeScanner.barcodeScanner() 29 + var scanner = BarcodeScanner.barcodeScanner()
30 30
31 public static func register(with registrar: FlutterPluginRegistrar) { 31 public static func register(with registrar: FlutterPluginRegistrar) {
32 let instance = SwiftMobileScannerPlugin(registrar.textures()) 32 let instance = SwiftMobileScannerPlugin(registrar.textures())
@@ -171,6 +171,15 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -171,6 +171,15 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
171 // let ratio: Int = argReader.int(key: "ratio") 171 // let ratio: Int = argReader.int(key: "ratio")
172 let torch: Bool = argReader.bool(key: "torch") ?? false 172 let torch: Bool = argReader.bool(key: "torch") ?? false
173 let facing: Int = argReader.int(key: "facing") ?? 1 173 let facing: Int = argReader.int(key: "facing") ?? 1
  174 + let formats: Array = argReader.intArray(key: "formats") ?? []
  175 +
  176 + let formatList: NSMutableArray = []
  177 + for index in formats {
  178 + formatList.add(BarcodeFormat(rawValue: index))
  179 + }
  180 +
  181 + let barcodeOptions = BarcodeScannerOptions(formats: formatList.firstObject as! BarcodeFormat)
  182 + scanner = BarcodeScanner.barcodeScanner(options: barcodeOptions)
174 183
175 // Set the camera to use 184 // Set the camera to use
176 position = facing == 0 ? AVCaptureDevice.Position.front : .back 185 position = facing == 0 ? AVCaptureDevice.Position.front : .back
@@ -357,4 +366,8 @@ class MapArgumentReader { @@ -357,4 +366,8 @@ class MapArgumentReader {
357 return args?[key] as? [String] 366 return args?[key] as? [String]
358 } 367 }
359 368
  369 + func intArray(key: String) -> [Int]? {
  370 + return args?[key] as? [Int]
  371 + }
  372 +
360 } 373 }
@@ -24,12 +24,9 @@ class MobileScanner extends StatefulWidget { @@ -24,12 +24,9 @@ class MobileScanner extends StatefulWidget {
24 final BoxFit fit; 24 final BoxFit fit;
25 25
26 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized. 26 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized.
27 - const MobileScanner({  
28 - Key? key,  
29 - this.onDetect,  
30 - this.controller,  
31 - this.fit = BoxFit.cover,  
32 - }) : super(key: key); 27 + const MobileScanner(
  28 + {Key? key, this.onDetect, this.controller, this.fit = BoxFit.cover})
  29 + : super(key: key);
33 30
34 @override 31 @override
35 State<MobileScanner> createState() => _MobileScannerState(); 32 State<MobileScanner> createState() => _MobileScannerState();
1 import 'dart:async'; 1 import 'dart:async';
  2 +import 'dart:io';
2 3
3 import 'package:flutter/cupertino.dart'; 4 import 'package:flutter/cupertino.dart';
4 import 'package:flutter/services.dart'; 5 import 'package:flutter/services.dart';
@@ -43,6 +44,11 @@ class MobileScannerController { @@ -43,6 +44,11 @@ class MobileScannerController {
43 final Ratio? ratio; 44 final Ratio? ratio;
44 final bool? torchEnabled; 45 final bool? torchEnabled;
45 46
  47 + /// If provided, the scanner will only detect those specific formats.
  48 + ///
  49 + /// WARNING: On iOS, only 1 format is supported.
  50 + final List<BarcodeFormat>? formats;
  51 +
46 CameraFacing facing; 52 CameraFacing facing;
47 bool hasTorch = false; 53 bool hasTorch = false;
48 late StreamController<Barcode> barcodesController; 54 late StreamController<Barcode> barcodesController;
@@ -50,7 +56,10 @@ class MobileScannerController { @@ -50,7 +56,10 @@ class MobileScannerController {
50 Stream<Barcode> get barcodes => barcodesController.stream; 56 Stream<Barcode> get barcodes => barcodesController.stream;
51 57
52 MobileScannerController( 58 MobileScannerController(
53 - {this.facing = CameraFacing.back, this.ratio, this.torchEnabled}) { 59 + {this.facing = CameraFacing.back,
  60 + this.ratio,
  61 + this.torchEnabled,
  62 + this.formats}) {
54 // In case a new instance is created before calling dispose() 63 // In case a new instance is created before calling dispose()
55 if (_controllerHashcode != null) { 64 if (_controllerHashcode != null) {
56 stop(); 65 stop();
@@ -138,6 +147,14 @@ class MobileScannerController { @@ -138,6 +147,14 @@ class MobileScannerController {
138 if (ratio != null) arguments['ratio'] = ratio; 147 if (ratio != null) arguments['ratio'] = ratio;
139 if (torchEnabled != null) arguments['torch'] = torchEnabled; 148 if (torchEnabled != null) arguments['torch'] = torchEnabled;
140 149
  150 + if (formats != null) {
  151 + if (Platform.isAndroid) {
  152 + arguments['formats'] = formats!.map((e) => e.index).toList();
  153 + } else if (Platform.isIOS || Platform.isMacOS) {
  154 + arguments['formats'] = formats!.map((e) => e.rawValue).toList();
  155 + }
  156 + }
  157 +
141 // Start the camera with arguments 158 // Start the camera with arguments
142 Map<String, dynamic>? startResult = {}; 159 Map<String, dynamic>? startResult = {};
143 try { 160 try {
@@ -553,6 +553,43 @@ enum BarcodeFormat { @@ -553,6 +553,43 @@ enum BarcodeFormat {
553 aztec, 553 aztec,
554 } 554 }
555 555
  556 +extension BarcodeValue on BarcodeFormat {
  557 + int get rawValue {
  558 + switch (this) {
  559 + case BarcodeFormat.unknown:
  560 + return -1;
  561 + case BarcodeFormat.all:
  562 + return 0;
  563 + case BarcodeFormat.code128:
  564 + return 1;
  565 + case BarcodeFormat.code39:
  566 + return 2;
  567 + case BarcodeFormat.code93:
  568 + return 4;
  569 + case BarcodeFormat.codebar:
  570 + return 8;
  571 + case BarcodeFormat.dataMatrix:
  572 + return 16;
  573 + case BarcodeFormat.ean13:
  574 + return 32;
  575 + case BarcodeFormat.ean8:
  576 + return 64;
  577 + case BarcodeFormat.itf:
  578 + return 128;
  579 + case BarcodeFormat.qrCode:
  580 + return 256;
  581 + case BarcodeFormat.upcA:
  582 + return 512;
  583 + case BarcodeFormat.upcE:
  584 + return 1024;
  585 + case BarcodeFormat.pdf417:
  586 + return 2048;
  587 + case BarcodeFormat.aztec:
  588 + return 4096;
  589 + }
  590 + }
  591 +}
  592 +
556 /// Address type constants. 593 /// Address type constants.
557 enum AddressType { 594 enum AddressType {
558 /// Unknown address type. 595 /// Unknown address type.