Julian Steenbakker
Committed by GitHub

Merge pull request #4 from juliansteenbakker/macos

feat: add macos support
  1 +## 0.1.0
  2 +We now have MacOS support using Apple's Vision framework!
  3 +Keep in mind that for now, only the raw value is supported.
  4 +
1 ## 0.0.3 5 ## 0.0.3
2 * Added some API docs and README 6 * Added some API docs and README
3 * Updated the example app 7 * Updated the example app
@@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
26 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 26 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
27 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 27 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
28 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 28 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
  29 + 5225F51353DA345E2811B6A4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E614A1DF8B88C7B0CE1B97 /* Pods_Runner.framework */; };
29 /* End PBXBuildFile section */ 30 /* End PBXBuildFile section */
30 31
31 /* Begin PBXContainerItemProxy section */ 32 /* Begin PBXContainerItemProxy section */
@@ -54,7 +55,7 @@ @@ -54,7 +55,7 @@
54 /* Begin PBXFileReference section */ 55 /* Begin PBXFileReference section */
55 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; 56 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
56 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; 57 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
57 - 33CC10ED2044A3C60003C045 /* mobile_scanner_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "mobile_scanner_example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 58 + 33CC10ED2044A3C60003C045 /* mobile_scanner_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mobile_scanner_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
58 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 59 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
59 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; }; 60 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
60 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; 61 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@@ -66,8 +67,12 @@ @@ -66,8 +67,12 @@
66 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; }; 67 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
67 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; }; 68 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
68 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; }; 69 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
  70 + 65E614A1DF8B88C7B0CE1B97 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
69 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; 71 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
70 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; 72 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
  73 + CB0901144E09E7D7CA20584F /* 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>"; };
  74 + D522F9F6F348C5944077606B /* 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>"; };
  75 + F63009B5E287A1C82F9D7D2F /* 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>"; };
71 /* End PBXFileReference section */ 76 /* End PBXFileReference section */
72 77
73 /* Begin PBXFrameworksBuildPhase section */ 78 /* Begin PBXFrameworksBuildPhase section */
@@ -75,12 +80,23 @@ @@ -75,12 +80,23 @@
75 isa = PBXFrameworksBuildPhase; 80 isa = PBXFrameworksBuildPhase;
76 buildActionMask = 2147483647; 81 buildActionMask = 2147483647;
77 files = ( 82 files = (
  83 + 5225F51353DA345E2811B6A4 /* Pods_Runner.framework in Frameworks */,
78 ); 84 );
79 runOnlyForDeploymentPostprocessing = 0; 85 runOnlyForDeploymentPostprocessing = 0;
80 }; 86 };
81 /* End PBXFrameworksBuildPhase section */ 87 /* End PBXFrameworksBuildPhase section */
82 88
83 /* Begin PBXGroup section */ 89 /* Begin PBXGroup section */
  90 + 20F8C9AA20C2A495C125E194 /* Pods */ = {
  91 + isa = PBXGroup;
  92 + children = (
  93 + CB0901144E09E7D7CA20584F /* Pods-Runner.debug.xcconfig */,
  94 + D522F9F6F348C5944077606B /* Pods-Runner.release.xcconfig */,
  95 + F63009B5E287A1C82F9D7D2F /* Pods-Runner.profile.xcconfig */,
  96 + );
  97 + path = Pods;
  98 + sourceTree = "<group>";
  99 + };
