Committed by
GitHub
Merge pull request #42 from juliansteenbakker/barcode-formats-allowed
Barcode formats allowed
Showing
8 changed files
with
89 additions
and
86 deletions
| @@ -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,7 +171,16 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -171,7 +171,16 @@ 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 | - | 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) | ||
| 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 |
| 177 | 186 | ||
| @@ -356,5 +365,9 @@ class MapArgumentReader { | @@ -356,5 +365,9 @@ class MapArgumentReader { | ||
| 356 | func stringArray(key: String) -> [String]? { | 365 | func stringArray(key: String) -> [String]? { |
| 357 | return args?[key] as? [String] | 366 | return args?[key] as? [String] |
| 358 | } | 367 | } |
| 368 | + | ||
| 369 | + func intArray(key: String) -> [Int]? { | ||
| 370 | + return args?[key] as? [Int] | ||
| 371 | + } | ||
| 359 | 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. |
-
Please register or login to post a comment