Skip to content

Commit

Permalink
feat: refactor ios and macos to darwin, migrate to vision api
Browse files Browse the repository at this point in the history
  • Loading branch information
juliansteenbakker committed Oct 23, 2024
1 parent e01fbed commit 7db2758
Show file tree
Hide file tree
Showing 22 changed files with 128 additions and 1,170 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 7.0.0-beta.1

This version replaces MLKit on iOS with Apples Vision API. The code base is now shared with macOS.
This change removes the requirement for iOS 15.

There are still some problems with this build.
- Zoom slider not working
- scanWindow not working
- Flash shows briefly when starting scanner.
- Other issues, not fully tested yet.

## 6.0.2

Bugs fixed:
Expand Down
7 changes: 7 additions & 0 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
14 changes: 10 additions & 4 deletions macos/mobile_scanner.podspec → darwin/mobile_scanner.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
#
Pod::Spec.new do |s|
s.name = 'mobile_scanner'
s.version = '6.0.2'
s.version = '7.0.0'
s.summary = 'An universal scanner for Flutter based on MLKit.'
s.description = <<-DESC
An universal scanner for Flutter based on MLKit.
DESC
s.homepage = 'https://github.com/juliansteenbakker/mobile_scanner'
s.license = { :file => '../LICENSE' }
s.author = { 'Julian Steenbakker' => '[email protected]' }


s.source = { :path => '.' }
s.source_files = 'mobile_scanner/Sources/mobile_scanner/**/*.swift'
s.dependency 'FlutterMacOS'
s.platform = :osx, '10.14'
s.ios.dependency 'Flutter'
s.osx.dependency 'FlutterMacOS'
s.ios.deployment_target = '12.0'
s.osx.deployment_target = '10.14'


s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
s.resource_bundles = {'mobile_scanner_macos_privacy' => ['mobile_scanner/Sources/mobile_scanner/Resources/PrivacyInfo.xcprivacy']}
s.resource_bundles = {'mobile_scanner_privacy' => ['mobile_scanner/Sources/mobile_scanner/Resources/PrivacyInfo.xcprivacy']}
end
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import AVFoundation
import FlutterMacOS
import Flutter
import Vision
import AppKit
import VideoToolbox

#if os(iOS)
import Flutter
import UIKit
import MobileCoreServices
#else
import AppKit
import FlutterMacOS
#endif

public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate {

let registry: FlutterTextureRegistry
Expand Down Expand Up @@ -39,11 +47,23 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
var position = AVCaptureDevice.Position.back

public static func register(with registrar: FlutterPluginRegistrar) {
let instance = MobileScannerPlugin(registrar.textures)
#if os(iOS)
let textures = registrar.textures()
#else
let textures = registrar.textures
#endif

#if os(iOS)
let messenger = registrar.messenger()
#else
let messenger = registrar.messenger
#endif

let instance = MobileScannerPlugin(textures)
let method = FlutterMethodChannel(name:
"dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger)
"dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: messenger)
let event = FlutterEventChannel(name:
"dev.steenbakker.mobile_scanner/scanner/event", binaryMessenger: registrar.messenger)
"dev.steenbakker.mobile_scanner/scanner/event", binaryMessenger: messenger)
registrar.addMethodCallDelegate(instance, channel: method)
event.setStreamHandler(instance)
}
Expand Down Expand Up @@ -267,6 +287,26 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
let scaledScanWindow = CGRect(x: minX, y: minY, width: width, height: height)
return scaledScanWindow.contains(barcode.boundingBox)
}

private func getVideoOrientation() -> AVCaptureVideoOrientation {
var videoOrientation: AVCaptureVideoOrientation

switch UIDevice.current.orientation {
case .portrait:
videoOrientation = .portrait
case .portraitUpsideDown:
videoOrientation = .portraitUpsideDown
case .landscapeLeft:
videoOrientation = .landscapeRight
case .landscapeRight:
videoOrientation = .landscapeLeft
default:
videoOrientation = .portrait
}

return videoOrientation
}


func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
if (device != nil || captureSession != nil) {
Expand Down Expand Up @@ -327,30 +367,59 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
return
}
captureSession!.sessionPreset = AVCaptureSession.Preset.photo
// Add video output.
let videoOutput = AVCaptureVideoDataOutput()