84 33BA886A226E78AF003329D5 /* Configs */ = { 100 33BA886A226E78AF003329D5 /* Configs */ = {
85 isa = PBXGroup; 101 isa = PBXGroup;
86 children = ( 102 children = (
@@ -98,7 +114,8 @@ @@ -98,7 +114,8 @@
98 33FAB671232836740065AC1E /* Runner */, 114 33FAB671232836740065AC1E /* Runner */,
99 33CEB47122A05771004F2AC0 /* Flutter */, 115 33CEB47122A05771004F2AC0 /* Flutter */,
100 33CC10EE2044A3C60003C045 /* Products */, 116 33CC10EE2044A3C60003C045 /* Products */,
101 - D73912EC22F37F3D000D13A0 /* Frameworks */, 117 + 20F8C9AA20C2A495C125E194 /* Pods */,
  118 + 3539353E79638640B4999C09 /* Frameworks */,
102 ); 119 );
103 sourceTree = "<group>"; 120 sourceTree = "<group>";
104 }; 121 };
@@ -145,9 +162,10 @@ @@ -145,9 +162,10 @@
145 path = Runner; 162 path = Runner;
146 sourceTree = "<group>"; 163 sourceTree = "<group>";
147 }; 164 };
148 - D73912EC22F37F3D000D13A0 /* Frameworks */ = { 165 + 3539353E79638640B4999C09 /* Frameworks */ = {
149 isa = PBXGroup; 166 isa = PBXGroup;
150 children = ( 167 children = (
  168 + 65E614A1DF8B88C7B0CE1B97 /* Pods_Runner.framework */,
151 ); 169 );
152 name = Frameworks; 170 name = Frameworks;
153 sourceTree = "<group>"; 171 sourceTree = "<group>";
@@ -159,11 +177,13 @@ @@ -159,11 +177,13 @@
159 isa = PBXNativeTarget; 177 isa = PBXNativeTarget;
160 buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; 178 buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
161 buildPhases = ( 179 buildPhases = (
  180 + 696298230BDAD783AEC51C81 /* [CP] Check Pods Manifest.lock */,
162 33CC10E92044A3C60003C045 /* Sources */, 181 33CC10E92044A3C60003C045 /* Sources */,
163 33CC10EA2044A3C60003C045 /* Frameworks */, 182 33CC10EA2044A3C60003C045 /* Frameworks */,
164 33CC10EB2044A3C60003C045 /* Resources */, 183 33CC10EB2044A3C60003C045 /* Resources */,
165 33CC110E2044A8840003C045 /* Bundle Framework */, 184 33CC110E2044A8840003C045 /* Bundle Framework */,
166 3399D490228B24CF009A79C7 /* ShellScript */, 185 3399D490228B24CF009A79C7 /* ShellScript */,
  186 + 8A90D2BC4083C5ACCEEBF32B /* [CP] Embed Pods Frameworks */,
167 ); 187 );
168 buildRules = ( 188 buildRules = (
169 ); 189 );
@@ -270,6 +290,45 @@ @@ -270,6 +290,45 @@
270 shellPath = /bin/sh; 290 shellPath = /bin/sh;
271 shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; 291 shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
272 }; 292 };
  293 + 696298230BDAD783AEC51C81 /* [CP] Check Pods Manifest.lock */ = {
  294 + isa = PBXShellScriptBuildPhase;
  295 + buildActionMask = 2147483647;
  296 + files = (
  297 + );
  298 + inputFileListPaths = (
  299 + );
  300 + inputPaths = (
  301 + "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
  302 + "${PODS_ROOT}/Manifest.lock",
  303 + );
  304 + name = "[CP] Check Pods Manifest.lock";
  305 + outputFileListPaths = (
  306 + );
  307 + outputPaths = (
  308 + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
  309 + );
  310 + runOnlyForDeploymentPostprocessing = 0;
  311 + shellPath = /bin/sh;
  312 + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
  313 + showEnvVarsInLog = 0;
  314 + };
  315 + 8A90D2BC4083C5ACCEEBF32B /* [CP] Embed Pods Frameworks */ = {
  316 + isa = PBXShellScriptBuildPhase;
  317 + buildActionMask = 2147483647;
  318 + files = (
  319 + );
  320 + inputFileListPaths = (
  321 + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
  322 + );
  323 + name = "[CP] Embed Pods Frameworks";
  324 + outputFileListPaths = (
  325 + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
  326 + );
  327 + runOnlyForDeploymentPostprocessing = 0;
  328 + shellPath = /bin/sh;
  329 + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
  330 + showEnvVarsInLog = 0;
  331 + };
