feat(arc): 添加虹软人脸识别插件基础功能

- 集成虹软 ArcSoft Face SDK,提供人脸检测、识别、活体检测功能
- 实现 Android 平台原生插件,包含 ArcPlugin 和 FaceEngineManager
- 添加完整的人脸错误码枚举(586个错误码),覆盖 SDK 所有错误情况
- 创建人脸信息数据模型 FaceInfo,封装检测结果和特征数据
- 配置 Android 权限和依赖,包括相机、存储、网络等必要权限
- 添加方法通道实现,连接 Flutter 层与原生层通信
- 配置项目基础文件,包含 .gitignore、分析选项和元数据配置
- 实现单元测试框架,包含 Dart 和 Java 层的基本测试用例
- 添加示例应用配置,验证插件集成和基本功能使用
- 提供详细的开发指导文档 CLAUDE.md,说明架构和 API 使用方法
This commit is contained in:
2026-03-30 17:13:58 +08:00
parent fe851528df
commit 2a1cfd230d
50 changed files with 4357 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@@ -0,0 +1,33 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

30
.metadata Normal file
View File

@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "20f82749394e68bcfbbeee96bad384abaae09c13"
channel: "stable"
project_type: plugin
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
- platform: android
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

3
CHANGELOG.md Normal file
View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

121
CLAUDE.md Normal file
View File

@@ -0,0 +1,121 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Flutter 人脸识别插件,封装虹软 (ArcSoft) Face SDK提供 Android 平台的人脸检测、识别、活体检测功能。
## Build Commands
```bash
# Analyze Dart code
flutter analyze
# Run Dart unit tests
flutter test
# Analyze example app
cd example && flutter analyze lib/main.dart
# Run example app on Android device
cd example && flutter run
# Build example APK
cd example && flutter build apk
```
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Flutter (Dart) │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ arc.dart │───▶│ ArcPlatform │ (abstract) │
│ │ (Public API)│ └────────┬─────────┘ │
│ └─────────────┘ │ │
│ ┌────────▼─────────┐ │
│ │ MethodChannelArc │ │
│ │ (Method Channel) │ │
│ └────────┬─────────┘ │
└─────────────────────────────┼───────────────────────────────┘
│ Method Channel: "arc"
┌─────────────────────────────┼───────────────────────────────┐
│ Android (Java) │
│ ┌────────▼─────────┐ │
│ │ ArcPlugin │ │
│ │ (Method Handler) │ │
│ └────────┬─────────┘ │
│ ┌────────▼─────────┐ │
│ │ FaceEngineManager│ (Singleton) │
│ └────────┬─────────┘ │
│ ┌────────▼─────────┐ │
│ │ ArcSoft SDK │ │
│ │ (arcsoft_face.jar)│ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Key Files
| Layer | File | Purpose |
|-------|------|---------|
| Public API | `lib/arc.dart` | 用户调用的主要接口 |
| Platform Interface | `lib/arc_platform_interface.dart` | 平台抽象接口定义 |
| Method Channel | `lib/arc_method_channel.dart` | Flutter 与原生通信 |
| Android Plugin | `android/src/.../ArcPlugin.java` | 处理 Method Channel 调用 |
| Engine Manager | `android/src/.../FaceEngineManager.java` | 虹软 SDK 单例管理 |
| Data Model | `android/src/.../FaceInfo.java` | 人脸信息数据模型 |
| Error Codes | `android/src/.../FaceErrorCode.java` | 586个错误码枚举 |
## Adding New API
1. **Dart Interface**: Add method to `arc_platform_interface.dart`
2. **Dart Implementation**: Add method to `arc_method_channel.dart`
3. **Public API**: Expose method in `arc.dart`
4. **Android Handler**: Add case in `ArcPlugin.java` `onMethodCall()`
5. **Android Logic**: Add method in `FaceEngineManager.java`
## ArcSoft SDK Configuration
SDK 需要三个密钥(从虹软控制台获取):
- `appId`: 应用 ID
- `sdkKey`: SDK 密钥
- `activeKey`: 激活密钥
## API Methods
| Method | Purpose |
|--------|---------|
| `activeOnline()` | 在线激活 SDK |
| `init()` | 初始化人脸识别引擎 |
| `detectFaces()` | 人脸检测 + RGB 活体检测 |
| `extractFaceFeature()` | 提取人脸特征 (512字节) |
| `compareFaceFeature()` | 1:1 特征比对,返回相似度 |
| `registerFaceFeature()` | 注册特征到人脸库 (1:N) |
## Thresholds (Recommended by ArcSoft)
| 功能 | 阈值 |
|------|------|
| 人脸比对 (生活照) | 0.8 |
| 人脸比对 (证件照) | 0.82 |
| RGB 活体检测 | 0.5 |
| IR 活体检测 | 0.5 |
## Combined Mask Values
```java
ASF_FACE_DETECT = 0x00000001 // 人脸检测
ASF_FACE_RECOGNITION= 0x00000004 // 人脸识别
ASF_AGE = 0x00000008 // 年龄检测
ASF_GENDER = 0x00000010 // 性别检测
ASF_LIVENESS = 0x00000080 // RGB 活体检测
// 组合: 0x9D = 人脸检测 + 识别 + 年龄 + 性别 + 活体
```
## Image Format
- 格式: NV21 (Android camera default)
- 格式代码: 2050
- 宽度必须是4的倍数高度必须是2的倍数

