Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c179310
sort Barcode.data keys alphabetically on Android; add missing boundin…
navaronbracke Sep 4, 2024
c83dd61
fix discrepancy for barcode capture image size on Android
navaronbracke Sep 4, 2024
28be45d
fix docs for barcode capture
navaronbracke Sep 4, 2024
0856a59
add size to barcode
navaronbracke Sep 4, 2024
fa763a2
update the image parsing from the method channel
navaronbracke Sep 4, 2024
48d83fe
adjust raw value on Android & iOS
navaronbracke Sep 4, 2024
fa23d7d
clean up some redundant statements in ios/MobileScanner.swift
navaronbracke Sep 4, 2024
f772f29
fix early returns in iOS implementation of mobile scanner
navaronbracke Sep 4, 2024
2e9e246
fix return image being ignored on iOS
navaronbracke Sep 4, 2024
e503f0a
sort barcode map keys for iOS; add barcode size on iOS
navaronbracke Sep 4, 2024
2f7de30
remove unused code in MacOS impl
navaronbracke Sep 4, 2024
02e0102
add jpeg data extension & toMap extension
navaronbracke Sep 4, 2024
cc23a80
set return image flag on macos
navaronbracke Sep 4, 2024
cfc3643
use early returns on MacOS; fix MacOS from only returning a single ba…
navaronbracke Sep 4, 2024
9209f59
let MacOS also use the same barcode format parsing as iOS/Android
navaronbracke Sep 4, 2024
3bb18bd
update changelog & version
navaronbracke Sep 4, 2024
1d1b0f9
update MacOS example project
navaronbracke Sep 5, 2024
3450814
fix type error
navaronbracke Sep 5, 2024
6dea0d3
fix double values for MacOS
navaronbracke Sep 5, 2024
75418f2
update ios example app
navaronbracke Sep 5, 2024
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
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## 5.2.2

Improvements:
* [MacOS] Adds Swift Package Manager support.
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 was added by a previous PR, but wasn't yet in the changelog.

* [MacOS] Adds support for `returnImage`.
* Added a new `size` property to `Barcode`, that denotes the bounding box of the barcode.

Bugs fixed:
* Fixed some documentation errors for the `size` and `image` of `BarcodeCapture`.
* [iOS] Fixed a bug with `returnImage`.
* [Android/iOS] Adjusted the raw barcode scan value to pass the raw event data, like on MacOS.

## 5.2.1

* Updates the `package:web` dependency to use a version range.
Expand Down Expand Up @@ -44,7 +56,7 @@ Improvements:
This major release contains all the changes from the 5.0.0 beta releases, along with the following changes:

Improvements:
- [Android] Remove the Kotlin Standard Library from the dependencies, as it is automatically included in Kotlin 1.4+
* [Android] Remove the Kotlin Standard Library from the dependencies, as it is automatically included in Kotlin 1.4+