273 /* End PBXShellScriptBuildPhase section */ 332 /* End PBXShellScriptBuildPhase section */
274 333
275 /* Begin PBXSourcesBuildPhase section */ 334 /* Begin PBXSourcesBuildPhase section */
@@ -344,7 +403,7 @@ @@ -344,7 +403,7 @@
344 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 403 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
345 GCC_WARN_UNUSED_FUNCTION = YES; 404 GCC_WARN_UNUSED_FUNCTION = YES;
346 GCC_WARN_UNUSED_VARIABLE = YES; 405 GCC_WARN_UNUSED_VARIABLE = YES;
347 - MACOSX_DEPLOYMENT_TARGET = 10.11; 406 + MACOSX_DEPLOYMENT_TARGET = 10.13;
348 MTL_ENABLE_DEBUG_INFO = NO; 407 MTL_ENABLE_DEBUG_INFO = NO;
349 SDKROOT = macosx; 408 SDKROOT = macosx;
350 SWIFT_COMPILATION_MODE = wholemodule; 409 SWIFT_COMPILATION_MODE = wholemodule;
@@ -366,6 +425,7 @@ @@ -366,6 +425,7 @@
366 "$(inherited)", 425 "$(inherited)",
367 "@executable_path/../Frameworks", 426 "@executable_path/../Frameworks",
368 ); 427 );
  428 + MACOSX_DEPLOYMENT_TARGET = 10.13;
369 PROVISIONING_PROFILE_SPECIFIER = ""; 429 PROVISIONING_PROFILE_SPECIFIER = "";
370 SWIFT_VERSION = 5.0; 430 SWIFT_VERSION = 5.0;
371 }; 431 };
@@ -423,7 +483,7 @@ @@ -423,7 +483,7 @@
423 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
424 GCC_WARN_UNUSED_FUNCTION = YES; 484 GCC_WARN_UNUSED_FUNCTION = YES;
425 GCC_WARN_UNUSED_VARIABLE = YES; 485 GCC_WARN_UNUSED_VARIABLE = YES;
426 - MACOSX_DEPLOYMENT_TARGET = 10.11; 486 + MACOSX_DEPLOYMENT_TARGET = 10.13;
427 MTL_ENABLE_DEBUG_INFO = YES; 487 MTL_ENABLE_DEBUG_INFO = YES;
428 ONLY_ACTIVE_ARCH = YES; 488 ONLY_ACTIVE_ARCH = YES;
429 SDKROOT = macosx; 489 SDKROOT = macosx;
@@ -470,7 +530,7 @@ @@ -470,7 +530,7 @@
470 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 530 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
471 GCC_WARN_UNUSED_FUNCTION = YES; 531 GCC_WARN_UNUSED_FUNCTION = YES;
472 GCC_WARN_UNUSED_VARIABLE = YES; 532 GCC_WARN_UNUSED_VARIABLE = YES;
473 - MACOSX_DEPLOYMENT_TARGET = 10.11; 533 + MACOSX_DEPLOYMENT_TARGET = 10.13;
474 MTL_ENABLE_DEBUG_INFO = NO; 534 MTL_ENABLE_DEBUG_INFO = NO;
475 SDKROOT = macosx; 535 SDKROOT = macosx;
476 SWIFT_COMPILATION_MODE = wholemodule; 536 SWIFT_COMPILATION_MODE = wholemodule;
@@ -492,6 +552,7 @@ @@ -492,6 +552,7 @@
492 "$(inherited)", 552 "$(inherited)",
493 "@executable_path/../Frameworks", 553 "@executable_path/../Frameworks",
494 ); 554 );
  555 + MACOSX_DEPLOYMENT_TARGET = 10.13;
495 PROVISIONING_PROFILE_SPECIFIER = ""; 556 PROVISIONING_PROFILE_SPECIFIER = "";
496 SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 557 SWIFT_OPTIMIZATION_LEVEL = "-Onone";
497 SWIFT_VERSION = 5.0; 558 SWIFT_VERSION = 5.0;
@@ -512,6 +573,7 @@ @@ -512,6 +573,7 @@
512 "$(inherited)", 573 "$(inherited)",
513 "@executable_path/../Frameworks", 574 "@executable_path/../Frameworks",
514 ); 575 );
  576 + MACOSX_DEPLOYMENT_TARGET = 10.13;