1
LICENSE Normal file
View File

@@ -0,0 +1 @@
TODO: Add your license here.

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# arc
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.dev/to/develop-plugins),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

4
analysis_options.yaml Normal file
View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

9
android/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

59
android/build.gradle Normal file
View File

@@ -0,0 +1,59 @@
group = "com.xiarui.arc"
version = "1.0"
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.9.1")
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: "com.android.library"
android {
namespace = "com.xiarui.arc"
compileSdk = 36
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
defaultConfig {
minSdk = 24
}
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito:mockito-core:5.0.0")
}
testOptions {
unitTests.all {
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen {false}
showStandardStreams = true
}
}
}
}

Binary file not shown.

Binary file not shown.

1
android/settings.gradle Normal file
View File

@@ -0,0 +1 @@
rootProject.name = 'arc'

View File

@@ -0,0 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xiarui.arc">
<!-- 虹软 SDK 所需权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
</manifest>

View File

@@ -0,0 +1,586 @@
package com.xiarui.arc;
/**
* 虹软人脸识别 SDK 错误码枚举
* 映射 SDK 返回的错误码到可读描述
* 官方错误码文档https://ai.arcsoft.com.cn
*/
public enum FaceErrorCode {
// ==================== 通用错误码 ====================
/**
* 成功
*/
MOK(0, "0x0", "成功"),
/**
* 错误原因不明
*/
MERR_UNKNOWN(1, "0x1", "错误原因不明"),
/**
* 无效的参数
*/
MERR_INVALID_PARAM(2, "0x2", "无效的参数"),
/**
* 引擎不支持
*/
MERR_UNSUPPORTED(3, "0x3", "引擎不支持"),
/**
* 内存不足
*/
MERR_NO_MEMORY(4, "0x4", "内存不足"),
/**
* 状态错误
*/
MERR_BAD_STATE(5, "0x5", "状态错误"),
/**
* 用户取消相关操作
*/
MERR_USER_CANCEL(6, "0x6", "用户取消相关操作"),
/**
* 操作时间过期
*/
MERR_EXPIRED(7, "0x7", "操作时间过期"),
/**
* 用户暂停操作
*/
MERR_USER_PAUSE(8, "0x8", "用户暂停操作"),
/**
* 缓冲上溢
*/
MERR_BUFFER_OVERFLOW(9, "0x9", "缓冲上溢"),
/**
* 缓冲下溢
*/
MERR_BUFFER_UNDERFLOW(10, "0xA", "缓冲下溢"),
/**
* 存贮空间不足
*/
MERR_NO_DISKSPACE(11, "0xB", "存贮空间不足"),
/**
* 组件不存在
*/
MERR_COMPONENT_NOT_EXIST(12, "0xC", "组件不存在"),
/**
* 全局数据不存在
*/
MERR_GLOBAL_DATA_NOT_EXIST(13, "0xD", "全局数据不存在"),
// ==================== SDK 基础错误码 ====================
/**
* 无效的 APP_ID
*/
MERR_FSDK_INVALID_APP_ID(28673, "0x7001", "无效的 APP_ID"),
/**
* 无效的 SDK_KEY
*/
MERR_FSDK_INVALID_SDK_ID(28674, "0x7002", "无效的 SDK_KEY"),
/**
* APP_ID 和 SDK_KEY 不匹配
*/
MERR_FSDK_INVALID_ID_PAIR(28675, "0x7003", "APP_ID 和 SDK_KEY 不匹配"),
/**
* SDK_KEY 和使用的 SDK 不匹配
*/
MERR_FSDK_MISMATCH_ID_AND_SDK(28676, "0x7004", "SDK_KEY 和使用的 SDK 不匹配"),
/**
* 系统版本不被当前 SDK 所支持
*/
MERR_FSDK_SYSTEM_VERSION_UNSUPPORTED(28677, "0x7005", "系统版本不被当前 SDK 所支持"),
// ==================== 人脸识别基础错误码 ====================
/**
* 无效的输入内存
*/
MERR_FSDK_FR_INVALID_MEMORY_INFO(73729, "0x12001", "无效的输入内存"),
/**
* 无效的输入图像参数
*/
MERR_FSDK_FR_INVALID_IMAGE_INFO(73730, "0x12002", "无效的输入图像参数"),
/**
* 无效的脸部信息
*/
MERR_FSDK_FR_INVALID_FACE_INFO(73731, "0x12003", "无效的脸部信息"),
/**
* 当前设备无 GPU 可用
*/
MERR_FSDK_FR_NO_GPU_AVAILABLE(73732, "0x12004", "当前设备无 GPU 可用"),
/**
* 待比较的两个人脸特征的版本不一致
*/
MERR_FSDK_FR_MISMATCHED_FEATURE_LEVEL(73733, "0x12005", "待比较的两个人脸特征的版本不一致"),
// ==================== 人脸特征检测错误码 ====================
/**
* 人脸特征检测错误未知
*/
MERR_FSDK_FACEFEATURE_UNKNOWN(81921, "0x14001", "人脸特征检测错误未知"),
/**
* 人脸特征检测内存错误
*/
MERR_FSDK_FACEFEATURE_MEMORY(81922, "0x14002", "人脸特征检测内存错误"),
/**
* 人脸特征检测格式错误
*/
MERR_FSDK_FACEFEATURE_INVALID_FORMAT(81923, "0x14003", "人脸特征检测格式错误"),
/**
* 人脸特征检测参数错误
*/
MERR_FSDK_FACEFEATURE_INVALID_PARAM(81924, "0x14004", "人脸特征检测参数错误"),
/**
* 人脸特征检测结果置信度低
*/
MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL(81925, "0x14005", "人脸特征检测结果置信度低"),
/**
* 人脸特征检测结果操作过期
*/
MERR_FSDK_FACEFEATURE_EXPIRED(81926, "0x14006", "人脸特征检测结果操作过期"),
/**
* 人脸特征检测人脸丢失
*/
MERR_FSDK_FACEFEATURE_MISSFACE(81927, "0x14007", "人脸特征检测人脸丢失"),
/**
* 人脸特征检测没有人脸
*/
MERR_FSDK_FACEFEATURE_NO_FACE(81928, "0x14008", "人脸特征检测没有人脸"),
/**
* 人脸特征检测人脸信息错误
*/
MERR_FSDK_FACEFEATURE_FACEDATA(81929, "0x14009", "人脸特征检测人脸信息错误"),
// ==================== ASF 扩展错误码 ====================
/**
* Engine 不支持的检测属性
*/
MERR_ASF_EX_FEATURE_UNSUPPORTED_ON_INIT(86017, "0x15001", "Engine 不支持的检测属性"),
/**
* 需要检测的属性未初始化
*/
MERR_ASF_EX_FEATURE_UNINITED(86018, "0x15002", "需要检测的属性未初始化"),
/**
* 待获取的属性未在 process 中处理过
*/
MERR_ASF_EX_FEATURE_UNPROCESSED(86019, "0x15003", "待获取的属性未在 process 中处理过"),
/**
* PROCESS 不支持的检测属性
*/
MERR_ASF_EX_FEATURE_UNSUPPORTED_ON_PROCESS(86020, "0x15004", "PROCESS 不支持的检测属性"),
/**
* 无效的输入图像
*/
MERR_ASF_EX_INVALID_IMAGE_INFO(86021, "0x15005", "无效的输入图像"),
/**
* 无效的脸部信息
*/
MERR_ASF_EX_INVALID_FACE_INFO(86022, "0x15006", "无效的脸部信息"),
// ==================== 激活相关错误码 ====================
/**
* SDK 激活失败,请打开读写权限
*/
MERR_ASF_ACTIVATION_FAIL(90113, "0x16001", "SDK 激活失败,请打开读写权限"),
/**
* SDK 已激活
*/
MERR_ASF_ALREADY_ACTIVATED(90114, "0x16002", "SDK 已激活"),
/**
* SDK 未激活
*/
MERR_ASF_NOT_ACTIVATED(90115, "0x16003", "SDK 未激活"),
/**
* detectFaceScaleVal 不支持
*/
MERR_ASF_SCALE_NOT_SUPPORT(90116, "0x16004", "detectFaceScaleVal 不支持"),
/**
* 激活文件与 SDK 类型不匹配
*/
MERR_ASF_ACTIVEFILE_SDKTYPE_MISMATCH(90117, "0x16005", "激活文件与 SDK 类型不匹配"),
/**
* 设备不匹配
*/
MERR_ASF_DEVICE_MISMATCH(90118, "0x16006", "设备不匹配"),
/**
* 唯一标识不合法
*/
MERR_ASF_UNIQUE_IDENTIFIER_ILLEGAL(90119, "0x16007", "唯一标识不合法"),
/**
* 参数为空
*/
MERR_ASF_PARAM_NULL(90120, "0x16008", "参数为空"),
/**
* 版本不支持
*/
MERR_ASF_VERSION_NOT_SUPPORT(90122, "0x1600A", "版本不支持"),
/**
* 签名错误
*/
MERR_ASF_SIGN_ERROR(90123, "0x1600B", "签名错误"),
/**
* 激活信息保存异常
*/
MERR_ASF_DATABASE_ERROR(90124, "0x1600C", "激活信息保存异常"),
/**
* 唯一标识符校验失败
*/
MERR_ASF_UNIQUE_CHECKOUT_FAIL(90125, "0x1600D", "唯一标识符校验失败"),
/**
* 颜色空间不支持
*/
MERR_ASF_COLOR_SPACE_NOT_SUPPORT(90126, "0x1600E", "颜色空间不支持"),
/**
* 图片宽高不支持,宽度需四字节对齐
*/
MERR_ASF_IMAGE_WIDTH_HEIGHT_NOT_SUPPORT(90127, "0x1600F", "图片宽高不支持,宽度需四字节对齐"),
/**
* android.permission.READ_PHONE_STATE 权限被拒绝
*/
MERR_ASF_READ_PHONE_STATE_DENIED(90128, "0x16010", "android.permission.READ_PHONE_STATE 权限被拒绝"),
/**
* 激活数据被破坏,请删除激活文件,重新进行激活
*/
MERR_ASF_ACTIVATION_DATA_DESTROYED(90129, "0x16011", "激活数据被破坏,请删除激活文件,重新进行激活"),
/**
* 服务端未知错误
*/
MERR_ASF_SERVER_UNKNOWN_ERROR(90130, "0x16012", "服务端未知错误"),
/**
* android.permission.INTERNET 权限被拒绝
*/
MERR_ASF_INTERNET_DENIED(90131, "0x16013", "android.permission.INTERNET 权限被拒绝"),
/**
* 激活文件与 SDK 版本不匹配,请重新激活
*/
MERR_ASF_ACTIVEFILE_SDK_MISMATCH(90132, "0x16014", "激活文件与 SDK 版本不匹配,请重新激活"),
/**
* 设备信息太少,不足以生成设备指纹
*/
MERR_ASF_DEVICEINFO_LESS(90133, "0x16015", "设备信息太少,不足以生成设备指纹"),
/**
* 客户端时间与服务器时间前后相差在 30 分钟以上
*/
MERR_ASF_LOCAL_TIME_NOT_CALIBRATED(90134, "0x16016", "客户端时间与服务器时间前后相差在 30 分钟以上"),
/**
* 数据校验异常
*/
MERR_ASF_APPID_DATA_DECRYPT(90135, "0x16017", "数据校验异常"),
/**
* 传入的 APP_ID 和 AppKey 与使用的 SDK 版本不一致
*/
MERR_ASF_APPID_APPKEY_SDK_MISMATCH(90136, "0x16018", "传入的 APP_ID 和 AppKey 与使用的 SDK 版本不一致"),
/**
* 短时间大量请求会被禁止请求30 分钟之后解封
*/
MERR_ASF_NO_REQUEST(90137, "0x16019", "短时间大量请求会被禁止请求30 分钟之后解封"),
/**
* 激活文件不存在
*/
MERR_ASF_ACTIVE_FILE_NO_EXIST(90138, "0x1601A", "激活文件不存在"),
/**
* 当前设备时间不正确,请调整设备时间
*/
MERR_ASF_CURRENT_DEVICE_TIME_INCORRECT(90139, "0x1601B", "当前设备时间不正确,请调整设备时间"),
/**
* 检测模型不支持
*/
MERR_ASF_DETECT_MODEL_UNSUPPORTED(90140, "0x1601C", "检测模型不支持"),
// ==================== 网络相关错误码 ====================
/**
* 无法解析主机地址
*/
MERR_ASF_NETWORK_COULDNT_RESOLVE_HOST(94209, "0x17001", "无法解析主机地址"),
/**
* 无法连接服务器
*/
MERR_ASF_NETWORK_COULDNT_CONNECT_SERVER(94210, "0x17002", "无法连接服务器"),
/**
* 网络连接超时
*/
MERR_ASF_NETWORK_CONNECT_TIMEOUT(94211, "0x17003", "网络连接超时"),
/**
* 网络未知错误
*/
MERR_ASF_NETWORK_UNKNOWN_ERROR(94212, "0x17004", "网络未知错误"),
// ==================== 激活密钥相关错误码 ====================
/**
* 无法连接激活服务器
*/
MERR_ASF_ACTIVEKEY_COULDNT_CONNECT_SERVER(98305, "0x18001", "无法连接激活服务器"),
/**
* 服务器系统错误
*/
MERR_ASF_ACTIVEKEY_SERVER_SYSTEM_ERROR(98306, "0x18002", "服务器系统错误"),
/**
* 请求参数错误
*/
MERR_ASF_ACTIVEKEY_POST_PARM_ERROR(98307, "0x18003", "请求参数错误"),
/**
* ACTIVE_KEY 与 APP_ID、SDK_KEY 不匹配
*/
MERR_ASF_ACTIVEKEY_PARM_MISMATCH(98308, "0x18004", "ACTIVE_KEY 与 APP_ID、SDK_KEY 不匹配"),
/**
* ACTIVE_KEY 已经被使用
*/
MERR_ASF_ACTIVEKEY_ACTIVEKEY_ACTIVATED(98309, "0x18005", "ACTIVE_KEY 已经被使用"),
/**
* ACTIVE_KEY 信息异常
*/
MERR_ASF_ACTIVEKEY_ACTIVEKEY_FORMAT_ERROR(98310, "0x18006", "ACTIVE_KEY 信息异常"),
/**
* ACTIVE_KEY 与 APP_ID 不匹配
*/
MERR_ASF_ACTIVEKEY_APPID_PARM_MISMATCH(98311, "0x18007", "ACTIVE_KEY 与 APP_ID 不匹配"),
/**
* SDK 与激活文件版本不匹配
*/
MERR_ASF_ACTIVEKEY_SDK_FILE_MISMATCH(98312, "0x18008", "SDK 与激活文件版本不匹配"),
/**
* ACTIVE_KEY 已过期
*/
MERR_ASF_ACTIVEKEY_EXPIRED(98313, "0x18009", "ACTIVE_KEY 已过期"),
// ==================== 离线授权相关错误码 ====================
/**
* 离线授权文件不存在或无读写权限
*/
MERR_ASF_LICENSE_FILE_NOT_EXIST(102401, "0x19001", "离线授权文件不存在或无读写权限"),
/**
* 离线授权文件已损坏
*/
MERR_ASF_LICENSE_FILE_DATA_DESTROYED(102402, "0x19002", "离线授权文件已损坏"),
/**
* 离线授权文件与 SDK 版本不匹配
*/
MERR_ASF_LICENSE_FILE_SDK_MISMATCH(102403, "0x19003", "离线授权文件与 SDK 版本不匹配"),
/**
* 离线授权文件与 SDK 信息不匹配
*/
MERR_ASF_LICENSE_FILEINFO_SDKINFO_MISMATCH(102404, "0x19004", "离线授权文件与 SDK 信息不匹配"),
/**
* 离线授权文件与设备指纹不匹配
*/
MERR_ASF_LICENSE_FILE_FINGERPRINT_MISMATCH(102405, "0x19005", "离线授权文件与设备指纹不匹配"),
/**
* 离线授权文件已过期
*/
MERR_ASF_LICENSE_FILE_EXPIRED(102406, "0x19006", "离线授权文件已过期"),
/**
* 离线授权文件不可用,本地原有激活文件可继续使用
*/
MERR_ASF_LOCAL_EXIST_USEFUL_ACTIVE_FILE(102407, "0x19007", "离线授权文件不可用,本地原有激活文件可继续使用"),
/**
* 离线授权文件版本过低
*/
MERR_ASF_LICENSE_FILE_VERSION_TOO_LOW(102408, "0x19008", "离线授权文件版本过低"),
// ==================== 人脸搜索相关错误码 ====================
/**
* 人脸列表为空
*/
MERR_ASF_SEARCH_EMPTY(151553, "0x25001", "人脸列表为空"),
/**
* 人脸不存在
*/
MERR_ASF_SEARCH_NO_EXIST(151554, "0x25002", "人脸不存在"),
/**
* 特征值长度不匹配
*/
MERR_ASF_SEARCH_FEATURE_SIZE_MISMATCH(151555, "0x25003", "特征值长度不匹配"),
/**
* 相似度异常
*/
MERR_ASF_SEARCH_LOW_CONFIDENCE(151556, "0x25004", "相似度异常"),
// ==================== 自定义扩展错误码 ====================
/**
* 引擎未初始化(自定义错误码)
*/
ENGINE_NOT_INITIALIZED(-1, "N/A", "引擎未初始化"),
/**
* 未知错误
*/
UNKNOWN(-2, "N/A", "未知错误");
/**
* 错误码(十进制)
*/
private final int code;
/**
* 错误码(十六进制)
*/
private final String hexCode;
/**
* 错误描述
*/
private final String message;
/**
* 构造函数
* @param code 错误码(十进制)
* @param hexCode 错误码(十六进制)
* @param message 错误描述
*/
FaceErrorCode(int code, String hexCode, String message) {
this.code = code;
this.hexCode = hexCode;
this.message = message;
}
/**
* 获取错误码(十进制)
* @return 错误码整数值
*/
public int getCode() {
return code;
}
/**
* 获取错误码(十六进制)
* @return 错误码十六进制字符串
*/
public String getHexCode() {
return hexCode;
}
/**
* 获取错误描述
* @return 错误描述信息
*/
public String getMessage() {
return message;
}
/**
* 根据错误码整数值获取对应的枚举
* @param code 错误码整数(十进制)
* @return 对应的 FaceErrorCode 枚举
*/
public static FaceErrorCode fromCode(int code) {
for (FaceErrorCode e : values()) {
if (e.code == code) {
return e;
}
}
return UNKNOWN;
}
/**
* 根据错误码获取错误描述
* @param code 错误码整数
* @return 错误描述信息
*/
public static String getMessageByCode(int code) {
FaceErrorCode errorCode = fromCode(code);
return errorCode.getMessage();
}
/**
* 判断是否为成功状态
* @param code 错误码整数
* @return true 表示成功false 表示失败
*/
public static boolean isSuccess(int code) {
return code == MOK.getCode();
}
/**
* 获取错误的详细信息(包含十进制、十六进制和描述)
* @return 格式化的错误信息
*/
public String getDetailMessage() {
return String.format("[%d / %s] %s", code, hexCode, message);
}
/**
* 获取错误的详细信息(包含十进制、十六进制和描述)
* @param code 错误码整数
* @return 格式化的错误信息
*/
public static String getDetailMessageByCode(int code) {
FaceErrorCode errorCode = fromCode(code);
return errorCode.getDetailMessage();
}
}