// Add video output
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
videoOutput.alwaysDiscardsLateVideoFrames = true

videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
captureSession!.addOutput(videoOutput)
for connection in videoOutput.connections {
let orientation = self.getVideoOrientation()

// Adjust orientation for the video connection
if let connection = videoOutput.connections.first {
if connection.isVideoOrientationSupported {
connection.videoOrientation = orientation
}

if position == .front && connection.isVideoMirroringSupported {
connection.isVideoMirrored = true
}
}

captureSession!.commitConfiguration()
captureSession!.startRunning()
let dimensions = CMVideoFormatDescriptionGetDimensions(device.activeFormat.formatDescription)
let size = ["width": Double(dimensions.width), "height": Double(dimensions.height)]

let answer: [String : Any?] = [
"textureId": textureId,
"size": size,
"currentTorchState": device.hasTorch ? device.torchMode.rawValue : -1,
]
result(answer)

// Move startRunning to a background thread to avoid blocking the main UI thread
DispatchQueue.global(qos: .background).async {
self.captureSession!.startRunning()

DispatchQueue.main.async {
// Return the result on the main thread after the session starts
let dimensions = CMVideoFormatDescriptionGetDimensions(self.device!.activeFormat.formatDescription)
var width = Double(dimensions.width)
var height = Double(dimensions.height)

// Swap width and height if the image is in portrait mode
if orientation == AVCaptureVideoOrientation.portrait || orientation == AVCaptureVideoOrientation.portraitUpsideDown {
let temp = width
width = height
height = temp
}

let size = ["width": width, "height": height]

let answer: [String : Any?] = [
"textureId": self.textureId,
"size": size,
"currentTorchState": self.device!.hasTorch ? self.device!.torchMode.rawValue : -1,
]

result(answer) // Make sure to return the result on the main thread
}
}
}

// TODO: this method should be removed when iOS and MacOS share their implementation.
Expand Down Expand Up @@ -587,7 +656,7 @@ extension CGImage {

let formatHint: CFString

if #available(macOS 11.0, *) {
if #available(iOS 14.0, macOS 11.0, *) {
formatHint = UTType.jpeg.identifier as CFString
} else {
formatHint = kUTTypeJPEG
Expand Down Expand Up @@ -626,6 +695,7 @@ extension VNBarcodeObservation {
],
"format": symbology.toInt ?? -1,
"rawValue": payloadStringValue ?? "",
"displayValue": payloadStringValue ?? "",
"size": [
"width": distanceBetween(topLeft, topRight),
"height": distanceBetween(topLeft, bottomLeft),
Expand All @@ -636,7 +706,7 @@ extension VNBarcodeObservation {

extension VNBarcodeSymbology {
static func fromInt(_ mapValue:Int) -> VNBarcodeSymbology? {
if #available(macOS 12.0, *) {
if #available(iOS 15.0, macOS 12.0, *) {
if(mapValue == 8){
return VNBarcodeSymbology.codabar
}
Expand Down Expand Up @@ -670,7 +740,7 @@ extension VNBarcodeSymbology {
}

var toInt: Int? {
if #available(macOS 12.0, *) {
if #available(iOS 15.0, macOS 12.0, *) {
if(self == VNBarcodeSymbology.codabar){
return 8
}
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '15.5.0'
platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down Expand Up @@ -41,7 +41,7 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.5.0'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
24 changes: 3 additions & 21 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
3DBCC0215D7BED1D9A756EA3 /* [CP] Embed Pods Frameworks */,
BB0C8EA8DA81A75DE53F052F /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -318,23 +317,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
BB0C8EA8DA81A75DE53F052F /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
C7DE006A696F551C4E067E41 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -488,7 +470,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 75Y2P2WSQQ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down Expand Up @@ -670,7 +652,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 75Y2P2WSQQ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand All @@ -696,7 +678,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 75Y2P2WSQQ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down
38 changes: 0 additions & 38 deletions ios/.gitignore

This file was deleted.

Empty file removed ios/Assets/.gitkeep
Empty file.
44 changes: 0 additions & 44 deletions ios/Classes/BarcodeHandler.swift

This file was deleted.

Loading

0 comments on commit 7db2758

Please sign in to comment.