99 lines
3.6 KiB
Dart
99 lines
3.6 KiB
Dart
/// 扫码能力的对外 API。来源:conti-docs/07-native-integration.md。
|
||||
|
|
///
|
|||
|
|
/// **调用方只允许 import 这个文件**,不允许直接 import `src/generated/` 里的
|
|||
|
|
/// 生成代码(07 §使用规则)。调用方包括 `feature_scan` 和 `core_webview` 的
|
|||
|
|
/// JSBridge——后者正是 01 里「`core_*` 允许依赖 `native_*`」这条例外存在的
|
|||
|
|
/// 原因。
|
|||
|
|
///
|
|||
|
|
/// 本包按 01 的硬约束**不依赖仓库内任何其他包**(连 `core_foundation` 也不),
|
|||
|
|
/// 所以这里抛的是包内自定义的 [NativeScanException];转成统一错误体系里的
|
|||
|
|
/// `NativeException` 由调用方完成。
|
|||
|
|
library;
|
|||
|
|
|
|||
|
|
import 'package:flutter/services.dart';
|
|||
|
|
|
|||
|
|
import 'src/generated/scan_api.g.dart';
|
|||
|
|
|
|||
|
|
export 'src/generated/scan_api.g.dart' show ScanMode, ScanOptions, ScanResult;
|
|||
|
|
|
|||
|
|
/// 扫码失败的错误码。
|
|||
|
|
///
|
|||
|
|
/// 取值与 12 的 `NativeException.code` 对齐,调用方可以直接透传。
|
|||
|
|
abstract final class NativeScanErrorCode {
|
|||
|
|
/// 用户主动取消。
|
|||
|
|
///
|
|||
|
|
/// 这不是异常流程,调用方通常应当静默返回,**不弹错误提示、不上报**。
|
|||
|
|
static const String cancelled = 'CANCELLED';
|
|||
|
|
|
|||
|
|
/// 相机权限被拒绝。
|
|||
|
|
static const String permissionDenied = 'PERMISSION_DENIED';
|
|||
|
|
|
|||
|
|
/// 能力暂时不可用(相机被占用、初始化失败等)。
|
|||
|
|
static const String unavailable = 'UNAVAILABLE';
|
|||
|
|
|
|||
|
|
/// 当前平台没有实现。
|
|||
|
|
///
|
|||
|
|
/// 见 07 §「OHOS 后续演进」:**不允许静默返回空值或占位假数据**——
|
|||
|
|
/// 静默返回会让"这个平台其实没实现"的问题一直藏到用户手里。
|
|||
|
|
static const String unsupportedPlatform = 'UNSUPPORTED_PLATFORM';
|
|||
|
|
|
|||
|
|
/// 超时。
|
|||
|
|
static const String timeout = 'TIMEOUT';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 扫码相关的异常。
|
|||
|
|
class NativeScanException implements Exception {
|
|||
|
|
/// [code] 取自 [NativeScanErrorCode]。
|
|||
|
|
const NativeScanException(this.code, this.message);
|
|||
|
|
|
|||
|
|
/// 稳定错误码。
|
|||
|
|
final String code;
|
|||
|
|
|
|||
|
|
/// 面向开发者的描述。**不要直接展示给用户**——文案由调用方按 12 的
|
|||
|
|
/// `ErrorPresenter` 决定。
|
|||
|
|
final String message;
|
|||
|
|
|
|||
|
|
/// 是否是用户主动取消。
|
|||
|
|
bool get isCancelled => code == NativeScanErrorCode.cancelled;
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
String toString() => 'NativeScanException($code): $message';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 扫码。
|
|||
|
|
class NativeScan {
|
|||
|
|
/// [api] 仅供测试注入;生产走默认实例。
|
|||
|
|
NativeScan({ScanHostApi? api}) : _api = api ?? ScanHostApi();
|
|||
|
|
|
|||
|
|
final ScanHostApi _api;
|
|||
|
|
|
|||
|
|
/// 打开扫码页并等待一次结果。
|
|||
|
|
///
|
|||
|
|
/// 用户取消时抛 [NativeScanException](`code == CANCELLED`)而不是返回 null——
|
|||
|
|
/// 「取消」和「扫到了空字符串」必须能区分开。
|
|||
|
|
Future<ScanResult> startScan(ScanOptions options) async {
|
|||
|
|
try {
|
|||
|
|
return await _api.startScan(options);
|
|||
|
|
} on PlatformException catch (e) {
|
|||
|
|
// 原生异常类型不外泄(07 §使用规则)。
|
|||
|
|
throw NativeScanException(e.code, e.message ?? '扫码失败');
|
|||
|
|
} on MissingPluginException {
|
|||
|
|
throw const NativeScanException(NativeScanErrorCode.unsupportedPlatform, '当前平台未实现扫码能力');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 当前平台是否支持指定识别类型。
|
|||
|
|
///
|
|||
|
|
/// 调用方应当先查这个再决定要不要显示入口,而不是等 [startScan] 抛错——
|
|||
|
|
/// 车牌识别的技术路径尚未确定(07 待确认项),首版可能只有部分平台支持。
|
|||
|
|
Future<bool> isModeSupported(ScanMode mode) async {
|
|||
|
|
try {
|
|||
|
|
return await _api.isModeSupported(mode);
|
|||
|
|
} on PlatformException {
|
|||
|
|
return false;
|
|||
|
|
} on MissingPluginException {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|