View File

@@ -0,0 +1,292 @@
package com.xiarui.arc;
import android.graphics.Rect;
import java.util.HashMap;
import java.util.Map;
/**
* 人脸信息类
* 封装虹软 SDK 返回的单个人脸检测结果
*/
public class FaceInfo {
/**
* 人脸矩形框(左、上、右、下坐标)
*/
private Rect faceRect;
/**
* 人脸角度0/90/180/270
*/
private int faceOrientation;
/**
* 人脸 IDVIDEO 模式下有效)
*/
private int faceId;
/**
* 人脸相似度(识别功能返回)
*/
private float similarity;
/**
* 活体检测结果(>0 表示活体)
*/
private float liveness;
/**
* 年龄(年龄估计功能返回)
*/
private int age;
/**
* 性别性别识别功能返回0=未知1=男2=女)
*/
private int gender;
/**
* 口罩佩戴状态(口罩检测功能返回,-1=未知0=未佩戴1=已佩戴)
*/
private int maskStatus;
/**
* 人脸特征数据(特征提取功能返回)
*/
private byte[] featureData;
/**
* 默认构造函数
*/
public FaceInfo() {
this.faceRect = new Rect();
this.faceOrientation = 0;
this.faceId = 0;
this.similarity = 0.0f;
this.liveness = 0.0f;
this.age = 0;
this.gender = 0;
this.maskStatus = -1;
this.featureData = null;
}
/**
* 构造函数
* @param faceRect 人脸矩形框
*/
public FaceInfo(Rect faceRect) {
this.faceRect = faceRect;
this.faceOrientation = 0;
this.faceId = 0;
this.similarity = 0.0f;
this.liveness = 0.0f;
this.age = 0;
this.gender = 0;
this.maskStatus = -1;
this.featureData = null;
}
/**
* 获取人脸矩形框
* @return 人脸矩形框 Rect 对象
*/
public Rect getFaceRect() {
return faceRect;
}
/**
* 设置人脸矩形框
* @param faceRect 人脸矩形框
*/
public void setFaceRect(Rect faceRect) {
this.faceRect = faceRect;
}
/**
* 设置人脸矩形 left 坐标
* @param left 左坐标
*/
public void setLeft(int left) {
this.faceRect = new Rect(left, faceRect.top, faceRect.right, faceRect.bottom);
}
/**
* 设置人脸矩形 top 坐标
* @param top 上坐标
*/
public void setTop(int top) {
this.faceRect = new Rect(faceRect.left, top, faceRect.right, faceRect.bottom);
}
/**
* 设置人脸矩形 right 坐标
* @param right 右坐标
*/
public void setRight(int right) {
this.faceRect = new Rect(faceRect.left, faceRect.top, right, faceRect.bottom);
}
/**
* 设置人脸矩形 bottom 坐标
* @param bottom 下坐标
*/
public void setBottom(int bottom) {
this.faceRect = new Rect(faceRect.left, faceRect.top, faceRect.right, bottom);
}
/**
* 设置人脸角度
* @param orient 人脸角度
*/
public void setOrient(int orient) {
this.faceOrientation = orient;
}
/**
* 获取人脸角度
* @return 人脸角度0/90/180/270
*/
public int getFaceOrientation() {
return faceOrientation;
}
/**
* 设置人脸角度
* @param faceOrientation 人脸角度
*/
public void setFaceOrientation(int faceOrientation) {
this.faceOrientation = faceOrientation;
}
/**
* 获取人脸 ID
* @return 人脸 ID
*/
public int getFaceId() {
return faceId;
}
/**
* 设置人脸 ID
* @param faceId 人脸 ID
*/
public void setFaceId(int faceId) {
this.faceId = faceId;
}
/**
* 获取人脸相似度
* @return 人脸相似度0.0-1.0
*/
public float getSimilarity() {
return similarity;
}
/**
* 设置人脸相似度
* @param similarity 人脸相似度
*/
public void setSimilarity(float similarity) {
this.similarity = similarity;
}
/**
* 获取活体检测结果
* @return 活体值(>0 表示活体)
*/
public float getLiveness() {
return liveness;
}
/**
* 设置活体检测结果
* @param liveness 活体值
*/
public void setLiveness(float liveness) {
this.liveness = liveness;
}
/**
* 获取年龄
* @return 年龄值
*/
public int getAge() {
return age;
}
/**
* 设置年龄
* @param age 年龄值
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取性别
* @return 性别0=未知1=男2=女)
*/
public int getGender() {
return gender;
}
/**
* 设置性别
* @param gender 性别值
*/
public void setGender(int gender) {
this.gender = gender;
}
/**
* 获取口罩佩戴状态
* @return 口罩状态(-1=未知0=未佩戴1=已佩戴)
*/
public int getMaskStatus() {
return maskStatus;
}
/**
* 设置口罩佩戴状态
* @param maskStatus 口罩状态值
*/
public void setMaskStatus(int maskStatus) {
this.maskStatus = maskStatus;
}
/**
* 获取人脸特征数据
* @return 人脸特征数据字节数组
*/
public byte[] getFeatureData() {
return featureData;
}
/**
* 设置人脸特征数据
* @param featureData 人脸特征数据
*/
public void setFeatureData(byte[] featureData) {
this.featureData = featureData;
}
/**
* 将 FaceInfo 转换为 Map 对象,用于 Flutter 端调用
* @return 包含人脸信息的 Map
*/
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("left", faceRect.left);
map.put("top", faceRect.top);
map.put("right", faceRect.right);
map.put("bottom", faceRect.bottom);
map.put("orientation", faceOrientation);
map.put("faceId", faceId);
map.put("similarity", similarity);
map.put("liveness", liveness);
map.put("age", age);
map.put("gender", gender);
map.put("maskStatus", maskStatus);
map.put("featureData", featureData != null ? featureData : new byte[0]);
return map;
}
}