## 5.0.0-beta.3
**BREAKING CHANGES:**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ class MobileScannerHandler(
barcodeHandler.publishEvent(mapOf(
"name" to "barcode",
"data" to barcodes,
"image" to image,
"width" to width!!.toDouble(),
Copy link
Collaborator Author

@navaronbracke navaronbracke Sep 4, 2024

Choose a reason for hiding this comment

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

Since the width/height is only for the output image data, I put them in a Map for the image data.

I also added safe access to the width / height. The method channel implementation handles null width/height by falling back to Size.zero

"height" to height!!.toDouble()
"image" to mapOf(
"bytes" to image,
"width" to width?.toDouble(),
"height" to height?.toDouble(),
)
))
} else {
barcodeHandler.publishEvent(mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,22 @@ fun Image.toByteArray(): ByteArray {

val Barcode.data: Map<String, Any?>
get() = mapOf(
"corners" to cornerPoints?.map { corner -> corner.data }, "format" to format,
"rawBytes" to rawBytes, "rawValue" to rawValue, "type" to valueType,
"calendarEvent" to calendarEvent?.data, "contactInfo" to contactInfo?.data,
"driverLicense" to driverLicense?.data, "email" to email?.data,
"geoPoint" to geoPoint?.data, "phone" to phone?.data, "sms" to sms?.data,
"url" to url?.data, "wifi" to wifi?.data, "displayValue" to displayValue
"calendarEvent" to calendarEvent?.data,
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 sorted these properties alphabetically, to make it easier to follow. The line "size" to boundingBox?.size, is new.

"contactInfo" to contactInfo?.data,
"corners" to cornerPoints?.map { corner -> corner.data },
"displayValue" to displayValue,
"driverLicense" to driverLicense?.data,
"email" to email?.data,
"format" to format,
"geoPoint" to geoPoint?.data,
"phone" to phone?.data,
"rawBytes" to rawBytes,
"rawValue" to rawValue,
"size" to boundingBox?.size,
"sms" to sms?.data,
"type" to valueType,
"url" to url?.data,
"wifi" to wifi?.data,
)

private val Point.data: Map<String, Double>
Expand Down Expand Up @@ -92,4 +102,14 @@ private val Barcode.UrlBookmark.data: Map<String, Any?>
get() = mapOf("title" to title, "url" to url)

private val Barcode.WiFi.data: Map<String, Any?>
get() = mapOf("encryptionType" to encryptionType, "password" to password, "ssid" to ssid)
get() = mapOf("encryptionType" to encryptionType, "password" to password, "ssid" to ssid)

private val Rect.size: Map<String, Any?>
get() {
// Rect.isValid can't be accessed for some reason, so just do the check manually.
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 inspecting the source code of android.graphics.Rect, there was an isValid() method, but I could not access it. So I just took the check from there.

if (left <= right && top <= bottom) {
return mapOf("width" to width().toDouble(), "height" to height().toDouble())
}

return emptyMap()
}
2 changes: 1 addition & 1 deletion example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UIKit
import Flutter

@UIApplicationMain
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

UIApplicationMain is deprecated. This was updated by the Flutter tool project migrators.

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
2 changes: 1 addition & 1 deletion example/macos/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Cocoa
import FlutterMacOS

@NSApplicationMain
@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
Expand Down
21 changes: 4 additions & 17 deletions ios/Classes/MobileScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
/// Barcode scanner for results
var scanner = BarcodeScanner.barcodeScanner()

/// Return image buffer with the Barcode event
var returnImage: Bool = false
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 was moved


/// Default position of camera
var videoPosition: AVCaptureDevice.Position = AVCaptureDevice.Position.back

Expand Down Expand Up @@ -127,7 +124,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
/// Gets called when a new image is added to the buffer
public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
print("Failed to get image buffer from sample buffer.")
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 print doesn't add any value

return
}
latestBuffer = imageBuffer
Expand Down Expand Up @@ -160,7 +156,9 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega

if (error == nil && barcodesString != nil && newScannedBarcodes != nil && barcodesString!.elementsEqual(newScannedBarcodes!)) {
return
} else if (newScannedBarcodes?.isEmpty == false) {
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Early return instead of else


if (newScannedBarcodes?.isEmpty == false) {
barcodesString = newScannedBarcodes
}
}
Expand All @@ -171,7 +169,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
}

/// Start scanning for barcodes
func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws {
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 parameter is no longer needed

func start(barcodeScannerOptions: BarcodeScannerOptions?, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws {
self.detectionSpeed = detectionSpeed
if (device != nil || captureSession != nil) {
throw MobileScannerError.alreadyStarted
Expand Down Expand Up @@ -446,17 +444,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega

var barcodesString: Array<String?>?

// /// Convert image buffer to jpeg
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unused

// private func ciImageToJpeg(ciImage: CIImage) -> Data {
//
// // let ciImage = CIImage(cvPixelBuffer: latestBuffer)
// let context:CIContext = CIContext.init(options: nil)
// let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)!
// let uiImage:UIImage = UIImage(cgImage: cgImage, scale: 1, orientation: UIImage.Orientation.up)
//
// return uiImage.jpegData(compressionQuality: 0.8)!
// }

/// Rotates images accordingly
func imageOrientation(
deviceOrientation: UIDeviceOrientation,
Expand Down
73 changes: 52 additions & 21 deletions ios/Classes/MobileScannerPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {

/// The handler sends all information via an event channel back to Flutter
private let barcodeHandler: BarcodeHandler

/// Whether to return the input image with the barcode event.
/// This is static to avoid accessing `self` in the callback in the constructor.
private static var returnImage: Bool = false

/// The points for the scan window.
static var scanWindow: [CGFloat]?
Expand All @@ -37,24 +41,48 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {

init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) {
self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in
if barcodes != nil {
let barcodesMap: [Any?] = barcodes!.compactMap { barcode in
if (MobileScannerPlugin.scanWindow != nil) {
if (MobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) {
return barcode.data
} else {
return nil
}
} else {
return barcode.data
}
if error != nil {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Rewriting to use early returns

barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription])
return
}

if barcodes == nil {
return
}

let barcodesMap: [Any?] = barcodes!.compactMap { barcode in
if (MobileScannerPlugin.scanWindow == nil) {
return barcode.data
}
if (!barcodesMap.isEmpty) {
barcodeHandler.publishEvent(["name": "barcode", "data": barcodesMap, "image": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!), "width": image.size.width, "height": image.size.height])

if (MobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) {
return barcode.data
}
} else if (error != nil){
barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription])

return nil
}

if (barcodesMap.isEmpty) {
return
}

if (!MobileScannerPlugin.returnImage) {
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 is new. Previously we would always send the image data, which is incorrect.

barcodeHandler.publishEvent([
"name": "barcode",
"data": barcodesMap,
])
return
}

barcodeHandler.publishEvent([
"name": "barcode",
"data": barcodesMap,
"image": [
"bytes": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!),
"width": image.size.width,
"height": image.size.height,
],
])
}, torchModeChangeCallback: { torchState in
barcodeHandler.publishEvent(["name": "torchState", "data": torchState])
}, zoomScaleChangeCallback: { zoomScale in
Expand Down Expand Up @@ -104,6 +132,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0
let timeoutMs: Int = (call.arguments as! Dictionary<String, Any?>)["timeout"] as? Int ?? 0
self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000)
MobileScannerPlugin.returnImage = returnImage

let formatList = formats.map { format in return BarcodeFormat(rawValue: format)}
var barcodeOptions: BarcodeScannerOptions? = nil
Expand All @@ -120,7 +149,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)!

do {
try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in
try mobileScanner.start(barcodeScannerOptions: barcodeOptions, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in
DispatchQueue.main.async {
result([
"textureId": parameters.textureId,
Expand Down Expand Up @@ -257,12 +286,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
DispatchQueue.main.async {
result(nil)
}
} else {
let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data }

DispatchQueue.main.async {
result(["name": "barcode", "data": barcodesMap])
}
return
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Early return

}

let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data }

DispatchQueue.main.async {
result(["name": "barcode", "data": barcodesMap])
}
})
}
Expand Down
23 changes: 21 additions & 2 deletions ios/Classes/MobileScannerUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,27 @@ extension UIDeviceOrientation {

extension Barcode {
var data: [String: Any?] {
let corners = cornerPoints?.map({$0.cgPointValue.data})
return ["corners": corners, "format": format.rawValue, "rawBytes": rawData, "rawValue": rawValue, "type": valueType.rawValue, "calendarEvent": calendarEvent?.data, "contactInfo": contactInfo?.data, "driverLicense": driverLicense?.data, "email": email?.data, "geoPoint": geoPoint?.data, "phone": phone?.data, "sms": sms?.data, "url": url?.data, "wifi": wifi?.data, "displayValue": displayValue]
return [
"calendarEvent": calendarEvent?.data,
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 Android, I sorted these on alphabetical keys, and added the size.

"contactInfo": contactInfo?.data,
"corners": cornerPoints?.map({$0.cgPointValue.data}),
"displayValue": displayValue,
"driverLicense": driverLicense?.data,
"email": email?.data,
"format": format.rawValue,
"geoPoint": geoPoint?.data,
"phone": phone?.data,
"rawBytes": rawData,
"rawValue": rawValue,
"size": frame.isNull ? nil : [
"width": frame.width,
"height": frame.height,
],
"sms": sms?.data,
"type": valueType.rawValue,
"url": url?.data,
"wifi": wifi?.data,
]
}
}

Expand Down
2 changes: 1 addition & 1 deletion ios/mobile_scanner.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'mobile_scanner'
s.version = '5.2.1'
s.version = '5.2.2'
s.summary = 'An universal scanner for Flutter based on MLKit.'
s.description = <<-DESC
An universal scanner for Flutter based on MLKit.
Expand Down
35 changes: 11 additions & 24 deletions lib/src/method_channel/mobile_scanner_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:mobile_scanner/src/enums/barcode_format.dart';
import 'package:mobile_scanner/src/enums/mobile_scanner_authorization_state.dart';
import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart';
import 'package:mobile_scanner/src/enums/torch_state.dart';
Expand Down Expand Up @@ -54,31 +53,19 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
final List<Map<Object?, Object?>> barcodes =
data.cast<Map<Object?, Object?>>();

if (defaultTargetPlatform == TargetPlatform.macOS) {
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 updated the MacOS implementation to use the same keys / datatypes as iOS/Android. So this can be removed.

return BarcodeCapture(
raw: event,
barcodes: barcodes
.map(
(barcode) => Barcode(
rawValue: barcode['payload'] as String?,
format: BarcodeFormat.fromRawValue(
barcode['symbology'] as int? ?? -1,
),
),
)
.toList(),
);
}

if (defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS) {
final double? width = event['width'] as double?;
final double? height = event['height'] as double?;
defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

MacOS is added here, as the format has been updated to match the other platforms.

final Map<Object?, Object?>? imageData =
event['image'] as Map<Object?, Object?>?;
final Uint8List? image = imageData?['bytes'] as Uint8List?;
final double? width = imageData?['width'] as double?;
final double? height = imageData?['height'] as double?;

return BarcodeCapture(
raw: data,
raw: event,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

MacOS used the event Map as raw data. This wasn't true for iOS/Android, so I updated it to match MacOS.

Unlikely that a lot of people need the raw data anyway.

barcodes: barcodes.map(Barcode.fromNative).toList(),
image: event['image'] as Uint8List?,
image: image,
size: width == null || height == null ? Size.zero : Size(width, height),
);
}
Expand Down Expand Up @@ -154,8 +141,8 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {

@override
Future<BarcodeCapture?> analyzeImage(String path) async {
final Map<String, Object?>? result =
await methodChannel.invokeMapMethod<String, Object?>(
final Map<Object?, Object?>? result =
await methodChannel.invokeMapMethod<Object?, Object?>(
'analyzeImage',
path,
);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/mobile_scanner_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
///
/// If this is false, [BarcodeCapture.image] will always be null.
///
/// Defaults to false, and is only supported on iOS and Android.
/// Defaults to false, and is only supported on iOS, MacOS and Android.
final bool returnImage;

/// Whether the flashlight should be turned on when the camera is started.
Expand Down
Loading
Loading