p-mazhnik

feat(web): support formats and detectionTimeout arguments

@@ -5,6 +5,7 @@ import 'dart:ui' as ui; @@ -5,6 +5,7 @@ import 'dart:ui' as ui;
5 import 'package:flutter/services.dart'; 5 import 'package:flutter/services.dart';
6 import 'package:flutter_web_plugins/flutter_web_plugins.dart'; 6 import 'package:flutter_web_plugins/flutter_web_plugins.dart';
7 import 'package:mobile_scanner/mobile_scanner_web.dart'; 7 import 'package:mobile_scanner/mobile_scanner_web.dart';
  8 +import 'package:mobile_scanner/src/barcode_utility.dart';
8 import 'package:mobile_scanner/src/enums/camera_facing.dart'; 9 import 'package:mobile_scanner/src/enums/camera_facing.dart';
9 import 'package:mobile_scanner/src/objects/barcode.dart'; 10 import 'package:mobile_scanner/src/objects/barcode.dart';
10 11
@@ -91,8 +92,23 @@ class MobileScannerWebPlugin { @@ -91,8 +92,23 @@ class MobileScannerWebPlugin {
91 }; 92 };
92 } 93 }
93 try { 94 try {
  95 + List<BarcodeFormat>? formats;
  96 + if (arguments.containsKey('formats')) {
  97 + formats = (arguments['formats'] as List)
  98 + .cast<int>()
  99 + .map((e) => toFormat(e))
  100 + .toList();
  101 + }
  102 + final Duration? detectionTimeout;
  103 + if (arguments.containsKey('timeout')) {
  104 + detectionTimeout = Duration(milliseconds: arguments['timeout'] as int);
  105 + } else {
  106 + detectionTimeout = null;
  107 + }
94 await barCodeReader.start( 108 await barCodeReader.start(
95 cameraFacing: cameraFacing, 109 cameraFacing: cameraFacing,
  110 + formats: formats,
  111 + detectionTimeout: detectionTimeout,
96 ); 112 );
97 113
98 _barCodeStreamSubscription = 114 _barCodeStreamSubscription =
@@ -125,10 +125,10 @@ class MobileScannerController { @@ -125,10 +125,10 @@ class MobileScannerController {
125 arguments['timeout'] = detectionTimeoutMs; 125 arguments['timeout'] = detectionTimeoutMs;
126 126
127 if (formats != null) { 127 if (formats != null) {
128 - if (Platform.isAndroid) {  
129 - arguments['formats'] = formats!.map((e) => e.index).toList();  
130 - } else if (Platform.isIOS || Platform.isMacOS) { 128 + if (kIsWeb || Platform.isIOS || Platform.isMacOS) {
131 arguments['formats'] = formats!.map((e) => e.rawValue).toList(); 129 arguments['formats'] = formats!.map((e) => e.rawValue).toList();
  130 + } else if (Platform.isAndroid) {
  131 + arguments['formats'] = formats!.map((e) => e.index).toList();
132 } 132 }
133 } 133 }
134 arguments['returnImage'] = true; 134 arguments['returnImage'] = true;
1 import 'dart:html'; 1 import 'dart:html';
2 2
3 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
  4 +import 'package:js/js.dart';
4 import 'package:mobile_scanner/src/enums/camera_facing.dart'; 5 import 'package:mobile_scanner/src/enums/camera_facing.dart';
5 import 'package:mobile_scanner/src/objects/barcode.dart'; 6 import 'package:mobile_scanner/src/objects/barcode.dart';
6 import 'package:mobile_scanner/src/web/media.dart'; 7 import 'package:mobile_scanner/src/web/media.dart';
7 8
8 abstract class WebBarcodeReaderBase { 9 abstract class WebBarcodeReaderBase {
9 /// Timer used to capture frames to be analyzed 10 /// Timer used to capture frames to be analyzed
10 - final Duration frameInterval; 11 + Duration frameInterval = const Duration(milliseconds: 200);
11 final DivElement videoContainer; 12 final DivElement videoContainer;
12 13
13 - const WebBarcodeReaderBase({ 14 + WebBarcodeReaderBase({
14 required this.videoContainer, 15 required this.videoContainer,
15 - this.frameInterval = const Duration(milliseconds: 200),  
16 }); 16 });
17 17
18 bool get isStarted; 18 bool get isStarted;
@@ -23,6 +23,8 @@ abstract class WebBarcodeReaderBase { @@ -23,6 +23,8 @@ abstract class WebBarcodeReaderBase {
23 /// Starts streaming video 23 /// Starts streaming video
24 Future<void> start({ 24 Future<void> start({
25 required CameraFacing cameraFacing, 25 required CameraFacing cameraFacing,
  26 + List<BarcodeFormat>? formats,
  27 + Duration? detectionTimeout,
26 }); 28 });
27 29
28 /// Starts scanning QR codes or barcodes 30 /// Starts scanning QR codes or barcodes
@@ -119,3 +121,14 @@ mixin InternalTorchDetection on InternalStreamCreation { @@ -119,3 +121,14 @@ mixin InternalTorchDetection on InternalStreamCreation {
119 } 121 }
120 } 122 }
121 } 123 }
  124 +
  125 +@JS('Map')
  126 +@staticInterop
  127 +class JsMap {
  128 + external factory JsMap();
  129 +}
  130 +
  131 +extension JsMapExt on JsMap {
  132 + external void set(dynamic key, dynamic value);
  133 + external dynamic get(dynamic key);
  134 +}
@@ -30,9 +30,15 @@ class JsQrCodeReader extends WebBarcodeReaderBase @@ -30,9 +30,15 @@ class JsQrCodeReader extends WebBarcodeReaderBase
30 @override 30 @override
31 Future<void> start({ 31 Future<void> start({
32 required CameraFacing cameraFacing, 32 required CameraFacing cameraFacing,
  33 + List<BarcodeFormat>? formats,
  34 + Duration? detectionTimeout,
33 }) async { 35 }) async {
34 videoContainer.children = [video]; 36 videoContainer.children = [video];
35 37
  38 + if (detectionTimeout != null) {
  39 + frameInterval = detectionTimeout;
  40 + }
  41 +
36 final stream = await initMediaStream(cameraFacing); 42 final stream = await initMediaStream(cameraFacing);
37 43
38 prepareVideoElement(video); 44 prepareVideoElement(video);
@@ -47,12 +47,14 @@ extension ResultExt on Result { @@ -47,12 +47,14 @@ extension ResultExt on Result {
47 /// https://github.com/zxing-js/library/blob/1e9ccb3b6b28d75b9eef866dba196d8937eb4449/src/core/BarcodeFormat.ts#L28 47 /// https://github.com/zxing-js/library/blob/1e9ccb3b6b28d75b9eef866dba196d8937eb4449/src/core/BarcodeFormat.ts#L28
48 BarcodeFormat get barcodeFormat { 48 BarcodeFormat get barcodeFormat {
49 switch (format) { 49 switch (format) {
50 - case 1: 50 + case 0:
51 return BarcodeFormat.aztec; 51 return BarcodeFormat.aztec;
52 - case 2: 52 + case 1:
53 return BarcodeFormat.codebar; 53 return BarcodeFormat.codebar;
54 - case 3: 54 + case 2:
55 return BarcodeFormat.code39; 55 return BarcodeFormat.code39;
  56 + case 3:
  57 + return BarcodeFormat.code93;
56 case 4: 58 case 4:
57 return BarcodeFormat.code128; 59 return BarcodeFormat.code128;
58 case 5: 60 case 5:
@@ -83,6 +85,42 @@ extension ResultExt on Result { @@ -83,6 +85,42 @@ extension ResultExt on Result {
83 } 85 }
84 } 86 }
85 87
  88 +extension ZXingBarcodeFormat on BarcodeFormat {
  89 + int get zxingBarcodeFormat {
  90 + switch (this) {
  91 + case BarcodeFormat.aztec:
  92 + return 0;
  93 + case BarcodeFormat.codebar:
  94 + return 1;
  95 + case BarcodeFormat.code39:
  96 + return 2;
  97 + case BarcodeFormat.code93:
  98 + return 3;
  99 + case BarcodeFormat.code128:
  100 + return 4;
  101 + case BarcodeFormat.dataMatrix:
  102 + return 5;
  103 + case BarcodeFormat.ean8:
  104 + return 6;
  105 + case BarcodeFormat.ean13:
  106 + return 7;
  107 + case BarcodeFormat.itf:
  108 + return 8;
  109 + case BarcodeFormat.pdf417:
  110 + return 10;
  111 + case BarcodeFormat.qrCode:
  112 + return 11;
  113 + case BarcodeFormat.upcA:
  114 + return 14;
  115 + case BarcodeFormat.upcE:
  116 + return 15;
  117 + case BarcodeFormat.unknown:
  118 + case BarcodeFormat.all:
  119 + return -1;
  120 + }
  121 + }
  122 +}
  123 +
86 typedef BarcodeDetectionCallback = void Function( 124 typedef BarcodeDetectionCallback = void Function(
87 Result? result, 125 Result? result,
88 dynamic error, 126 dynamic error,
@@ -136,11 +174,7 @@ extension JsZXingBrowserMultiFormatReaderExt @@ -136,11 +174,7 @@ extension JsZXingBrowserMultiFormatReaderExt
136 174
137 class ZXingBarcodeReader extends WebBarcodeReaderBase 175 class ZXingBarcodeReader extends WebBarcodeReaderBase
138 with InternalStreamCreation, InternalTorchDetection { 176 with InternalStreamCreation, InternalTorchDetection {
139 - late final JsZXingBrowserMultiFormatReader _reader =  
140 - JsZXingBrowserMultiFormatReader(  
141 - null,  
142 - frameInterval.inMilliseconds,  
143 - ); 177 + JsZXingBrowserMultiFormatReader? _reader;
144 178
145 ZXingBarcodeReader({required super.videoContainer}); 179 ZXingBarcodeReader({required super.videoContainer});
146 180
@@ -150,7 +184,27 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase @@ -150,7 +184,27 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
150 @override 184 @override
151 Future<void> start({ 185 Future<void> start({
152 required CameraFacing cameraFacing, 186 required CameraFacing cameraFacing,
  187 + List<BarcodeFormat>? formats,
  188 + Duration? detectionTimeout,
153 }) async { 189 }) async {
  190 + final JsMap? hints;
  191 + if (formats != null && !formats.contains(BarcodeFormat.all)) {
  192 + hints = JsMap();
  193 + final zxingFormats =
  194 + formats.map((e) => e.zxingBarcodeFormat).where((e) => e > 0).toList();
  195 + // set hint DecodeHintType.POSSIBLE_FORMATS
  196 + // https://github.com/zxing-js/library/blob/1e9ccb3b6b28d75b9eef866dba196d8937eb4449/src/core/DecodeHintType.ts#L28
  197 + hints.set(2, zxingFormats);
  198 + } else {
  199 + hints = null;
  200 + }
  201 + if (detectionTimeout != null) {
  202 + frameInterval = detectionTimeout;
  203 + }
  204 + _reader = JsZXingBrowserMultiFormatReader(
  205 + hints,
  206 + frameInterval.inMilliseconds,
  207 + );
154 videoContainer.children = [video]; 208 videoContainer.children = [video];
155 209
156 final stream = await initMediaStream(cameraFacing); 210 final stream = await initMediaStream(cameraFacing);
@@ -163,7 +217,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase @@ -163,7 +217,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
163 217
164 @override 218 @override
165 void prepareVideoElement(VideoElement videoSource) { 219 void prepareVideoElement(VideoElement videoSource) {
166 - _reader.prepareVideoElement(videoSource); 220 + _reader?.prepareVideoElement(videoSource);
167 } 221 }
168 222
169 @override 223 @override
@@ -171,9 +225,9 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase @@ -171,9 +225,9 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
171 MediaStream stream, 225 MediaStream stream,
172 VideoElement videoSource, 226 VideoElement videoSource,
173 ) async { 227 ) async {
174 - _reader.addVideoSource(videoSource, stream);  
175 - _reader.videoElement = videoSource;  
176 - _reader.stream = stream; 228 + _reader?.addVideoSource(videoSource, stream);
  229 + _reader?.videoElement = videoSource;
  230 + _reader?.stream = stream;
177 localMediaStream = stream; 231 localMediaStream = stream;
178 await videoSource.play(); 232 await videoSource.play();
179 } 233 }
@@ -182,7 +236,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase @@ -182,7 +236,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
182 Stream<Barcode?> detectBarcodeContinuously() { 236 Stream<Barcode?> detectBarcodeContinuously() {
183 final controller = StreamController<Barcode?>(); 237 final controller = StreamController<Barcode?>();
184 controller.onListen = () async { 238 controller.onListen = () async {
185 - _reader.decodeContinuously( 239 + _reader?.decodeContinuously(
186 video, 240 video,
187 allowInterop((result, error) { 241 allowInterop((result, error) {
188 if (result != null) { 242 if (result != null) {
@@ -192,14 +246,14 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase @@ -192,14 +246,14 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
192 ); 246 );
193 }; 247 };
194 controller.onCancel = () { 248 controller.onCancel = () {
195 - _reader.stopContinuousDecode(); 249 + _reader?.stopContinuousDecode();
196 }; 250 };
197 return controller.stream; 251 return controller.stream;
198 } 252 }
199 253
200 @override 254 @override
201 Future<void> stop() async { 255 Future<void> stop() async {
202 - _reader.reset(); 256 + _reader?.reset();
203 super.stop(); 257 super.stop();
204 } 258 }
205 } 259 }