Binary file not shown.

View File

@@ -0,0 +1,29 @@
package com.xiarui.arc;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import org.junit.Test;
/**
* This demonstrates a simple unit test of the Java portion of this plugin's implementation.
*
* Once you have built the plugin's example app, you can run these tests from the command
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
* you can run them directly from IDEs that support JUnit such as Android Studio.
*/
public class ArcPluginTest {
@Test
public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
ArcPlugin plugin = new ArcPlugin();
final MethodCall call = new MethodCall("getPlatformVersion", null);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
plugin.onMethodCall(call, mockResult);
verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
}
}

File diff suppressed because it is too large Load Diff

45
example/.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

16
example/README.md Normal file
View File

@@ -0,0 +1,16 @@
# arc_example
Demonstrates how to use the arc plugin.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

14
example/android/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View File

@@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.xiarui.arc_example"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.xiarui.arc_example"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,50 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 摄像头权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application
android:label="arc_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@@ -0,0 +1,6 @@
package com.xiarui.arc_example;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,24 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip

View File

@@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
include(":app")

View File

@@ -0,0 +1,25 @@
// This is a basic Flutter integration test.
//
// Since integration tests run in a full Flutter application, they can interact
// with the host side of a plugin implementation, unlike Dart unit tests.
//
// For more information about Flutter integration tests, please see
// https://flutter.dev/to/integration-testing
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:arc/arc.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
final Arc plugin = Arc();
final String? version = await plugin.getPlatformVersion();
// The version string depends on the host platform running the test, so
// just assert that some non-empty string is returned.
expect(version?.isNotEmpty, true);
});
}