515 PROVISIONING_PROFILE_SPECIFIER = ""; 577 PROVISIONING_PROFILE_SPECIFIER = "";
516 SWIFT_VERSION = 5.0; 578 SWIFT_VERSION = 5.0;
517 }; 579 };
@@ -4,4 +4,7 @@ @@ -4,4 +4,7 @@
4 <FileRef 4 <FileRef
5 location = "group:Runner.xcodeproj"> 5 location = "group:Runner.xcodeproj">
6 </FileRef> 6 </FileRef>
  7 + <FileRef
  8 + location = "group:Pods/Pods.xcodeproj">
  9 + </FileRef>
7 </Workspace> 10 </Workspace>
@@ -6,6 +6,8 @@ @@ -6,6 +6,8 @@
6 <true/> 6 <true/>
7 <key>com.apple.security.cs.allow-jit</key> 7 <key>com.apple.security.cs.allow-jit</key>
8 <true/> 8 <true/>
  9 + <key>com.apple.security.device.camera</key>
  10 + <true/>
9 <key>com.apple.security.network.server</key> 11 <key>com.apple.security.network.server</key>
10 <true/> 12 <true/>
11 </dict> 13 </dict>
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 <plist version="1.0"> 3 <plist version="1.0">
4 <dict> 4 <dict>
  5 + <key>NSCameraUsageDescription</key>
  6 + <string>The camera is required to scan barcodes or QR codes</string>
5 <key>CFBundleDevelopmentRegion</key> 7 <key>CFBundleDevelopmentRegion</key>
6 <string>$(DEVELOPMENT_LANGUAGE)</string> 8 <string>$(DEVELOPMENT_LANGUAGE)</string>
7 <key>CFBundleExecutable</key> 9 <key>CFBundleExecutable</key>
@@ -4,5 +4,7 @@ @@ -4,5 +4,7 @@
4 <dict> 4 <dict>
5 <key>com.apple.security.app-sandbox</key> 5 <key>com.apple.security.app-sandbox</key>
6 <true/> 6 <true/>
  7 + <key>com.apple.security.device.camera</key>
  8 + <true/>
