app scaffold
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
.idea/
|
||||
.vagrant/
|
||||
.sconsign.dblite
|
||||
.svn/
|
||||
|
||||
.DS_Store
|
||||
*.swp
|
||||
profile
|
||||
|
||||
DerivedData/
|
||||
build/
|
||||
GeneratedPluginRegistrant.h
|
||||
GeneratedPluginRegistrant.m
|
||||
|
||||
.generated/
|
||||
|
||||
*.pbxuser
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.perspectivev3
|
||||
|
||||
!default.pbxuser
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.perspectivev3
|
||||
|
||||
xcuserdata
|
||||
|
||||
*.moved-aside
|
||||
|
||||
*.pyc
|
||||
*sync/
|
||||
Icon?
|
||||
.tags*
|
||||
|
||||
/Flutter/Generated.xcconfig
|
||||
/Flutter/ephemeral/
|
||||
/Flutter/flutter_export_environment.sh
|
||||
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint native_scan.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'native_scan'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A new Flutter plugin project.'
|
||||
s.description = <<-DESC
|
||||
A new Flutter plugin project.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'native_scan/Sources/native_scan/**/*'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '13.0'
|
||||
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||
s.swift_version = '5.0'
|
||||
|
||||
# If your plugin requires a privacy manifest, for example if it uses any
|
||||
# required reason APIs, update the PrivacyInfo.xcprivacy file to describe your
|
||||
# plugin's privacy impact, and then uncomment this line. For more information,
|
||||
# see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
|
||||
# s.resource_bundles = {'native_scan_privacy' => ['native_scan/Sources/native_scan/PrivacyInfo.xcprivacy']}
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
// swift-tools-version: 5.9
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "native_scan",
|
||||
platforms: [
|
||||
.iOS("13.0")
|
||||
],
|
||||
products: [
|
||||
.library(name: "native-scan", targets: ["native_scan"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "FlutterFramework", path: "../FlutterFramework")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "native_scan",
|
||||
dependencies: [
|
||||
.product(name: "FlutterFramework", package: "FlutterFramework")
|
||||
],
|
||||
resources: [
|
||||
// If your plugin requires a privacy manifest, for example if it uses any required
|
||||
// reason APIs, update the PrivacyInfo.xcprivacy file to describe your plugin's
|
||||
// privacy impact, and then uncomment these lines. For more information, see
|
||||
// https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
|
||||
// .process("PrivacyInfo.xcprivacy"),
|
||||
|
||||
// If you have other resources that need to be bundled with your plugin, refer to
|
||||
// the following instructions to add them:
|
||||
// https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
/// 扫码插件的 iOS 入口。
|
||||
///
|
||||
/// TODO(native): 手写原生实现本次不在范围内,见 conti-docs/07-native-integration.md。
|
||||
/// 补完时需要做的事:
|
||||
/// 1. 让某个类实现 pigeon 生成的 `ScanHostApi`(见 ScanApi.g.swift,不要手改生成物);
|
||||
/// 2. 在 register 里 `ScanHostApiSetup.setUp(binaryMessenger:api:)`;
|
||||
/// 3. Info.plist 需要 NSCameraUsageDescription,否则相机权限弹窗会直接崩;
|
||||
/// 4. 取消 / 权限拒绝 / 能力不可用必须回 FlutterError,code 用
|
||||
/// CANCELLED / PERMISSION_DENIED / UNAVAILABLE —— 与 Dart 侧
|
||||
/// NativeScanErrorCode 一一对应,**不允许返回占位假数据**。
|
||||
///
|
||||
/// 在此之前,Dart 侧调用会收到 MissingPluginException,
|
||||
/// 并被 NativeScan 转成 UNSUPPORTED_PLATFORM,属预期行为。
|
||||
public class NativeScanPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
// TODO(native): ScanHostApiSetup.setUp(binaryMessenger: registrar.messenger(), api: ScanApiImpl())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,432 @@
|
||||
// Autogenerated from Pigeon (v27.3.0), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
/// Error class for passing custom error details to Dart side.
|
||||
final class PigeonError: Error {
|
||||
let code: String
|
||||
let message: String?
|
||||
let details: Sendable?
|
||||
|
||||
init(code: String, message: String?, details: Sendable?) {
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.details = details
|
||||
}
|
||||
|
||||
var localizedDescription: String {
|
||||
return
|
||||
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
|
||||
}
|
||||
}
|
||||
|
||||
private func wrapResult(_ result: Any?) -> [Any?] {
|
||||
return [result]
|
||||
}
|
||||
|
||||
private func wrapError(_ error: Any) -> [Any?] {
|
||||
if let pigeonError = error as? PigeonError {
|
||||
return [
|
||||
pigeonError.code,
|
||||
pigeonError.message,
|
||||
pigeonError.details,
|
||||
]
|
||||
}
|
||||
if let flutterError = error as? FlutterError {
|
||||
return [
|
||||
flutterError.code,
|
||||
flutterError.message,
|
||||
flutterError.details,
|
||||
]
|
||||
}
|
||||
return [
|
||||
"\(error)",
|
||||
"\(Swift.type(of: error))",
|
||||
"Stacktrace: \(Thread.callStackSymbols)",
|
||||
]
|
||||
}
|
||||
|
||||
enum ScanApiPigeonInternal {
|
||||
static func isNullish(_ value: Any?) -> Bool {
|
||||
guard let innerValue = value else {
|
||||
return true
|
||||
}
|
||||
|
||||
if case Optional<Any>.some(Optional<Any>.none) = value {
|
||||
return true
|
||||
}
|
||||
|
||||
return innerValue is NSNull
|
||||
}
|
||||
static func doubleEquals(_ lhs: Double, _ rhs: Double) -> Bool {
|
||||
return (lhs.isNaN && rhs.isNaN) || lhs == rhs
|
||||
}
|
||||
|
||||
static func doubleHash(_ value: Double, _ hasher: inout Hasher) {
|
||||
if value.isNaN {
|
||||
hasher.combine(0x7FF8000000000000)
|
||||
} else {
|
||||
// Normalize -0.0 to 0.0
|
||||
hasher.combine(value == 0 ? 0 : value)
|
||||
}
|
||||
}
|
||||
|
||||
static func deepEquals(_ lhs: Any?, _ rhs: Any?) -> Bool {
|
||||
let cleanLhs = nilOrValue(lhs) as Any?
|
||||
let cleanRhs = nilOrValue(rhs) as Any?
|
||||
switch (cleanLhs, cleanRhs) {
|
||||
case (nil, nil):
|
||||
return true
|
||||
|
||||
case (nil, _), (_, nil):
|
||||
return false
|
||||
|
||||
case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs:
|
||||
return true
|
||||
|
||||
case is (Void, Void):
|
||||
return true
|
||||
|
||||
case (let lhsArray, let rhsArray) as ([Any?], [Any?]):
|
||||
guard lhsArray.count == rhsArray.count else { return false }
|
||||
for (index, element) in lhsArray.enumerated() {
|
||||
if !deepEquals(element, rhsArray[index]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case (let lhsArray, let rhsArray) as ([Double], [Double]):
|
||||
guard lhsArray.count == rhsArray.count else { return false }
|
||||
for (index, element) in lhsArray.enumerated() {
|
||||
if !doubleEquals(element, rhsArray[index]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case (let lhsDictionary, let rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
|
||||
guard lhsDictionary.count == rhsDictionary.count else { return false }
|
||||
for (lhsKey, lhsValue) in lhsDictionary {
|
||||
var found = false
|
||||
for (rhsKey, rhsValue) in rhsDictionary {
|
||||
if deepEquals(lhsKey, rhsKey) {
|
||||
if deepEquals(lhsValue, rhsValue) {
|
||||
found = true
|
||||
break
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found { return false }
|
||||
}
|
||||
return true
|
||||
|
||||
case (let lhs as Double, let rhs as Double):
|
||||
return doubleEquals(lhs, rhs)
|
||||
|
||||
case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable):
|
||||
return lhsHashable == rhsHashable
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func deepHash(value: Any?, hasher: inout Hasher) {
|
||||
let cleanValue = nilOrValue(value) as Any?
|
||||
if let cleanValue = cleanValue {
|
||||
if let doubleValue = cleanValue as? Double {
|
||||
doubleHash(doubleValue, &hasher)
|
||||
} else if let valueList = cleanValue as? [Any?] {
|
||||
for item in valueList {
|
||||
deepHash(value: item, hasher: &hasher)
|
||||
}
|
||||
} else if let valueList = cleanValue as? [Double] {
|
||||
for item in valueList {
|
||||
doubleHash(item, &hasher)
|
||||
}
|
||||
} else if let valueDict = cleanValue as? [AnyHashable: Any?] {
|
||||
var result = 0
|
||||
for (key, value) in valueDict {
|
||||
var entryKeyHasher = Hasher()
|
||||
deepHash(value: key, hasher: &entryKeyHasher)
|
||||
var entryValueHasher = Hasher()
|
||||
deepHash(value: value, hasher: &entryValueHasher)
|
||||
result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize())
|
||||
}
|
||||
hasher.combine(result)
|
||||
} else if let hashableValue = cleanValue as? AnyHashable {
|
||||
hasher.combine(hashableValue)
|
||||
} else {
|
||||
hasher.combine(String(describing: cleanValue))
|
||||
}
|
||||
} else {
|
||||
hasher.combine(0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func nilOrValue<T>(_ value: Any?) -> T? {
|
||||
if value is NSNull { return nil }
|
||||
return value as! T?
|
||||
}
|
||||
|
||||
|
||||
/// 识别类型。
|
||||
///
|
||||
/// **即使首版只做条码,这个参数也必须先留出来**(07 §「待确认:VIN 码与车牌
|
||||
/// 识别的技术路径」):车牌走的是专用 OCR、VIN 印刷字符走通用 OCR + 校验位
|
||||
/// 过滤,技术路径还没定。参数先在 schema 里占好位,后面加识别类型就不用改
|
||||
/// 接口签名——改签名意味着三端生成物和所有调用点一起动。
|
||||
enum ScanMode: Int, CaseIterable {
|
||||
/// 二维码 / 条形码(商品、库位)。
|
||||
case barcode = 0
|
||||
/// VIN 码。可能是 Code 39 条码,也可能只有印刷字符。
|
||||
case vin = 1
|
||||
/// 车牌。
|
||||
case plate = 2
|
||||
}
|
||||
|
||||
/// 扫码入参。
|
||||
///
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct ScanOptions: Hashable, CustomStringConvertible {
|
||||
/// 识别类型。
|
||||
var mode: ScanMode
|
||||
/// 超时毫秒数。null 表示不超时,由用户手动取消。
|
||||
var timeoutMs: Int64? = nil
|
||||
/// 是否默认打开闪光灯。
|
||||
var torchEnabled: Bool? = nil
|
||||
/// 扫码页标题。由调用方传,`native_scan` 不依赖任何 i18n 资源。
|
||||
var title: String? = nil
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> ScanOptions? {
|
||||
let mode = pigeonVar_list[0] as! ScanMode
|
||||
let timeoutMs: Int64? = nilOrValue(pigeonVar_list[1])
|
||||
let torchEnabled: Bool? = nilOrValue(pigeonVar_list[2])
|
||||
let title: String? = nilOrValue(pigeonVar_list[3])
|
||||
|
||||
return ScanOptions(
|
||||
mode: mode,
|
||||
timeoutMs: timeoutMs,
|
||||
torchEnabled: torchEnabled,
|
||||
title: title
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
mode,
|
||||
timeoutMs,
|
||||
torchEnabled,
|
||||
title,
|
||||
]
|
||||
}
|
||||
static func == (lhs: ScanOptions, rhs: ScanOptions) -> Bool {
|
||||
if Swift.type(of: lhs) != Swift.type(of: rhs) {
|
||||
return false
|
||||
}
|
||||
return ScanApiPigeonInternal.deepEquals(lhs.mode, rhs.mode) && ScanApiPigeonInternal.deepEquals(lhs.timeoutMs, rhs.timeoutMs) && ScanApiPigeonInternal.deepEquals(lhs.torchEnabled, rhs.torchEnabled) && ScanApiPigeonInternal.deepEquals(lhs.title, rhs.title)
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine("ScanOptions")
|
||||
ScanApiPigeonInternal.deepHash(value: mode, hasher: &hasher)
|
||||
ScanApiPigeonInternal.deepHash(value: timeoutMs, hasher: &hasher)
|
||||
ScanApiPigeonInternal.deepHash(value: torchEnabled, hasher: &hasher)
|
||||
ScanApiPigeonInternal.deepHash(value: title, hasher: &hasher)
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "ScanOptions(mode: \(String(describing: mode)), timeoutMs: \(String(describing: timeoutMs)), torchEnabled: \(String(describing: torchEnabled)), title: \(String(describing: title)))"
|
||||
}
|
||||
}
|
||||
|
||||
/// 扫码结果。
|
||||
///
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct ScanResult: Hashable, CustomStringConvertible {
|
||||
/// 实际生效的识别类型。
|
||||
var mode: ScanMode
|
||||
/// 识别到的文本。
|
||||
var value: String
|
||||
/// 从打开扫码页到出结果的耗时,供埋点用(见 13 的 `scan_succeeded`)。
|
||||
var durationMs: Int64
|
||||
/// 原始码制(如 `CODE_39` / `QR_CODE`)。OCR 路径下为 null。
|
||||
var rawFormat: String? = nil
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> ScanResult? {
|
||||
let mode = pigeonVar_list[0] as! ScanMode
|
||||
let value = pigeonVar_list[1] as! String
|
||||
let durationMs = pigeonVar_list[2] as! Int64
|
||||
let rawFormat: String? = nilOrValue(pigeonVar_list[3])
|
||||
|
||||
return ScanResult(
|
||||
mode: mode,
|
||||
value: value,
|
||||
durationMs: durationMs,
|
||||
rawFormat: rawFormat
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
mode,
|
||||
value,
|
||||
durationMs,
|
||||
rawFormat,
|
||||
]
|
||||
}
|
||||
static func == (lhs: ScanResult, rhs: ScanResult) -> Bool {
|
||||
if Swift.type(of: lhs) != Swift.type(of: rhs) {
|
||||
return false
|
||||
}
|
||||
return ScanApiPigeonInternal.deepEquals(lhs.mode, rhs.mode) && ScanApiPigeonInternal.deepEquals(lhs.value, rhs.value) && ScanApiPigeonInternal.deepEquals(lhs.durationMs, rhs.durationMs) && ScanApiPigeonInternal.deepEquals(lhs.rawFormat, rhs.rawFormat)
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine("ScanResult")
|
||||
ScanApiPigeonInternal.deepHash(value: mode, hasher: &hasher)
|
||||
ScanApiPigeonInternal.deepHash(value: value, hasher: &hasher)
|
||||
ScanApiPigeonInternal.deepHash(value: durationMs, hasher: &hasher)
|
||||
ScanApiPigeonInternal.deepHash(value: rawFormat, hasher: &hasher)
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "ScanResult(mode: \(String(describing: mode)), value: \(String(describing: value)), durationMs: \(String(describing: durationMs)), rawFormat: \(String(describing: rawFormat)))"
|
||||
}
|
||||
}
|
||||
|
||||
private class ScanApiPigeonCodecReader: FlutterStandardReader {
|
||||
override func readValue(ofType type: UInt8) -> Any? {
|
||||
switch type {
|
||||
case 129:
|
||||
let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
|
||||
if let enumResultAsInt = enumResultAsInt {
|
||||
return ScanMode(rawValue: enumResultAsInt)
|
||||
}
|
||||
return nil
|
||||
case 130:
|
||||
return ScanOptions.fromList(self.readValue() as! [Any?])
|
||||
case 131:
|
||||
return ScanResult.fromList(self.readValue() as! [Any?])
|
||||
default:
|
||||
return super.readValue(ofType: type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ScanApiPigeonCodecWriter: FlutterStandardWriter {
|
||||
override func writeValue(_ value: Any) {
|
||||
if let value = value as? ScanMode {
|
||||
super.writeByte(129)
|
||||
super.writeValue(value.rawValue)
|
||||
} else if let value = value as? ScanOptions {
|
||||
super.writeByte(130)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? ScanResult {
|
||||
super.writeByte(131)
|
||||
super.writeValue(value.toList())
|
||||
} else {
|
||||
super.writeValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ScanApiPigeonCodecReaderWriter: FlutterStandardReaderWriter {
|
||||
override func reader(with data: Data) -> FlutterStandardReader {
|
||||
return ScanApiPigeonCodecReader(data: data)
|
||||
}
|
||||
|
||||
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
|
||||
return ScanApiPigeonCodecWriter(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
class ScanApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
|
||||
static let shared = ScanApiPigeonCodec(readerWriter: ScanApiPigeonCodecReaderWriter())
|
||||
}
|
||||
|
||||
|
||||
/// Dart → 原生。
|
||||
///
|
||||
/// 用户取消、权限拒绝、平台未实现这三类都通过 `FlutterError` 抛出,
|
||||
/// 由 Dart 侧的公共 API 转成 `NativeScanException`——**原生异常类型
|
||||
/// (`PlatformException`)不允许直接抛到业务代码里**(07 §使用规则)。
|
||||
///
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol ScanHostApi {
|
||||
/// 打开扫码页并等待一次结果。
|
||||
///
|
||||
/// 用户取消时抛 code 为 `CANCELLED` 的错误,而不是返回 null——
|
||||
/// 「取消」和「扫到了空字符串」必须能区分开。
|
||||
func startScan(options: ScanOptions, completion: @escaping (Result<ScanResult, Error>) -> Void)
|
||||
/// 当前平台是否支持指定识别类型。
|
||||
///
|
||||
/// 车牌识别的技术路径未定(见上),首版可能只有部分平台支持;
|
||||
/// 调用方应当先查这个再决定要不要显示入口,而不是等 `startScan` 抛错。
|
||||
func isModeSupported(mode: ScanMode) throws -> Bool
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
class ScanHostApiSetup {
|
||||
static var codec: FlutterStandardMessageCodec { ScanApiPigeonCodec.shared }
|
||||
/// Sets up an instance of `ScanHostApi` to handle messages through the `binaryMessenger`.
|
||||
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ScanHostApi?, messageChannelSuffix: String = "") {
|
||||
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
|
||||
/// 打开扫码页并等待一次结果。
|
||||
///
|
||||
/// 用户取消时抛 code 为 `CANCELLED` 的错误,而不是返回 null——
|
||||
/// 「取消」和「扫到了空字符串」必须能区分开。
|
||||
let startScanChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.native_scan.ScanHostApi.startScan\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
startScanChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let optionsArg = args[0] as! ScanOptions
|
||||
api.startScan(options: optionsArg) { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
startScanChannel.setMessageHandler(nil)
|
||||
}
|
||||
/// 当前平台是否支持指定识别类型。
|
||||
///
|
||||
/// 车牌识别的技术路径未定(见上),首版可能只有部分平台支持;
|
||||
/// 调用方应当先查这个再决定要不要显示入口,而不是等 `startScan` 抛错。
|
||||
let isModeSupportedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.native_scan.ScanHostApi.isModeSupported\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
isModeSupportedChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let modeArg = args[0] as! ScanMode
|
||||
do {
|
||||
let result = try api.isModeSupported(mode: modeArg)
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isModeSupportedChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user