456
example/pubspec.lock Normal file
View File

@@ -0,0 +1,456 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
arc:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.0.1"
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
camera:
dependency: "direct main"
description:
name: camera
sha256: "4142a19a38e388d3bab444227636610ba88982e36dff4552d5191a86f65dc437"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.4"
camera_android_camerax:
dependency: transitive
description:
name: camera_android_camerax
sha256: "8516fe308bc341a5067fb1a48edff0ddfa57c0d3cdcc9dbe7ceca3ba119e2577"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.30"
camera_avfoundation:
dependency: transitive
description:
name: camera_avfoundation
sha256: "11b4aee2f5e5e038982e152b4a342c749b414aa27857899d20f4323e94cb5f0b"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.23+2"
camera_platform_interface:
dependency: transitive
description:
name: camera_platform_interface
sha256: "98cfc9357e04bad617671b4c1f78a597f25f08003089dd94050709ae54effc63"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.12.0"
camera_web:
dependency: transitive
description:
name: camera_web
sha256: "57f49a635c8bf249d07fb95eb693d7e4dda6796dedb3777f9127fb54847beba7"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.5+3"
characters:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.19.1"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.5+2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.9"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.3"
ffi:
dependency: transitive
description:
name: ffi
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.0.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_driver:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.34"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
integration_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.1.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.16.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.0"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
process:
dependency: transitive
description:
name: process
sha256: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.5"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.5"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.23"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.6"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.2"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
sync_http:
dependency: transitive
description:
name: sync_http
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.6"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.flutter-io.cn"
source: hosted
version: "15.0.2"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
webdriver:
dependency: transitive
description:
name: webdriver
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
sdks:
dart: ">=3.9.0 <4.0.0"
flutter: ">=3.35.0"

View File

@@ -0,0 +1,27 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:arc_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data!.startsWith('Running on:'),
),
findsOneWidget,
);
});
}

70
pubspec.yaml Normal file
View File

@@ -0,0 +1,70 @@
name: arc
description: "A new Flutter project."
version: 0.0.1
homepage:
environment:
sdk: ^3.9.0
flutter: '>=3.3.0'
dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
# which should be registered in the plugin registry. This is required for
# using method channels.
# The Android 'package' specifies package in which the registered class is.
# This is required for using method channels on Android.
# The 'ffiPlugin' specifies that native code should be built and bundled.
# This is required for using `dart:ffi`.
# All these are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: com.xiarui.arc
pluginClass: ArcPlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,27 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:arc/arc_method_channel.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
MethodChannelArc platform = MethodChannelArc();
const MethodChannel channel = MethodChannel('arc');
setUp(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(MethodCall methodCall) async {
return '42';
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
});
test('getPlatformVersion', () async {
expect(await platform.getPlatformVersion(), '42');
});
}