Julian Steenbakker
Committed by GitHub

Merge branch 'master' into patch-1

... ... @@ -34,10 +34,20 @@ To use this version you must alter the mobile_scanner gradle file to replace `co
### iOS
**Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:**
NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor.
**If you want to use the local gallery feature from [image_picker](https://pub.dev/packages/image_picker)**
NSPhotoLibraryUsageDescription - describe why your app needs permission for the photo library. This is called Privacy - Photo Library Usage Description in the visual editor.
Example,
```
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photos access to get QR code from photo library</string>
```
### macOS
Ensure that you granted camera permission in XCode -> Signing & Capabilities:
... ...
... ... @@ -36,5 +36,8 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
end
end
end
... ...
... ... @@ -26,7 +26,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
5B9BD2ADBC68B74D80B57DF1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC099C2B6D6B30BFB3FA6DB8 /* Pods_Runner.framework */; };
5348E36EDC155A01222C3599 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 41950513928B2DA794C685E3 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
... ... @@ -67,12 +67,12 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
3CEE8DB43A84811F33EB0202 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
41950513928B2DA794C685E3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
433FCBF2B1E3F653F96B3C79 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
A1CBC07680A8ED396DBB68C0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
CAD760C57A57D903AB03B47A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
EC099C2B6D6B30BFB3FA6DB8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
97D4F0103EE99761EF216A9C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
B67CB61EED45BF13A197D997 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
... ... @@ -80,27 +80,19 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5B9BD2ADBC68B74D80B57DF1 /* Pods_Runner.framework in Frameworks */,
5348E36EDC155A01222C3599 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
18927D60C719EB75FC0A6633 /* Frameworks */ = {
isa = PBXGroup;
children = (
EC099C2B6D6B30BFB3FA6DB8 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
20F8C9AA20C2A495C125E194 /* Pods */ = {
isa = PBXGroup;
children = (
CAD760C57A57D903AB03B47A /* Pods-Runner.debug.xcconfig */,
A1CBC07680A8ED396DBB68C0 /* Pods-Runner.release.xcconfig */,
3CEE8DB43A84811F33EB0202 /* Pods-Runner.profile.xcconfig */,
B67CB61EED45BF13A197D997 /* Pods-Runner.debug.xcconfig */,
97D4F0103EE99761EF216A9C /* Pods-Runner.release.xcconfig */,
433FCBF2B1E3F653F96B3C79 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
... ... @@ -123,7 +115,7 @@
33CEB47122A05771004F2AC0 /* Flutter */,
33CC10EE2044A3C60003C045 /* Products */,
20F8C9AA20C2A495C125E194 /* Pods */,
18927D60C719EB75FC0A6633 /* Frameworks */,
64FFE901A03ED70F67D8DCD6 /* Frameworks */,
);
sourceTree = "<group>";
};
... ... @@ -170,6 +162,14 @@
path = Runner;
sourceTree = "<group>";
};
64FFE901A03ED70F67D8DCD6 /* Frameworks */ = {
isa = PBXGroup;
children = (
41950513928B2DA794C685E3 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
... ... @@ -177,13 +177,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
20903D1E9D9F08576541FFD7 /* [CP] Check Pods Manifest.lock */,
11C0752B00246A027DB2D859 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
DF45614760BB9B24F49B2055 /* [CP] Embed Pods Frameworks */,
E6424ECF3C1308BAAF6E5E67 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
... ... @@ -253,7 +253,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
20903D1E9D9F08576541FFD7 /* [CP] Check Pods Manifest.lock */ = {
11C0752B00246A027DB2D859 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
... ... @@ -312,7 +312,7 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
DF45614760BB9B24F49B2055 /* [CP] Embed Pods Frameworks */ = {
E6424ECF3C1308BAAF6E5E67 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
... ...
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
... ...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
... ...
//
// DetectionSpeed.swift
// mobile_scanner
//
// Created by Julian Steenbakker on 11/11/2022.
//
enum DetectionSpeed: Int {
case noDuplicates = 0
case normal = 1
case unrestricted = 2
}
... ...
import AVFoundation
import FlutterMacOS
import Vision
import UIKit
import AppKit
public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate {
... ... @@ -25,6 +25,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
// optional window to limit scan search
var scanWindow: CGRect?
var detectionSpeed: DetectionSpeed = DetectionSpeed.noDuplicates
// var analyzeMode: Int = 0
var analyzing: Bool = false
... ... @@ -90,66 +92,59 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
var i = 0
// Gets called when a new image is added to the buffer
public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// Ignore invalid textureId
if textureId == nil {
return
}
i = i + 1;
latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
print("Failed to get image buffer from sample buffer.")
return
}
latestBuffer = imageBuffer
registry.textureFrameAvailable(textureId)
// switch analyzeMode {
// case 1: // barcode
// Limit the analyzer because the texture output will freeze otherwise
if i / 10 == 1 {
i = 0
} else {
return
}
if ((detectionSpeed == DetectionSpeed.normal || detectionSpeed == DetectionSpeed.noDuplicates) && i > 10 || detectionSpeed == DetectionSpeed.unrestricted) {
i = 0
let imageRequestHandler = VNImageRequestHandler(
cvPixelBuffer: latestBuffer,
orientation: .right)
do {
try imageRequestHandler.perform([VNDetectBarcodesRequest { (request, error) in
if error == nil {
if let results = request.results as? [VNBarcodeObservation] {
for barcode in results {
if scanWindow != nil {
let match = isbarCodeInScanWindow(scanWindow!, barcode, buffer!.image)
if (!match) {
continue
do {
try imageRequestHandler.perform([VNDetectBarcodesRequest { (request, error) in
if error == nil {
if let results = request.results as? [VNBarcodeObservation] {
for barcode in results {
if self.scanWindow != nil {
let match = self.isbarCodeInScanWindow(self.scanWindow!, barcode, self.latestBuffer)
if (!match) {
continue
}
}
}
let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "")
let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]]
self.sink?(event)
let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "")
let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]]
self.sink?(event)
// if barcodeType == "QR" {
// let image = CIImage(image: source)
// image?.cropping(to: barcode.boundingBox)
// self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!)
// }
}
// if barcodeType == "QR" {
// let image = CIImage(image: source)
// image?.cropping(to: barcode.boundingBox)
// self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!)
// }
}
}
} else {
print(error!.localizedDescription)
}
} else {
print(error!.localizedDescription)
}
}])
} catch {
print(error)
}
// default: // none
// break
// }
}])
} catch {
print(error)
}
} else {
i+=1
}
}
func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
... ... @@ -193,11 +188,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
scanWindow = CGRect(x: minX, y: minY, width: width, height: height)
}
func isbarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: Barcode, _ inputImage: UIImage) -> Bool {
let barcodeBoundingBox = barcode.frame
func isbarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: VNBarcodeObservation, _ inputImage: CVImageBuffer) -> Bool {
let size = CVImageBufferGetEncodedSize(inputImage)
let imageWidth = inputImage.size.width;
let imageHeight = inputImage.size.height;
let imageWidth = size.width;
let imageHeight = size.height;
let minX = scanWindow.minX * imageWidth
let minY = scanWindow.minY * imageHeight
... ... @@ -205,7 +200,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
let height = scanWindow.height * imageHeight
let scaledScanWindow = CGRect(x: minX, y: minY, width: width, height: height)
return scaledScanWindow.contains(barcodeBoundingBox)
return scaledScanWindow.contains(barcode.boundingBox)
}
func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
... ... @@ -224,6 +219,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
// let ratio: Int = argReader.int(key: "ratio")
let torch: Bool = argReader.bool(key: "torch") ?? false
let facing: Int = argReader.int(key: "facing") ?? 1
let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0
detectionSpeed = DetectionSpeed(rawValue: speed)!
// Set the camera to use
position = facing == 0 ? AVCaptureDevice.Position.front : .back
... ...