Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ Improvements:
* [MacOS] Added support for `analyzeImage`.
* [MacOS] Added a Privacy Manifest.
* [web] Added the size information to barcode results.
* [web] Added the video output size information to barcode capture.
* Added support for barcode formats to image analysis.
* Updated the scanner to report any scanning errors that were encountered during processing.
* Introduced a new getter `hasCameraPermission` for the `MobileScannerState`.
* Fixed a bug in the lifecycle handling sample. Now instead of checking `isInitialized`,
the sample recommends using `hasCameraPermission`, which also guards against camera permission errors.
* Updated the behavior of `returnImage` to only determine if the camera output bytes should be sent.
* Updated the behavior of `BarcodeCapture.size` to always be provided when available, regardless of `returnImage`.

Bugs fixed:
* Fixed a bug that would cause the scanner to emit an error when it was already started. Now it ignores any calls to start while it is starting.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ class MobileScanner(
}

if (!returnImage) {
mobileScannerCallback(barcodeMap, null, null, null)
mobileScannerCallback(
barcodeMap,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When not returning the created bitmap, use the available dimensions from the input image, which are the same.

null,
mediaImage.width,
mediaImage.height)
return@addOnSuccessListener
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,17 @@ class MobileScannerHandler(
private var analyzerResult: MethodChannel.Result? = null

private val callback: MobileScannerCallback = { barcodes: List<Map<String, Any?>>, image: ByteArray?, width: Int?, height: Int? ->
if (image != null) {
barcodeHandler.publishEvent(mapOf(
"name" to "barcode",
"data" to barcodes,
"image" to mapOf(
"bytes" to image,
"width" to width?.toDouble(),
"height" to height?.toDouble(),
)
))
} else {
barcodeHandler.publishEvent(mapOf(
"name" to "barcode",
"data" to barcodes
))
}
barcodeHandler.publishEvent(mapOf(
"name" to "barcode",
"data" to barcodes,
// The image dimensions are always provided.
// The image bytes are only non-null when `returnImage` is true.
"image" to mapOf(
"bytes" to image,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The width / height were nullable, which could have been changed. However, the Dart code already handles null, so I just left it as-is, to reduce the diff here.

"width" to width?.toDouble(),
"height" to height?.toDouble(),
)
))
}

private val errorCallback: MobileScannerErrorCallback = {error: String ->
Expand Down
20 changes: 8 additions & 12 deletions ios/Classes/MobileScannerPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
return
}

if (!MobileScannerPlugin.returnImage) {
barcodeHandler.publishEvent([
"name": "barcode",
"data": barcodesMap,
])
return
}
// The image dimensions are always provided.
// The image bytes are only non-null when `returnImage` is true.
let imageData: [String: Any?] = [
Copy link
Collaborator Author

@navaronbracke navaronbracke Oct 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making the bytes conditional started giving a warning with nullable Any, so I moved it to a separate dict here to fix that.

"bytes": MobileScannerPlugin.returnImage ? FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!) : nil,
"width": image.size.width,
"height": image.size.height,
]

barcodeHandler.publishEvent([
"name": "barcode",
"data": barcodesMap,
"image": [
"bytes": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!),
"width": image.size.width,
"height": image.size.height,
],
"image": imageData,
])
}, torchModeChangeCallback: { torchState in
barcodeHandler.publishEvent(["name": "torchState", "data": torchState])
Expand Down
3 changes: 1 addition & 2 deletions lib/src/mobile_scanner_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
/// If this is empty, all supported formats are detected.
final List<BarcodeFormat> formats;

/// Whether scanned barcodes should contain the image
/// that is embedded into the barcode.
/// Whether the [BarcodeCapture.image] bytes should be provided.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wording was incorrect, so I adjusted it. There is no support for returning images that are embedded in the center of some specific QR codes. (i.e. little square logo's)

///
/// If this is false, [BarcodeCapture.image] will always be null.
///
Expand Down
4 changes: 1 addition & 3 deletions lib/src/objects/barcode_capture.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ class BarcodeCapture {
/// This is the data that was used to detect the available [barcodes], the input [image] and the [size].
final Object? raw;

/// The size of the input [image].
///
/// If [image] is null, this will be [Size.zero].
/// The size of the camera input [image].
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted the wording to match the new behavior.

final Size size;
}
1 change: 1 addition & 0 deletions lib/src/web/mobile_scanner_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class MobileScannerWeb extends MobileScannerPlatform {
final JSArray<JSString>? facingModes = capabilities.facingModeNullable;

// TODO: this is an empty array on MacOS Chrome, where there is no facing mode, but one, user facing camera.
// We might be able to add a workaround, using the label of the video track.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to revisit the broken check for the transform on the web in the future. Just left a breadcrumb for myself here.

// Facing mode is not supported by this track, do nothing.
if (facingModes == null || facingModes.toDart.isEmpty) {
return;
Expand Down
1 change: 1 addition & 0 deletions lib/src/web/zxing/zxing_barcode_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ final class ZXingBarcodeReader extends BarcodeReader {
controller.add(
BarcodeCapture(
barcodes: [result.toBarcode],
size: videoSize,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already had this information in the getter implementation, so I just forwarded it. This returns Size.zero if the camera output is not available.

),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,22 +156,26 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
})

DispatchQueue.main.async {
if (!MobileScannerPlugin.returnImage) {
guard let image = cgImage else {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a guard-let to avoid ! in the cgImage below, makes things look better

self?.sink?([
"name": "barcode",
"data": barcodes.map({ $0.toMap() }),
])
return
}


// The image dimensions are always provided.
// The image bytes are only non-null when `returnImage` is true.
let imageData: [String: Any?] = [
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like on iOS, I moved the dict to a separate var to fix a warning.

"bytes": MobileScannerPlugin.returnImage ? FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!) : nil,
"width": Double(image.width),
"height": Double(image.height),
]

self?.sink?([
"name": "barcode",
"data": barcodes.map({ $0.toMap() }),
"image": cgImage == nil ? nil : [
"bytes": FlutterStandardTypedData(bytes: cgImage!.jpegData(compressionQuality: 0.8)!),
"width": Double(cgImage!.width),
"height": Double(cgImage!.height),
],
"image": imageData,
])
}
})
Expand Down
Loading