7 </dict> 9 </dict>
8 </plist> 10 </plist>
@@ -86,6 +86,9 @@ class MobileScannerController { @@ -86,6 +86,9 @@ class MobileScannerController {
86 final barcode = Barcode.fromNative(data); 86 final barcode = Barcode.fromNative(data);
87 barcodesController.add(barcode); 87 barcodesController.add(barcode);
88 break; 88 break;
  89 + case 'barcodeMac':
  90 + barcodesController.add(Barcode(rawValue: data['payload']));
  91 + break;
89 default: 92 default:
90 throw UnimplementedError(); 93 throw UnimplementedError();
91 } 94 }
@@ -18,7 +18,7 @@ class Barcode { @@ -18,7 +18,7 @@ class Barcode {
18 /// Returns raw bytes as it was encoded in the barcode. 18 /// Returns raw bytes as it was encoded in the barcode.
19 /// 19 ///
20 /// Returns null if the raw bytes can not be determined. 20 /// Returns null if the raw bytes can not be determined.
21 - final Uint8List rawBytes; 21 + final Uint8List? rawBytes;
22 22
23 /// Returns barcode value as it was encoded in the barcode. Structured values are not parsed, for example: 'MEBKM:TITLE:Google;URL://www.google.com;;'. 23 /// Returns barcode value as it was encoded in the barcode. Structured values are not parsed, for example: 'MEBKM:TITLE:Google;URL://www.google.com;;'.
24 /// 24 ///
@@ -63,6 +63,22 @@ class Barcode { @@ -63,6 +63,22 @@ class Barcode {
63 /// Gets parsed WiFi AP details. 63 /// Gets parsed WiFi AP details.
64 final WiFi? wifi; 64 final WiFi? wifi;
65 65
  66 + Barcode(
  67 + {this.corners,
  68 + this.format = BarcodeFormat.ean13,
  69 + this.rawBytes,
  70 + this.type = BarcodeType.text,
  71 + this.calendarEvent,
  72 + this.contactInfo,
  73 + this.driverLicense,
  74 + this.email,
  75 + this.geoPoint,
  76 + this.phone,
  77 + this.sms,
  78 + this.url,
  79 + this.wifi,
  80 + required this.rawValue});
  81 +
66 /// Create a [Barcode] from native data. 82 /// Create a [Barcode] from native data.
67 Barcode.fromNative(Map<dynamic, dynamic> data) 83 Barcode.fromNative(Map<dynamic, dynamic> data)
68 : corners = toCorners(data['corners']), 84 : corners = toCorners(data['corners']),
1 -import Cocoa 1 +import AVFoundation
2 import FlutterMacOS 2 import FlutterMacOS
  3 +import Vision
  4 +
  5 +public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate {
  6 +
  7 + let registry: FlutterTextureRegistry
  8 +
  9 + // Sink for publishing event changes
  10 + var sink: FlutterEventSink!
  11 +
  12 + // Texture id of the camera preview
  13 + var textureId: Int64!
  14 +
  15 + // Capture session of the camera
  16 + var captureSession: AVCaptureSession!
  17 +
  18 + // The selected camera
  19 + var device: AVCaptureDevice!
  20 +
  21 + // Image to be sent to the texture
  22 + var latestBuffer: CVImageBuffer!
  23 +
  24 +
  25 + var analyzeMode: Int = 0
  26 + var analyzing: Bool = false
  27 + var position = AVCaptureDevice.Position.back
3 28
4 -public class MobileScannerPlugin: NSObject, FlutterPlugin {  
5 public static func register(with registrar: FlutterPluginRegistrar) { 29 public static func register(with registrar: FlutterPluginRegistrar) {
6 - let channel = FlutterMethodChannel(name: "mobile_scanner", binaryMessenger: registrar.messenger)  
7 - let instance = MobileScannerPlugin()  
8 - registrar.addMethodCallDelegate(instance, channel: channel) 30 + let instance = MobileScannerPlugin(registrar.textures)
  31 +
  32 + let method = FlutterMethodChannel(name:
  33 + "dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger)
  34 + let event = FlutterEventChannel(name:
  35 + "dev.steenbakker.mobile_scanner/scanner/event", binaryMessenger: registrar.messenger)
  36 + registrar.addMethodCallDelegate(instance, channel: method)
  37 + event.setStreamHandler(instance)
  38 + }
  39 +
  40 + init(_ registry: FlutterTextureRegistry) {
  41 + self.registry = registry
  42 + super.init()
9 } 43 }
10 44
  45 +
11 public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 46 public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12 switch call.method { 47 switch call.method {
13 - case "getPlatformVersion":  
14 - result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) 48 + case "state":
  49 + checkPermission(call, result)
  50 + case "request":
  51 + requestPermission(call, result)
  52 + case "start":
  53 + start(call, result)
  54 + case "torch":
  55 + switchTorch(call, result)
  56 + case "analyze":
  57 + switchAnalyzeMode(call, result)
  58 + case "stop":
  59 + stop(result)
15 default: 60 default:
16 result(FlutterMethodNotImplemented) 61 result(FlutterMethodNotImplemented)
17 } 62 }
18 } 63 }
  64 +
  65 + // FlutterStreamHandler
  66 + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
  67 + sink = events
  68 + return nil
  69 + }
  70 +
  71 + // FlutterStreamHandler
  72 + public func onCancel(withArguments arguments: Any?) -> FlutterError? {
  73 + sink = nil
  74 + return nil
  75 + }
  76 +
  77 + // FlutterTexture
  78 + public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
  79 + if latestBuffer == nil {
  80 + return nil
  81 + }
  82 + return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer)
  83 + }
  84 +
  85 + var i = 0
  86 +
  87 +
  88 + // Gets called when a new image is added to the buffer
  89 + public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
  90 + i = i + 1;
  91 +
  92 + latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
  93 + registry.textureFrameAvailable(textureId)
  94 +
  95 + switch analyzeMode {
  96 + case 1: // barcode
  97 +
  98 + // Limit the analyzer because the texture output will freeze otherwise
  99 + if i / 10 == 1 {
  100 + i = 0
  101 + } else {
  102 + break
  103 + }
  104 + let imageRequestHandler = VNImageRequestHandler(
  105 + cvPixelBuffer: latestBuffer,
  106 + orientation: .right)
  107 +
  108 + do {
  109 + try imageRequestHandler.perform([VNDetectBarcodesRequest { (request, error) in
  110 + if error == nil {
  111 + if let results = request.results as? [VNBarcodeObservation] {
  112 + for barcode in results {
  113 + let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "")
  114 + let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]]
  115 + self.sink?(event)
  116 +
  117 + // if barcodeType == "QR" {
  118 + // let image = CIImage(image: source)
  119 + // image?.cropping(to: barcode.boundingBox)
  120 + // self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!)
  121 + // }
  122 + }
  123 + }
  124 + } else {
  125 + print(error!.localizedDescription)
  126 + }
  127 + }])
  128 + } catch {
  129 + print(error)
  130 + }
  131 +
  132 + default: // none
  133 + break
  134 + }
  135 + }
  136 +
  137 + func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  138 + if #available(macOS 10.14, *) {
  139 + let status = AVCaptureDevice.authorizationStatus(for: .video)
  140 + switch status {
  141 + case .notDetermined:
  142 + result(0)
  143 + case .authorized:
  144 + result(1)
  145 + default:
  146 + result(2)
  147 + }
  148 + } else {
  149 + result(1)
  150 + }
  151 + }
  152 +
  153 + func requestPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  154 + if #available(macOS 10.14, *) {
  155 + AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) })
  156 + } else {
  157 + result(0)
  158 + }
  159 + }
  160 +
  161 + func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  162 + textureId = registry.register(self)
  163 + captureSession = AVCaptureSession()
  164 +
  165 + let argReader = MapArgumentReader(call.arguments as? [String: Any])
  166 +
  167 +// let ratio: Int = argReader.int(key: "ratio")
  168 + let torch: Bool = argReader.bool(key: "torch") ?? false
  169 + let facing: Int = argReader.int(key: "facing") ?? 1
  170 +
  171 + // Set the camera to use
  172 + position = facing == 0 ? AVCaptureDevice.Position.front : .back
  173 +
  174 + // Open the camera device
  175 + if #available(macOS 10.15, *) {
  176 + device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first
  177 + } else {
  178 + device = AVCaptureDevice.devices(for: .video).filter({$0.position == position}).first
  179 + }
  180 +
  181 + // Enable the torch if parameter is set and torch is available
  182 + if (device.hasTorch) {
  183 + do {
  184 + try device.lockForConfiguration()
  185 + device.torchMode = torch ? .on : .off
  186 + device.unlockForConfiguration()
  187 + } catch {
  188 + result(FlutterError(code: error.localizedDescription, message: nil, details: nil))
  189 + }
  190 + }
  191 +
  192 + device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil)
  193 + captureSession.beginConfiguration()
  194 +
  195 + // Add device input
  196 + do {
  197 + let input = try AVCaptureDeviceInput(device: device)
  198 + captureSession.addInput(input)
  199 + } catch {
  200 + result(FlutterError(code: error.localizedDescription, message: nil, details: nil))
  201 + }
  202 + captureSession.sessionPreset = AVCaptureSession.Preset.photo;
  203 + // Add video output.
  204 + let videoOutput = AVCaptureVideoDataOutput()
  205 +
  206 + videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
  207 + videoOutput.alwaysDiscardsLateVideoFrames = true
  208 +
  209 + videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
  210 + captureSession.addOutput(videoOutput)
  211 + for connection in videoOutput.connections {
  212 +// connection.videoOrientation = .portrait
  213 + if position == .front && connection.isVideoMirroringSupported {
  214 + connection.isVideoMirrored = true
  215 + }
  216 + }
  217 + captureSession.commitConfiguration()
  218 + captureSession.startRunning()
  219 + let dimensions = CMVideoFormatDescriptionGetDimensions(device.activeFormat.formatDescription)
  220 + let size = ["width": Double(dimensions.width), "height": Double(dimensions.height)]
  221 + let answer: [String : Any?] = ["textureId": textureId, "size": size, "torchable": device.hasTorch]
  222 + result(answer)
  223 + }
  224 +
  225 + func switchTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  226 + do {
  227 + try device.lockForConfiguration()
  228 + device.torchMode = call.arguments as! Int == 1 ? .on : .off
  229 + device.unlockForConfiguration()
  230 + result(nil)
  231 + } catch {
  232 + result(FlutterError(code: error.localizedDescription, message: nil, details: nil))
  233 + }
  234 + }
  235 +
  236 + func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  237 + analyzeMode = call.arguments as! Int
  238 + result(nil)
  239 + }
  240 +
  241 + func stop(_ result: FlutterResult) {
  242 + captureSession.stopRunning()
  243 + for input in captureSession.inputs {
  244 + captureSession.removeInput(input)
  245 + }
  246 + for output in captureSession.outputs {
  247 + captureSession.removeOutput(output)
  248 + }
  249 + device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode))
  250 + registry.unregisterTexture(textureId)
  251 +
  252 + analyzeMode = 0
  253 + latestBuffer = nil
  254 + captureSession = nil
  255 + device = nil
  256 + textureId = nil
  257 +
  258 + result(nil)
  259 + }
  260 +
  261 + // Observer for torch state
  262 + public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
  263 + switch keyPath {
  264 + case "torchMode":
  265 + // off = 0; on = 1; auto = 2;
  266 + let state = change?[.newKey] as? Int
  267 + let event: [String: Any?] = ["name": "torchState", "data": state]
  268 + sink?(event)
  269 + default:
  270 + break
  271 + }
  272 + }
  273 +}
  274 +
  275 +class MapArgumentReader {
  276 +
  277 + let args: [String: Any]?
  278 +
  279 + init(_ args: [String: Any]?) {
  280 + self.args = args
  281 + }
  282 +
  283 + func string(key: String) -> String? {
  284 + return args?[key] as? String
  285 + }
  286 +
  287 + func int(key: String) -> Int? {
  288 + return (args?[key] as? NSNumber)?.intValue
  289 + }
  290 +
  291 + func bool(key: String) -> Bool? {
  292 + return (args?[key] as? NSNumber)?.boolValue
  293 + }
  294 +
  295 + func stringArray(key: String) -> [String]? {
  296 + return args?[key] as? [String]
  297 + }
  298 +
19 } 299 }
@@ -15,8 +15,7 @@ An universal scanner for Flutter based on MLKit. @@ -15,8 +15,7 @@ An universal scanner for Flutter based on MLKit.
15 s.source = { :path => '.' } 15 s.source = { :path => '.' }
16 s.source_files = 'Classes/**/*' 16 s.source_files = 'Classes/**/*'
17 s.dependency 'FlutterMacOS' 17 s.dependency 'FlutterMacOS'
18 -  
19 - s.platform = :osx, '10.11' 18 + s.platform = :osx, '10.13'
20 s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } 19 s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
21 s.swift_version = '5.0' 20 s.swift_version = '5.0'
22 end 21 end
1 name: mobile_scanner 1 name: mobile_scanner
2 description: A universal scanner for Flutter based on MLKit. Uses CameraX on Android and AVFoundation on iOS. 2 description: A universal scanner for Flutter based on MLKit. Uses CameraX on Android and AVFoundation on iOS.
3 -version: 0.0.3 3 +version: 0.1.0
4 repository: https://github.com/juliansteenbakker/mobile_scanner 4 repository: https://github.com/juliansteenbakker/mobile_scanner
5 5
6 environment: 6 environment:
@@ -24,3 +24,5 @@ flutter: @@ -24,3 +24,5 @@ flutter:
24 pluginClass: MobileScannerPlugin 24 pluginClass: MobileScannerPlugin
25 ios: 25 ios:
26 pluginClass: MobileScannerPlugin 26 pluginClass: MobileScannerPlugin
  27 + macos:
  28 + pluginClass: MobileScannerPlugin