p-mazhnik

feat(web): support formats and detectionTimeout arguments

... ... @@ -5,6 +5,7 @@ import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:mobile_scanner/mobile_scanner_web.dart';
import 'package:mobile_scanner/src/barcode_utility.dart';
import 'package:mobile_scanner/src/enums/camera_facing.dart';
import 'package:mobile_scanner/src/objects/barcode.dart';
... ... @@ -91,8 +92,23 @@ class MobileScannerWebPlugin {
};
}
try {
List<BarcodeFormat>? formats;
if (arguments.containsKey('formats')) {
formats = (arguments['formats'] as List)
.cast<int>()
.map((e) => toFormat(e))
.toList();
}
final Duration? detectionTimeout;
if (arguments.containsKey('timeout')) {
detectionTimeout = Duration(milliseconds: arguments['timeout'] as int);
} else {
detectionTimeout = null;
}
await barCodeReader.start(
cameraFacing: cameraFacing,
formats: formats,
detectionTimeout: detectionTimeout,
);
_barCodeStreamSubscription =
... ...
... ... @@ -125,10 +125,10 @@ class MobileScannerController {
arguments['timeout'] = detectionTimeoutMs;
if (formats != null) {
if (Platform.isAndroid) {
arguments['formats'] = formats!.map((e) => e.index).toList();
} else if (Platform.isIOS || Platform.isMacOS) {
if (kIsWeb || Platform.isIOS || Platform.isMacOS) {
arguments['formats'] = formats!.map((e) => e.rawValue).toList();
} else if (Platform.isAndroid) {
arguments['formats'] = formats!.map((e) => e.index).toList();
}
}
arguments['returnImage'] = true;
... ...
import 'dart:html';
import 'package:flutter/material.dart';
import 'package:js/js.dart';
import 'package:mobile_scanner/src/enums/camera_facing.dart';
import 'package:mobile_scanner/src/objects/barcode.dart';
import 'package:mobile_scanner/src/web/media.dart';
abstract class WebBarcodeReaderBase {
/// Timer used to capture frames to be analyzed
final Duration frameInterval;
Duration frameInterval = const Duration(milliseconds: 200);
final DivElement videoContainer;
const WebBarcodeReaderBase({
WebBarcodeReaderBase({
required this.videoContainer,
this.frameInterval = const Duration(milliseconds: 200),
});
bool get isStarted;
... ... @@ -23,6 +23,8 @@ abstract class WebBarcodeReaderBase {
/// Starts streaming video
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
Duration? detectionTimeout,
});
/// Starts scanning QR codes or barcodes
... ... @@ -119,3 +121,14 @@ mixin InternalTorchDetection on InternalStreamCreation {
}
}
}
@JS('Map')
@staticInterop
class JsMap {
external factory JsMap();
}
extension JsMapExt on JsMap {
external void set(dynamic key, dynamic value);
external dynamic get(dynamic key);
}
... ...
... ... @@ -30,9 +30,15 @@ class JsQrCodeReader extends WebBarcodeReaderBase
@override
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
Duration? detectionTimeout,
}) async {
videoContainer.children = [video];
if (detectionTimeout != null) {
frameInterval = detectionTimeout;
}
final stream = await initMediaStream(cameraFacing);
prepareVideoElement(video);
... ...
... ... @@ -47,12 +47,14 @@ extension ResultExt on Result {
/// https://github.com/zxing-js/library/blob/1e9ccb3b6b28d75b9eef866dba196d8937eb4449/src/core/BarcodeFormat.ts#L28
BarcodeFormat get barcodeFormat {
switch (format) {
case 1:
case 0:
return BarcodeFormat.aztec;
case 2:
case 1:
return BarcodeFormat.codebar;
case 3:
case 2:
return BarcodeFormat.code39;
case 3:
return BarcodeFormat.code93;
case 4:
return BarcodeFormat.code128;
case 5:
... ... @@ -83,6 +85,42 @@ extension ResultExt on Result {
}
}
extension ZXingBarcodeFormat on BarcodeFormat {
int get zxingBarcodeFormat {
switch (this) {
case BarcodeFormat.aztec:
return 0;
case BarcodeFormat.codebar:
return 1;
case BarcodeFormat.code39:
return 2;
case BarcodeFormat.code93:
return 3;
case BarcodeFormat.code128:
return 4;
case BarcodeFormat.dataMatrix:
return 5;
case BarcodeFormat.ean8:
return 6;
case BarcodeFormat.ean13:
return 7;
case BarcodeFormat.itf:
return 8;
case BarcodeFormat.pdf417:
return 10;
case BarcodeFormat.qrCode:
return 11;
case BarcodeFormat.upcA:
return 14;
case BarcodeFormat.upcE:
return 15;
case BarcodeFormat.unknown:
case BarcodeFormat.all:
return -1;
}
}
}
typedef BarcodeDetectionCallback = void Function(
Result? result,
dynamic error,
... ... @@ -136,11 +174,7 @@ extension JsZXingBrowserMultiFormatReaderExt
class ZXingBarcodeReader extends WebBarcodeReaderBase
with InternalStreamCreation, InternalTorchDetection {
late final JsZXingBrowserMultiFormatReader _reader =
JsZXingBrowserMultiFormatReader(
null,
frameInterval.inMilliseconds,
);
JsZXingBrowserMultiFormatReader? _reader;
ZXingBarcodeReader({required super.videoContainer});
... ... @@ -150,7 +184,27 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
@override
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
Duration? detectionTimeout,
}) async {
final JsMap? hints;
if (formats != null && !formats.contains(BarcodeFormat.all)) {
hints = JsMap();
final zxingFormats =
formats.map((e) => e.zxingBarcodeFormat).where((e) => e > 0).toList();
// set hint DecodeHintType.POSSIBLE_FORMATS
// https://github.com/zxing-js/library/blob/1e9ccb3b6b28d75b9eef866dba196d8937eb4449/src/core/DecodeHintType.ts#L28
hints.set(2, zxingFormats);
} else {
hints = null;
}
if (detectionTimeout != null) {
frameInterval = detectionTimeout;
}
_reader = JsZXingBrowserMultiFormatReader(
hints,
frameInterval.inMilliseconds,
);
videoContainer.children = [video];
final stream = await initMediaStream(cameraFacing);
... ... @@ -163,7 +217,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
@override
void prepareVideoElement(VideoElement videoSource) {
_reader.prepareVideoElement(videoSource);
_reader?.prepareVideoElement(videoSource);
}
@override
... ... @@ -171,9 +225,9 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
MediaStream stream,
VideoElement videoSource,
) async {
_reader.addVideoSource(videoSource, stream);
_reader.videoElement = videoSource;
_reader.stream = stream;
_reader?.addVideoSource(videoSource, stream);
_reader?.videoElement = videoSource;
_reader?.stream = stream;
localMediaStream = stream;
await videoSource.play();
}
... ... @@ -182,7 +236,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
Stream<Barcode?> detectBarcodeContinuously() {
final controller = StreamController<Barcode?>();
controller.onListen = () async {
_reader.decodeContinuously(
_reader?.decodeContinuously(
video,
allowInterop((result, error) {
if (result != null) {
... ... @@ -192,14 +246,14 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
);
};
controller.onCancel = () {
_reader.stopContinuousDecode();
_reader?.stopContinuousDecode();
};
return controller.stream;
}
@override
Future<void> stop() async {
_reader.reset();
_reader?.reset();
super.stop();
}
}
... ...