docs: map existing codebase
This commit is contained in:
160
.planning/codebase/ARCHITECTURE.md
Normal file
160
.planning/codebase/ARCHITECTURE.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Architecture
|
||||
|
||||
**Analysis Date:** 2026-03-30
|
||||
|
||||
## Pattern Overview
|
||||
|
||||
**Overall:** Flutter Plugin Architecture with Platform Interface Pattern
|
||||
|
||||
**Key Characteristics:**
|
||||
- Flutter plugin wrapping ArcSoft Face Recognition SDK (Android only)
|
||||
- Platform interface abstraction for cross-platform extensibility
|
||||
- Method channel communication between Dart and native Android
|
||||
- Singleton pattern for native FaceEngine management
|
||||
- Comprehensive error code enumeration for SDK error handling
|
||||
|
||||
## Layers
|
||||
|
||||
**Dart Public API Layer:**
|
||||
- Purpose: Provides user-facing API for face recognition operations
|
||||
- Location: `lib/arc.dart`
|
||||
- Contains: Public methods with detailed documentation
|
||||
- Depends on: `ArcPlatform` interface
|
||||
- Used by: Flutter applications consuming this plugin
|
||||
|
||||
**Platform Interface Layer:**
|
||||
- Purpose: Abstract interface for platform-specific implementations
|
||||
- Location: `lib/arc_platform_interface.dart`
|
||||
- Contains: Abstract method definitions, platform instance management
|
||||
- Depends on: `plugin_platform_interface` package
|
||||
- Used by: `Arc` class, mock implementations for testing
|
||||
|
||||
**Method Channel Layer:**
|
||||
- Purpose: Implements platform interface using Flutter method channels
|
||||
- Location: `lib/arc_method_channel.dart`
|
||||
- Contains: Method channel invocation logic, parameter marshalling
|
||||
- Depends on: Flutter `MethodChannel`, `ArcPlatform` interface
|
||||
- Used by: Default platform instance registration
|
||||
|
||||
**Android Native Plugin Layer:**
|
||||
- Purpose: Handles method channel calls, dispatches to engine manager
|
||||
- Location: `android/src/main/java/com/xiarui/arc/ArcPlugin.java`
|
||||
- Contains: Method call handler, parameter validation, result marshalling
|
||||
- Depends on: `FaceEngineManager`, Flutter plugin APIs
|
||||
- Used by: Flutter engine via plugin registration
|
||||
|
||||
**Android Engine Manager Layer:**
|
||||
- Purpose: Manages ArcSoft FaceEngine singleton, executes SDK operations
|
||||
- Location: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
|
||||
- Contains: SDK activation, engine lifecycle, face detection/extraction/comparison logic
|
||||
- Depends on: ArcSoft SDK (`arcsoft_face.jar`)
|
||||
- Used by: `ArcPlugin` handler methods
|
||||
|
||||
**Data Model Layer:**
|
||||
- Purpose: Encapsulates face information and error codes
|
||||
- Location: `android/src/main/java/com/xiarui/arc/FaceInfo.java`, `FaceErrorCode.java`
|
||||
- Contains: Data structures for face results, comprehensive error code enumeration
|
||||
- Depends on: Android SDK (`Rect` class)
|
||||
- Used by: All native layers for data transfer
|
||||
|
||||
## Data Flow
|
||||
|
||||
**SDK Activation Flow:**
|
||||
|
||||
1. Flutter app calls `Arc.activeOnline(appId, sdkKey, activeKey)`
|
||||
2. `Arc` delegates to `ArcPlatform.instance.activeOnline()`
|
||||
3. `MethodChannelArc` invokes method channel with `'activeOnline'`
|
||||
4. `ArcPlugin.handleActiveOnline()` validates parameters
|
||||
5. `FaceEngineManager.getInstance().activeOnline()` calls ArcSoft SDK
|
||||
6. Error code mapped to `FaceErrorCode` enum
|
||||
7. Result Map returned through method channel to Flutter
|
||||
|
||||
**Face Detection Flow:**
|
||||
|
||||
1. Flutter app calls `Arc.detectFaces(data, width, height, format)`
|
||||
2. Method channel invokes `'detectFaces'` with byte array payload
|
||||
3. `ArcPlugin.handleDetectFaces()` validates image parameters
|
||||
4. `FaceEngineManager.detectFaces()` executes SDK face detection
|
||||
5. RGB liveness detection processed via `faceEngine.process()` and `getLiveness()`
|
||||
6. `FaceInfo` list converted to Map structure via `convertFaceInfoToList()`
|
||||
7. Result includes faceList, rgbLiveness, isRgbAlive
|
||||
|
||||
**Face Feature Extraction Flow:**
|
||||
|
||||
1. Flutter app calls `Arc.extractFaceFeature()` with face rect from detection
|
||||
2. Native layer creates `FaceInfo` object from rect coordinates
|
||||
3. `FaceEngineManager.extractFaceFeature()` calls SDK extract method
|
||||
4. Feature data (byte array) returned in result Map
|
||||
|
||||
**Feature Comparison Flow:**
|
||||
|
||||
1. Flutter app calls `Arc.compareFaceFeature(featureData1, featureData2, compareModel)`
|
||||
2. Native layer creates `FaceFeature` objects from byte arrays
|
||||
3. `FaceEngineManager.compareFaceFeature()` executes SDK comparison
|
||||
4. Similarity score (0.0-1.0) returned in result Map
|
||||
|
||||
**State Management:**
|
||||
- Singleton `FaceEngineManager` holds `FaceEngine` instance
|
||||
- Engine initialization state tracked via `isInitialized` flag
|
||||
- Last RGB liveness result cached in `lastRgbLiveness` field
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
**ArcPlatform Interface:**
|
||||
- Purpose: Platform-agnostic interface for face recognition operations
|
||||
- Examples: `lib/arc_platform_interface.dart`
|
||||
- Pattern: Platform Interface pattern with token verification
|
||||
|
||||
**FaceEngineManager Singleton:**
|
||||
- Purpose: Centralized management of ArcSoft FaceEngine lifecycle
|
||||
- Examples: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
|
||||
- Pattern: Singleton with synchronized getInstance()
|
||||
|
||||
**FaceErrorCode Enumeration:**
|
||||
- Purpose: Maps SDK numeric error codes to human-readable messages
|
||||
- Examples: `android/src/main/java/com/xiarui/arc/FaceErrorCode.java`
|
||||
- Pattern: Enum with code lookup via `fromCode()`
|
||||
|
||||
**FaceInfo Data Model:**
|
||||
- Purpose: Represents single face detection result
|
||||
- Examples: `android/src/main/java/com/xiarui/arc/FaceInfo.java`
|
||||
- Pattern: JavaBean with `toMap()` for Flutter serialization
|
||||
|
||||
## Entry Points
|
||||
|
||||
**Flutter Plugin Entry:**
|
||||
- Location: `lib/arc.dart`
|
||||
- Triggers: Flutter app imports and instantiates `Arc` class
|
||||
- Responsibilities: Public API facade, delegates to platform interface
|
||||
|
||||
**Android Plugin Registration:**
|
||||
- Location: `android/src/main/java/com/xiarui/arc/ArcPlugin.java`
|
||||
- Triggers: Flutter engine attaches plugin via `onAttachedToEngine()`
|
||||
- Responsibilities: Method channel setup, call dispatching
|
||||
|
||||
**Example App Entry:**
|
||||
- Location: `example/lib/main.dart`
|
||||
- Triggers: App launch via `void main()`
|
||||
- Responsibilities: Demonstrates SDK activation, engine init, camera-based face detection
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Strategy:** Consistent Map return structure with success/error fields
|
||||
|
||||
**Patterns:**
|
||||
- All public methods return `Map<String, dynamic>?` with keys: `success`, `errorCode`, `message`
|
||||
- Native layer uses `FaceErrorCode.fromCode()` for error mapping
|
||||
- Custom error codes for internal failures (e.g., `ENGINE_NOT_INITIALIZED = -1`)
|
||||
- Flutter layer receives structured error info, no exception throwing for SDK errors
|
||||
- Platform exceptions caught in example app with `PlatformException` handler
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
**Logging:** Android native uses `android.util.Log` with tag "FaceEngineManager"
|
||||
**Validation:** Native methods validate engine initialization, image dimensions (width %4, height %2 for NV21)
|
||||
**Authentication:** SDK requires online activation with appId/sdkKey/activeKey from ArcSoft console
|
||||
**Image Format:** NV21 (format code 2050) is primary supported format for camera streams
|
||||
|
||||
---
|
||||
|
||||
*Architecture analysis: 2026-03-30*
|
||||
195
.planning/codebase/CONCERNS.md
Normal file
195
.planning/codebase/CONCERNS.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Codebase Concerns
|
||||
|
||||
**Analysis Date:** 2026-03-30
|
||||
|
||||
## Tech Debt
|
||||
|
||||
**iOS Platform Support Missing:**
|
||||
- Issue: The plugin only supports Android platform; iOS implementation is completely absent
|
||||
- Files: `pubspec.yaml` (no iOS platform definition), `lib/arc_platform_interface.dart`, `lib/arc_method_channel.dart`
|
||||
- Impact: Plugin cannot be used in iOS applications; limits market reach
|
||||
- Fix approach: Create iOS native implementation using Swift/Objective-C with ArcSoft SDK iOS bindings
|
||||
|
||||
**Unimplemented Mock Methods in Tests:**
|
||||
- Issue: `MockArcPlatform` in test file does not implement `registerFaceFeature` and `registerFaceFeatureBatch` methods
|
||||
- Files: `test/arc_test.dart` (lines 8-60)
|
||||
- Impact: Tests will fail when calling registration methods; incomplete test coverage
|
||||
- Fix approach: Add mock implementations for all missing methods in `MockArcPlatform`
|
||||
|
||||
**Placeholder Documentation:**
|
||||
- Issue: CHANGELOG.md and LICENSE files contain placeholder TODO text
|
||||
- Files: `CHANGELOG.md` (line 3), `LICENSE` (line 1)
|
||||
- Impact: Project appears incomplete; may confuse users about licensing
|
||||
- Fix approach: Replace placeholder text with actual content
|
||||
|
||||
## Known Bugs
|
||||
|
||||
**No explicit engine disposal:**
|
||||
- Symptoms: `FaceEngine` instance in `FaceEngineManager` is never nullified after `unInit()`
|
||||
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 268-278)
|
||||
- Trigger: Calling `unInit()` then attempting to reuse engine
|
||||
- Workaround: Re-initialize engine after disposal
|
||||
|
||||
**State inconsistency after initialization failure:**
|
||||
- Symptoms: If `init()` fails, `isInitialized` remains false but `faceEngine` object still exists
|
||||
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 141-159)
|
||||
- Trigger: Engine initialization with invalid parameters
|
||||
- Workaround: Check both `isInitialized` flag and error code before proceeding
|
||||
|
||||
## Security Considerations
|
||||
|
||||
**Hardcoded SDK Credentials in Example:**
|
||||
- Risk: Real SDK credentials (appId, sdkKey, activeKey) hardcoded in example application
|
||||
- Files: `example/lib/main.dart` (lines 41-43)
|
||||
```dart
|
||||
final String _appId = '4nPFuS2TYAQh9werHL2qbKjtTH9nnoixk7G6yqSUyjVH';
|
||||
final String _sdkKey = 'aWMTT3coxNQFETg9f3BGHCiBznAApXmcHFF3J5yQbsZ';
|
||||
final String _activeKey = 'NEAT7AU4KLCEK682';
|
||||
```
|
||||
- Current mitigation: Comment instructs users to replace with own keys (line 286)
|
||||
- Recommendations: Use environment variables or config file; never commit real credentials; add `.env.example` template
|
||||
|
||||
**Credential Transmission via Method Channel:**
|
||||
- Risk: Sensitive credentials (appId, sdkKey, activeKey) passed through method channel without encryption
|
||||
- Files: `lib/arc_method_channel.dart` (lines 24-28), `android/src/main/java/com/xiarui/arc/ArcPlugin.java` (lines 95-97)
|
||||
- Current mitigation: None
|
||||
- Recommendations: Credentials are passed to native SDK which handles secure activation; acceptable for current architecture
|
||||
|
||||
**Feature Data Privacy:**
|
||||
- Risk: Face feature data transmitted between Flutter and native layers could be intercepted
|
||||
- Files: `lib/arc_method_channel.dart`, `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
|
||||
- Current mitigation: Data stays within app boundary
|
||||
- Recommendations: Ensure no logging of feature data; consider encryption for stored features
|
||||
|
||||
## Performance Bottlenecks
|
||||
|
||||
**Large Binary Data Transfer:**
|
||||
- Problem: Image data (NV21 format, potentially several MB per frame) transferred through method channel for each detection
|
||||
- Files: `lib/arc_method_channel.dart` (lines 55-61), `android/src/main/java/com/xiarui/arc/ArcPlugin.java` (lines 154-157)
|
||||
- Cause: Method channel serialization overhead for large byte arrays
|
||||
- Improvement path: Consider using FFI for direct memory access; reduce image resolution; implement frame skipping
|
||||
|
||||
**Real-time Detection Frame Rate:**
|
||||
- Problem: `_isDetecting` flag prevents concurrent processing but may cause frame drops
|
||||
- Files: `example/lib/main.dart` (lines 406-407, 471-473)
|
||||
- Cause: Sequential processing - new frames wait until current detection completes
|
||||
- Improvement path: Implement queue-based processing with configurable frame skip; use dedicated detection thread
|
||||
|
||||
**Feature Extraction Overhead:**
|
||||
- Problem: Feature extraction called synchronously, blocking UI thread potential
|
||||
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 365-366)
|
||||
- Cause: SDK operation runs on method channel thread
|
||||
- Improvement path: Move heavy processing to background thread pool; provide async callbacks
|
||||
|
||||
## Fragile Areas
|
||||
|
||||
**NV21 Format Constraints:**
|
||||
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 179-189)
|
||||
- Why fragile: Image width must be multiple of 4, height must be multiple of 2 for NV21 format - violation causes runtime errors
|
||||
- Safe modification: Always validate dimensions before calling SDK; add Flutter-side validation
|
||||
- Test coverage: No tests for invalid dimension handling
|
||||
|
||||
**Singleton State Management:**
|
||||
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 54-59)
|
||||
- Why fragile: Singleton pattern with mutable `lastRgbLiveness` field; concurrent access risk
|
||||
- Safe modification: Add synchronization for mutable fields; or use ThreadLocal for liveness results
|
||||
- Test coverage: No concurrent access tests
|
||||
|
||||
**Camera Preview Orientation:**
|
||||
- Files: `example/lib/main.dart` (line 556)
|
||||
- Why fragile: Fixed `quarterTurns: -1` rotation may not work for all devices/orientations
|
||||
- Safe modification: Calculate rotation based on device orientation and camera sensor orientation
|
||||
- Test coverage: No tests for different device orientations
|
||||
|
||||
## Scaling Limits
|
||||
|
||||
**Face Database Size:**
|
||||
- Current capacity: Depends on ArcSoft SDK limits (typically 50,000 faces)
|
||||
- Limit: Memory consumption increases with registered faces; search time increases
|
||||
- Scaling path: Implement pagination; use external database for large-scale deployments; consider face clustering
|
||||
|
||||
**Image Resolution:**
|
||||
- Current capacity: Default camera resolution (medium preset)
|
||||
- Limit: Higher resolution = slower detection, more memory usage
|
||||
- Scaling path: Allow configurable resolution; implement dynamic resolution adjustment based on device capability
|
||||
|
||||
**Concurrent Detection Requests:**
|
||||
- Current capacity: Single detection at a time (`_isDetecting` flag)
|
||||
- Limit: Multiple simultaneous requests queue up
|
||||
- Scaling path: Implement request queue with priority; batch processing; dedicated worker threads
|
||||
|
||||
## Dependencies at Risk
|
||||
|
||||
**ArcSoft Face SDK:**
|
||||
- Risk: Third-party proprietary SDK; licensing and availability concerns
|
||||
- Impact: Plugin entirely dependent on ArcSoft SDK availability and compatibility
|
||||
- Migration plan: Abstract SDK interface; allow alternative face recognition backends (e.g., ML Kit, OpenCV)
|
||||
|
||||
**camera package:**
|
||||
- Risk: External dependency in example app; version compatibility
|
||||
- Impact: Example app functionality dependent on camera plugin stability
|
||||
- Migration plan: Keep camera package updated; abstract camera interface for alternative implementations
|
||||
|
||||
**Flutter SDK Version:**
|
||||
- Risk: Requires SDK ^3.9.0 which is relatively new
|
||||
- Files: `pubspec.yaml` (line 7)
|
||||
- Impact: Older Flutter versions cannot use plugin
|
||||
- Migration plan: Consider lowering SDK constraint if backward compatibility needed
|
||||
|
||||
## Missing Critical Features
|
||||
|
||||
**Face Search (1:N Matching):**
|
||||
- Problem: `registerFaceFeature` and `registerFaceFeatureBatch` exist but no `searchFaceFeature` method to query registered faces
|
||||
- Files: `lib/arc.dart`, `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
|
||||
- Blocks: Cannot implement 1:N face recognition workflow (find matching face from database)
|
||||
|
||||
**Engine Uninit from Flutter:**
|
||||
- Problem: No Flutter API to release engine resources
|
||||
- Files: `lib/arc.dart` (no unInit method)
|
||||
- Blocks: Memory leak if engine not properly released; cannot reset engine state
|
||||
|
||||
**Error Code Documentation for Flutter:**
|
||||
- Problem: Error codes defined in Java but no Flutter-accessible documentation
|
||||
- Files: `android/src/main/java/com/xiarui/arc/FaceErrorCode.java` (comprehensive), `lib/arc.dart` (no error code constants)
|
||||
- Blocks: Flutter developers cannot interpret error codes without referencing native code
|
||||
|
||||
**IR Liveness Detection:**
|
||||
- Problem: Only RGB liveness detection implemented; IR liveness not exposed
|
||||
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (only ASF_LIVENESS mask)
|
||||
- Blocks: Cannot use more secure IR-based liveness detection if hardware available
|
||||
|
||||
## Test Coverage Gaps
|
||||
|
||||
**Native Unit Tests:**
|
||||
- What's not tested: All methods except `getPlatformVersion` in `ArcPluginTest.java`
|
||||
- Files: `android/src/test/java/com/xiarui/arc/ArcPluginTest.java` (only 1 test method)
|
||||
- Risk: Native method handling bugs undetected
|
||||
- Priority: High
|
||||
|
||||
**Flutter Integration Tests:**
|
||||
- What's not tested: Actual face detection with real image data; end-to-end workflows
|
||||
- Files: No integration test files in `test/` directory
|
||||
- Risk: Real-world usage patterns not validated
|
||||
- Priority: High
|
||||
|
||||
**Method Channel Tests:**
|
||||
- What's not tested: All methods except `getPlatformVersion` in method channel test
|
||||
- Files: `test/arc_method_channel_test.dart` (lines 24-26)
|
||||
- Risk: Method channel communication bugs for complex methods undetected
|
||||
- Priority: Medium
|
||||
|
||||
**Edge Case Tests:**
|
||||
- What's not tested: Invalid parameters, null data, oversized images, engine not initialized
|
||||
- Files: All test files lack edge case coverage
|
||||
- Risk: Runtime crashes on invalid inputs
|
||||
- Priority: Medium
|
||||
|
||||
**Mock Implementation Completeness:**
|
||||
- What's not tested: `registerFaceFeature`, `registerFaceFeatureBatch` methods in mock
|
||||
- Files: `test/arc_test.dart` (`MockArcPlatform` missing methods)
|
||||
- Risk: Tests fail or skip when new methods are used
|
||||
- Priority: Low
|
||||
|
||||
---
|
||||
|
||||
*Concerns audit: 2026-03-30*
|
||||
239
.planning/codebase/CONVENTIONS.md
Normal file
239
.planning/codebase/CONVENTIONS.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-03-30
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
**Files:**
|
||||
- Dart: lowercase with underscores (`arc.dart`, `arc_platform_interface.dart`, `arc_method_channel.dart`)
|
||||
- Java: PascalCase matching class name (`ArcPlugin.java`, `FaceEngineManager.java`)
|
||||
|
||||
**Classes:**
|
||||
- PascalCase: `Arc`, `ArcPlatform`, `MethodChannelArc`, `FaceEngineManager`, `FaceErrorCode`
|
||||
|
||||
**Methods/Functions:**
|
||||
- camelCase: `getPlatformVersion`, `activeOnline`, `detectFaces`, `extractFaceFeature`
|
||||
|
||||
**Variables:**
|
||||
- Private Dart members: underscore prefix (`_token`, `_instance`, `_isDetecting`)
|
||||
- Java: `private` keyword with camelCase: `faceEngine`, `isInitialized`, `lastRgbLiveness`
|
||||
|
||||
**Constants:**
|
||||
- Dart: `static final` with underscore for private: `static final Object _token = Object();`
|
||||
- Java enum: uppercase with underscores: `MOK`, `MERR_UNKNOWN`, `ENGINE_NOT_INITIALIZED`
|
||||
|
||||
## Code Style
|
||||
|
||||
**Formatting:**
|
||||
- Dart: `flutter_lints ^5.0.0` via `analysis_options.yaml`
|
||||
- Config: `include: package:flutter_lints/flutter.yaml`
|
||||
- Java: Standard Android/Java conventions
|
||||
|
||||
**Linting:**
|
||||
- Dart: flutter_lints package rules
|
||||
- Java: No explicit linting config detected
|
||||
|
||||
## Import Organization
|
||||
|
||||
**Dart Order:**
|
||||
1. SDK imports (`dart:typed_data`)
|
||||
2. Package imports (`package:plugin_platform_interface/plugin_platform_interface.dart`)
|
||||
3. Relative imports (`arc_method_channel.dart`)
|
||||
|
||||
**Example:**
|
||||
```dart
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'arc_method_channel.dart';
|
||||
```
|
||||
|
||||
**Java Order:**
|
||||
1. Package declaration
|
||||
2. Android SDK imports
|
||||
3. Third-party imports
|
||||
4. JUnit/Mockito imports (in tests)
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Dart Patterns:**
|
||||
- Platform interface throws `UnimplementedError` for abstract methods:
|
||||
```dart
|
||||
Future<String?> getPlatformVersion() {
|
||||
throw UnimplementedError('platformVersion() has not been implemented.');
|
||||
}
|
||||
```
|
||||
- Method channel calls wrapped in try-catch with `PlatformException`
|
||||
- Return `Map<String, dynamic>?` with `success`, `errorCode`, `message` keys
|
||||
|
||||
**Java Patterns:**
|
||||
- Parameter validation before processing
|
||||
- Error codes via `FaceErrorCode` enum
|
||||
- Result Map with consistent keys: `success`, `errorCode`, `message`
|
||||
- Logging with `android.util.Log.e()` for errors
|
||||
|
||||
**Return Format:**
|
||||
All API methods return consistent Map structure:
|
||||
```dart
|
||||
{
|
||||
'success': bool, // Operation success status
|
||||
'errorCode': int, // Error code (0 = success)
|
||||
'message': String, // Error/success message
|
||||
// Additional data keys based on method
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
**Dart Comments:**
|
||||
- Triple-slash `///` for method documentation
|
||||
- Chinese descriptions required
|
||||
- Parameter documentation: `[paramName] description`
|
||||
- Return documentation after parameters
|
||||
|
||||
**Example:**
|
||||
```dart
|
||||
/// 激活 SDK(在线激活)
|
||||
/// [appId] 应用 ID(从虹软控制台获取)
|
||||
/// [sdkKey] SDK 密钥(从虹软控制台获取)
|
||||
/// [activeKey] 激活密钥
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
Future<Map<String, dynamic>?> activeOnline({
|
||||
required String appId,
|
||||
required String sdkKey,
|
||||
required String activeKey,
|
||||
})
|
||||
```
|
||||
|
||||
**Java Comments:**
|
||||
- Javadoc `/** */` format
|
||||
- `@param` and `@return` tags
|
||||
- Chinese descriptions
|
||||
|
||||
**Example:**
|
||||
```java
|
||||
/**
|
||||
* 激活 SDK(在线激活方式)
|
||||
* @param context Android 上下文
|
||||
* @param appId 应用 ID(从虹软控制台获取)
|
||||
* @param sdkKey SDK 密钥(从虹软控制台获取)
|
||||
* @param activeKey 激活密钥
|
||||
* @return 错误码(0 表示成功)
|
||||
*/
|
||||
public int activeOnline(Context context, String appId, String sdkKey, String activeKey)
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
**Dart:**
|
||||
- `debugPrint()` for development logging
|
||||
- Located in: `example/lib/main.dart`
|
||||
|
||||
**Java:**
|
||||
- `android.util.Log.i()` for info
|
||||
- `android.util.Log.e()` for errors
|
||||
- `android.util.Log.w()` for warnings
|
||||
- Tag: `"FaceEngineManager"`
|
||||
|
||||
**Example:**
|
||||
```java
|
||||
android.util.Log.i("FaceEngineManager", "引擎初始化成功!");
|
||||
android.util.Log.e("FaceEngineManager", "引擎初始化失败,错误码: " + result);
|
||||
```
|
||||
|
||||
## Function Design
|
||||
|
||||
**Parameters:**
|
||||
- Dart: Named parameters with `required` keyword
|
||||
- Optional parameters have default values
|
||||
|
||||
**Example:**
|
||||
```dart
|
||||
Future<Map<String, dynamic>?> detectFaces({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
int format = 2050, // Optional with default
|
||||
})
|
||||
```
|
||||
|
||||
**Return Values:**
|
||||
- All async methods return `Future<Map<String, dynamic>?>`
|
||||
- Null-safe with nullable type annotation `?`
|
||||
|
||||
## Module Design
|
||||
|
||||
**Platform Interface Pattern:**
|
||||
- Abstract class extends `PlatformInterface`
|
||||
- Static instance getter/setter
|
||||
- Token verification for platform registration
|
||||
|
||||
**Example Structure:**
|
||||
```dart
|
||||
abstract class ArcPlatform extends PlatformInterface {
|
||||
ArcPlatform() : super(token: _token);
|
||||
static final Object _token = Object();
|
||||
static ArcPlatform _instance = MethodChannelArc();
|
||||
static ArcPlatform get instance => _instance;
|
||||
static set instance(ArcPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Method Channel Implementation:**
|
||||
- Class extends platform interface
|
||||
- `@visibleForTesting` annotation for test exposure
|
||||
- Method channel with constant name: `'arc'`
|
||||
|
||||
**Singleton Pattern (Java):**
|
||||
```java
|
||||
public class FaceEngineManager {
|
||||
private static FaceEngineManager instance;
|
||||
private FaceEngineManager() {}
|
||||
public static synchronized FaceEngineManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new FaceEngineManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
**Flutter Widget State:**
|
||||
- Private state variables with underscore prefix
|
||||
- `StatefulWidget` with corresponding `State` class
|
||||
- `mounted` check before `setState`
|
||||
|
||||
**Example:**
|
||||
```dart
|
||||
class _HomePageState extends State<HomePage> {
|
||||
String _platformVersion = 'Unknown';
|
||||
bool _isActivated = false;
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_platformVersion = platformVersion;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Key Files
|
||||
|
||||
**Core Dart Files:**
|
||||
- `lib/arc.dart` - Main API class
|
||||
- `lib/arc_platform_interface.dart` - Abstract platform interface
|
||||
- `lib/arc_method_channel.dart` - Method channel implementation
|
||||
|
||||
**Core Java Files:**
|
||||
- `android/src/main/java/com/xiarui/arc/ArcPlugin.java` - Flutter plugin entry
|
||||
- `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` - Engine singleton
|
||||
- `android/src/main/java/com/xiarui/arc/FaceErrorCode.java` - Error code enum
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-03-30*
|
||||
158
.planning/codebase/INTEGRATIONS.md
Normal file
158
.planning/codebase/INTEGRATIONS.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# External Integrations
|
||||
|
||||
**Analysis Date:** 2026-03-30
|
||||
|
||||
## APIs & External Services
|
||||
|
||||
**人脸识别 SDK:**
|
||||
- 虹软 (ArcSoft) 人脸识别 SDK - 离线人脸检测、特征提取、活体检测
|
||||
- SDK 库: `arcsoft_face.jar`, `libarcsoft_face.so`
|
||||
- 版本: 基于 2024年1月版本
|
||||
- Auth: 需要 AppId、SdkKey、ActiveKey(从虹软开放平台获取)
|
||||
- 激活方式: 在线激活(`FaceEngine.activeOnline()`)
|
||||
- 官方文档: https://ai.arcsoft.com.cn
|
||||
|
||||
**图像处理工具:**
|
||||
- 虹软图像处理工具库 - 图像格式转换和处理
|
||||
- SDK 库: `arcsoft_image_util.jar`, `libarcsoft_image_util.so`
|
||||
|
||||
## Data Storage
|
||||
|
||||
**Databases:**
|
||||
- 无数据库集成 - 人脸特征数据存储在内存中(人脸库)
|
||||
|
||||
**Face Feature Database (In-Memory):**
|
||||
- 人脸库存储: 通过 `FaceEngine.registerFaceFeature()` 注册到 SDK 内部内存
|
||||
- 数据结构: `FaceFeatureInfo(searchId, featureData, faceTag)`
|
||||
- 搜索能力: 1:N 人脸搜索(SDK 内置)
|
||||
- 注意: 人脸库数据在引擎释放后丢失,不持久化
|
||||
|
||||
**File Storage:**
|
||||
- 虹软 SDK 激活文件存储于设备本地(SDK 自动管理)
|
||||
- 位置: Android 设备内部存储(SDK 控制)
|
||||
|
||||
**Caching:**
|
||||
- None - 无缓存层
|
||||
|
||||
## Authentication & Identity
|
||||
|
||||
**Auth Provider:**
|
||||
- 虹软 SDK 激活机制 - 设备绑定激活
|
||||
- Implementation: 在线激活验证 AppId + SdkKey + ActiveKey
|
||||
- 激活文件: 设备指纹绑定,单设备单激活
|
||||
- 错误处理: `FaceErrorCode` 枚举定义完整错误码映射
|
||||
|
||||
**激活状态检查:**
|
||||
```dart
|
||||
// 激活成功条件: errorCode == 0 或 errorCode == 90114 (已激活)
|
||||
result['success'] = errorCode == 0 || errorCode == 90114;
|
||||
```
|
||||
|
||||
**激活流程:**
|
||||
1. 调用 `Arc.activeOnline(appId, sdkKey, activeKey)`
|
||||
2. Flutter MethodChannel 传递参数到 Android
|
||||
3. Android 调用 `FaceEngine.activeOnline(context, activeKey, appId, sdkKey)`
|
||||
4. SDK 与虹软服务器验证并生成激活文件
|
||||
5. 返回激活结果 Map
|
||||
|
||||
## Monitoring & Observability
|
||||
|
||||
**Error Tracking:**
|
||||
- 错误码枚举系统 - `FaceErrorCode.java`(完整错误码映射)
|
||||
- 分类: 通用错误、SDK基础错误、人脸识别错误、激活错误、网络错误、离线授权错误
|
||||
|
||||
**Logs:**
|
||||
- Android Logcat 输出(`FaceEngineManager.java` 使用 `android.util.Log`)
|
||||
- Flutter debugPrint 输出(示例应用)
|
||||
- 日志级别: Info, Error
|
||||
|
||||
**关键日志点:**
|
||||
- SDK 初始化结果: `FaceEngineManager.init()`
|
||||
- 人脸检测结果: `FaceEngineManager.detectFaces()`
|
||||
- RGB 活体检测结果: `FaceEngineManager.getLiveness()`
|
||||
- 特征提取结果: `FaceEngineManager.extractFaceFeature()`
|
||||
- 人脸比对结果: `FaceEngineManager.compareFaceFeature()`
|
||||
|
||||
## CI/CD & Deployment
|
||||
|
||||
**Hosting:**
|
||||
- 本地 Flutter 插件包 - 不发布到 pub.dev(私有包)
|
||||
- `publish_to: 'none'`(示例应用)
|
||||
|
||||
**CI Pipeline:**
|
||||
- None - 无 CI 配置文件
|
||||
|
||||
**Build Commands:**
|
||||
```bash
|
||||
flutter pub get # 获取依赖
|
||||
flutter test # 运行 Flutter 单元测试
|
||||
flutter build apk # 构建 Android APK
|
||||
cd android && ./gradlew test # 运行 Android 单元测试
|
||||
```
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
**Required env vars (SDK 激活):**
|
||||
- AppId: 虹软开放平台申请的应用 ID
|
||||
- SdkKey: 虹软开放平台申请的 SDK 密钥
|
||||
- ActiveKey: 激活密钥(可选,部分场景需要)
|
||||
|
||||
**示例应用配置(硬编码):**
|
||||
```dart
|
||||
// example/lib/main.dart
|
||||
final String _appId = '4nPFuS2TYAQh9werHL2qbKjtTH9nnoixk7G6yqSUyjVH';
|
||||
final String _sdkKey = 'aWMTT3coxNQFETg9f3BGHCiBznAApXmcHFF3J5yQbsZ';
|
||||
final String _activeKey = 'NEAT7AU4KLCEK682';
|
||||
```
|
||||
|
||||
**Secrets location:**
|
||||
- 当前: 硬编码在示例应用(不推荐生产使用)
|
||||
- 建议: 通过环境变量或安全存储传递
|
||||
|
||||
## Webhooks & Callbacks
|
||||
|
||||
**Incoming:**
|
||||
- None - 无外部 webhook
|
||||
|
||||
**Outgoing:**
|
||||
- None - 无外部 API 调用(SDK 激活除外)
|
||||
|
||||
## Flutter-Native Communication
|
||||
|
||||
**Method Channel:**
|
||||
- Channel 名称: `'arc'`
|
||||
- 定义文件: `lib/arc_method_channel.dart`
|
||||
- 实现: `MethodChannelArc` 类
|
||||
|
||||
**支持的方法:**
|
||||
| 方法名 | 功能 | 参数 |
|
||||
|--------|------|------|
|
||||
| `getPlatformVersion` | 获取平台版本 | 无 |
|
||||
| `activeOnline` | SDK 在线激活 | appId, sdkKey, activeKey |
|
||||
| `init` | 初始化人脸引擎 | detectMode, orient, maxFaceNum, combinedMask |
|
||||
| `detectFaces` | 人脸检测+RGB活体 | data, width, height, format |
|
||||
| `extractFaceFeature` | 特征提取 | data, width, height, rect, faceId, extractType, mask |
|
||||
| `compareFaceFeature` | 特征比对 | featureData1, featureData2, compareModel |
|
||||
| `registerFaceFeature` | 单张人脸注册 | searchId, featureData, faceTag |
|
||||
| `registerFaceFeatureBatch` | 批量人脸注册 | faceList |
|
||||
|
||||
**数据格式:**
|
||||
- 图像数据: NV21 格式(`Uint8List`)
|
||||
- 特征数据: SDK 定义长度(`Uint8List`)
|
||||
- 返回值: `Map<String, dynamic>` 格式统一
|
||||
|
||||
## Image Format Requirements
|
||||
|
||||
**NV21 格式约束:**
|
||||
- 图像宽度必须是 4 的倍数
|
||||
- 图像高度必须是 2 的倍数(NV21 格式)
|
||||
- 格式码: 2050 (`FaceEngine.CP_PAF_NV21`)
|
||||
|
||||
**Camera Integration (Example):**
|
||||
- 使用 `camera` 插件获取摄像头图像流
|
||||
- `ImageFormatGroup.nv21` 设置图像格式
|
||||
- `CameraPreviewScreen` 处理实时图像流检测
|
||||
|
||||
---
|
||||
|
||||
*Integration audit: 2026-03-30*
|
||||
109
.planning/codebase/STACK.md
Normal file
109
.planning/codebase/STACK.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Technology Stack
|
||||
|
||||
**Analysis Date:** 2026-03-30
|
||||
|
||||
## Languages
|
||||
|
||||
**Primary:**
|
||||
- Dart 3.9.0+ - Flutter 插件层代码(`lib/*.dart`)
|
||||
- Java 11 - Android 原生实现(`android/src/main/java/**/*.java`)
|
||||
|
||||
**Secondary:**
|
||||
- Kotlin - 未使用(纯 Java 实现)
|
||||
- C/C++ (Native) - 虹软 SDK native 库(`libarcsoft_face.so`, `libarcsoft_face_engine.so`, `libarcsoft_image_util.so`)
|
||||
|
||||
## Runtime
|
||||
|
||||
**Environment:**
|
||||
- Flutter SDK 3.18.0+ (stable channel)
|
||||
- Dart SDK 3.9.0+ (<4.0.0)
|
||||
- Android SDK compileSdk 36, minSdk 24
|
||||
|
||||
**Package Manager:**
|
||||
- pub (Flutter/Dart 包管理器)
|
||||
- Gradle 8.9.1 (Android 构建)
|
||||
- Lockfile: `pubspec.lock` (present), `pubspec.yaml` (present)
|
||||
|
||||
## Frameworks
|
||||
|
||||
**Core:**
|
||||
- Flutter SDK - 跨平台移动应用框架
|
||||
- Flutter Plugin Architecture - 平台通道通信机制
|
||||
|
||||
**Testing:**
|
||||
- flutter_test - Flutter 单元测试框架
|
||||
- flutter_lints 5.0.0 - Dart/Flutter 代码规范检查
|
||||
- JUnit 4.13.2 - Java 单元测试
|
||||
- Mockito 5.0.0 - Java mock 测试框架
|
||||
|
||||
**Build/Dev:**
|
||||
- Gradle 8.9.1 - Android 构建系统
|
||||
- Android Gradle Plugin 8.9.1 - Android 构建插件
|
||||
- Flutter CLI - Flutter 命令行工具
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
**Critical:**
|
||||
- `plugin_platform_interface` 2.0.2 - Flutter 插件平台接口抽象层
|
||||
- `arcsoft_face.jar` - 虹软人脸识别核心 SDK
|
||||
- `arcsoft_image_util.jar` - 虹软图像处理工具库
|
||||
- `libarcsoft_face.so` (~87MB) - 虹软人脸识别 native 库(arm64-v8a)
|
||||
- `libarcsoft_face_engine.so` (~1.3MB) - 虹软人脸引擎 native 库
|
||||
- `libarcsoft_image_util.so` (~80KB) - 虹软图像处理 native 库
|
||||
|
||||
**Infrastructure (Example App):**
|
||||
- `camera` 0.11.1 - 摄像头访问插件(用于实时人脸检测)
|
||||
- `cupertino_icons` 1.0.8 - iOS 风格图标
|
||||
|
||||
## Configuration
|
||||
|
||||
**Environment:**
|
||||
- Dart SDK 版本约束: `^3.9.0`(`pubspec.yaml`)
|
||||
- Flutter SDK 版本约束: `>=3.3.0`(`pubspec.yaml`)
|
||||
- Java 版本: 11(`android/build.gradle`)
|
||||
- Pub 仓库源: `https://pub.flutter-io.cn`(中国镜像)
|
||||
|
||||
**Build:**
|
||||
- `pubspec.yaml` - Dart/Flutter 包配置
|
||||
- `pubspec.lock` - 依赖锁定文件
|
||||
- `analysis_options.yaml` - Dart 代码分析配置(继承 flutter_lints)
|
||||
- `android/build.gradle` - Android 构建配置
|
||||
- `android/settings.gradle` - Android 项目设置
|
||||
|
||||
## Platform Requirements
|
||||
|
||||
**Development:**
|
||||
- Flutter SDK 3.18.0+ 安装
|
||||
- Dart SDK 3.9.0+ 安装
|
||||
- Android Studio / Android SDK(compileSdk 36)
|
||||
- JDK 11
|
||||
|
||||
**Production:**
|
||||
- Android 设备(API 24+,即 Android 7.0+)
|
||||
- ARM64-v8a 或 armeabi-v7a 架构支持
|
||||
- 摄像头权限(用于人脸检测)
|
||||
- 虹软 SDK 有效激活(AppId、SdkKey、ActiveKey)
|
||||
|
||||
## Architecture Support
|
||||
|
||||
**Native Libraries:**
|
||||
- `arm64-v8a` - 64位 ARM 设备(主流现代设备)
|
||||
- `armeabi-v7a` - 32位 ARM 设备(兼容旧设备)
|
||||
|
||||
**SDK 功能掩码常量:**
|
||||
```java
|
||||
ASF_FACE_DETECT = 0x00000001 // 人脸检测
|
||||
ASF_FACE_RECOGNITION = 0x00000004 // 人脸特征
|
||||
ASF_AGE = 0x00000008 // 年龄检测
|
||||
ASF_GENDER = 0x00000010 // 性别检测
|
||||
ASF_LIVENESS = 0x00000080 // RGB 活体检测
|
||||
ASF_IMAGEQUALITY = 0x00000200 // 图像质量检测
|
||||
ASF_MASK_DETECT = 0x00001000 // 口罩检测
|
||||
ASF_UPDATE_FACEDATA = 0x00002000 // 人脸信息更新
|
||||
```
|
||||
|
||||
**默认功能掩码:** 0x85 = 人脸检测 + 人脸特征 + RGB活体检测
|
||||
|
||||
---
|
||||
|
||||
*Stack analysis: 2026-03-30*
|
||||
175
.planning/codebase/STRUCTURE.md
Normal file
175
.planning/codebase/STRUCTURE.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Codebase Structure
|
||||
|
||||
**Analysis Date:** 2026-03-30
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
D:\code\new_git_code\arc/
|
||||
├── lib/ # Flutter Dart source code
|
||||
├── android/ # Android native implementation
|
||||
├── test/ # Unit tests for Dart code
|
||||
├── example/ # Example Flutter application
|
||||
├── docs/ # Documentation files
|
||||
├── .dart_tool/ # Dart tool cache (generated)
|
||||
├── .planning/ # Planning documents (generated)
|
||||
└── pubspec.yaml # Flutter package manifest
|
||||
```
|
||||
|
||||
## Directory Purposes
|
||||
|
||||
**lib:**
|
||||
- Purpose: Flutter plugin Dart source code
|
||||
- Contains: Public API, platform interface, method channel implementation
|
||||
- Key files: `arc.dart`, `arc_platform_interface.dart`, `arc_method_channel.dart`
|
||||
|
||||
**android:**
|
||||
- Purpose: Android native implementation for ArcSoft SDK integration
|
||||
- Contains: Java source, ArcSoft SDK libraries, Gradle build config
|
||||
- Key files: `src/main/java/com/xiarui/arc/ArcPlugin.java`, `libs/arcsoft_face.jar`
|
||||
|
||||
**test:**
|
||||
- Purpose: Unit tests for Dart plugin code
|
||||
- Contains: Mock platform implementations, method channel tests
|
||||
- Key files: `arc_test.dart`, `arc_method_channel_test.dart`
|
||||
|
||||
**example:**
|
||||
- Purpose: Demonstrates plugin usage with camera-based face detection
|
||||
- Contains: Complete Flutter app with camera preview and detection UI
|
||||
- Key files: `lib/main.dart`, `integration_test/plugin_integration_test.dart`
|
||||
|
||||
**docs:**
|
||||
- Purpose: External SDK documentation
|
||||
- Contains: ArcSoft face recognition API documentation
|
||||
- Key files: `虹软人脸识别接口文档.md`
|
||||
|
||||
**android/libs:**
|
||||
- Purpose: ArcSoft native SDK JAR files
|
||||
- Contains: `arcsoft_face.jar` (face recognition), `arcsoft_image_util.jar` (image utilities)
|
||||
- Generated: No (third-party SDK)
|
||||
- Committed: Yes
|
||||
|
||||
**android/src/main/jniLibs:**
|
||||
- Purpose: Native library files for different CPU architectures
|
||||
- Contains: `.so` files for arm64-v8a, armeabi-v7a
|
||||
- Generated: No (from ArcSoft SDK)
|
||||
- Committed: Yes
|
||||
|
||||
## Key File Locations
|
||||
|
||||
**Entry Points:**
|
||||
- `lib/arc.dart`: Public API entry point for plugin consumers
|
||||
- `android/src/main/java/com/xiarui/arc/ArcPlugin.java`: Android plugin registration and method handling
|
||||
- `example/lib/main.dart`: Example app entry point
|
||||
|
||||
**Configuration:**
|
||||
- `pubspec.yaml`: Flutter package configuration, SDK version constraints
|
||||
- `android/build.gradle`: Android library build configuration, SDK dependencies
|
||||
- `android/src/main/AndroidManifest.xml`: Android permissions (CAMERA, INTERNET, STORAGE)
|
||||
|
||||
**Core Logic:**
|
||||
- `lib/arc_platform_interface.dart`: Platform interface abstract class
|
||||
- `lib/arc_method_channel.dart`: Method channel implementation
|
||||
- `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`: Singleton managing FaceEngine lifecycle
|
||||
|
||||
**Data Models:**
|
||||
- `android/src/main/java/com/xiarui/arc/FaceInfo.java`: Face detection result model
|
||||
- `android/src/main/java/com/xiarui/arc/FaceErrorCode.java`: Error code enumeration (586 lines)
|
||||
|
||||
**Testing:**
|
||||
- `test/arc_test.dart`: Mock platform tests
|
||||
- `test/arc_method_channel_test.dart`: Method channel unit tests
|
||||
- `android/src/test/java/com/xiarui/arc/ArcPluginTest.java`: Android unit tests
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
**Files:**
|
||||
- Dart: lowercase with underscores, e.g., `arc_platform_interface.dart`
|
||||
- Java: PascalCase for classes, e.g., `ArcPlugin.java`, `FaceEngineManager.java`
|
||||
|
||||
**Classes:**
|
||||
- Dart: PascalCase, e.g., `Arc`, `ArcPlatform`, `MethodChannelArc`
|
||||
- Java: PascalCase, e.g., `FaceEngineManager`, `FaceErrorCode`
|
||||
|
||||
**Methods:**
|
||||
- Dart: camelCase with descriptive names, e.g., `activeOnline`, `detectFaces`, `extractFaceFeature`
|
||||
- Java: camelCase with handler prefix for dispatch, e.g., `handleActiveOnline`, `handleDetectFaces`
|
||||
|
||||
**Variables:**
|
||||
- Dart: camelCase for local/instance variables
|
||||
- Java: camelCase, enum constants use UPPER_SNAKE_CASE, e.g., `MERR_ASF_ACTIVATION_FAIL`
|
||||
|
||||
## Where to Add New Code
|
||||
|
||||
**New Feature (Dart API):**
|
||||
- Primary code: `lib/arc.dart` - add public method
|
||||
- Interface: `lib/arc_platform_interface.dart` - add abstract method
|
||||
- Implementation: `lib/arc_method_channel.dart` - add method channel invocation
|
||||
|
||||
**New Feature (Android Native):**
|
||||
- Handler: `android/src/main/java/com/xiarui/arc/ArcPlugin.java` - add case in `onMethodCall()` switch
|
||||
- Logic: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` - add SDK operation method
|
||||
|
||||
**New Platform Support (iOS/Web):**
|
||||
- Create platform-specific implementation in appropriate directory
|
||||
- Register in `pubspec.yaml` under `plugin.platforms`
|
||||
|
||||
**New Data Model:**
|
||||
- Location: `android/src/main/java/com/xiarui/arc/` alongside existing models
|
||||
- Pattern: JavaBean style with getters/setters and `toMap()` method
|
||||
|
||||
**Tests:**
|
||||
- Dart tests: `test/` directory, named `*_test.dart`
|
||||
- Android tests: `android/src/test/java/com/xiarui/arc/` directory
|
||||
|
||||
**Utilities:**
|
||||
- Shared helpers: `android/src/main/java/com/xiarui/arc/` as utility classes
|
||||
|
||||
## Special Directories
|
||||
|
||||
**android/libs:**
|
||||
- Purpose: ArcSoft SDK JAR files (third-party)
|
||||
- Generated: No
|
||||
- Committed: Yes (required for build)
|
||||
- Note: Contains `arcsoft_face.jar`, `arcsoft_image_util.jar`
|
||||
|
||||
**android/src/main/jniLibs:**
|
||||
- Purpose: Native `.so` libraries for CPU architectures
|
||||
- Generated: No (from ArcSoft SDK distribution)
|
||||
- Committed: Yes
|
||||
- Architectures: arm64-v8a, armeabi-v7a
|
||||
|
||||
**example/build:**
|
||||
- Purpose: Flutter/Gradle build artifacts
|
||||
- Generated: Yes
|
||||
- Committed: No (should be in .gitignore)
|
||||
|
||||
**.dart_tool:**
|
||||
- Purpose: Dart package config and tool cache
|
||||
- Generated: Yes
|
||||
- Committed: No
|
||||
|
||||
**.planning:**
|
||||
- Purpose: GSD planning documents
|
||||
- Generated: Yes (by GSD tools)
|
||||
- Committed: Yes (for team visibility)
|
||||
|
||||
## Package Structure
|
||||
|
||||
**Flutter Package:**
|
||||
- Package name: `arc`
|
||||
- Version: 0.0.1
|
||||
- SDK constraint: `^3.9.0`
|
||||
- Flutter constraint: `>=3.3.0`
|
||||
- Platforms: Android only (current)
|
||||
|
||||
**Android Package:**
|
||||
- Package name: `com.xiarui.arc`
|
||||
- Plugin class: `ArcPlugin`
|
||||
- Min SDK: 24 (Android 7.0)
|
||||
- Compile SDK: 36
|
||||
- Java version: 11
|
||||
|
||||
---
|
||||
|
||||
*Structure analysis: 2026-03-30*
|
||||
284
.planning/codebase/TESTING.md
Normal file
284
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-03-30
|
||||
|
||||
## Test Framework
|
||||
|
||||
**Runner:**
|
||||
- Flutter Test Framework (flutter_test SDK)
|
||||
- Dart SDK: ^3.9.0
|
||||
- Config: `analysis_options.yaml` (linting only, no test config file)
|
||||
|
||||
**Assertion Library:**
|
||||
- flutter_test package: `expect()`, `findsOneWidget`, `isInstanceOf<T>()`
|
||||
|
||||
**Run Commands:**
|
||||
```bash
|
||||
flutter test # Run all tests
|
||||
flutter test test/arc_test.dart # Run specific test file
|
||||
flutter test --coverage # Run with coverage
|
||||
```
|
||||
|
||||
## Test File Organization
|
||||
|
||||
**Location:**
|
||||
- Plugin tests: `test/` directory (co-located with lib)
|
||||
- Example tests: `example/test/` directory
|
||||
- Java tests: `android/src/test/java/`
|
||||
|
||||
**Naming:**
|
||||
- Dart: `*_test.dart` pattern (`arc_test.dart`, `arc_method_channel_test.dart`, `widget_test.dart`)
|
||||
- Java: `*Test.java` pattern (`ArcPluginTest.java`)
|
||||
|
||||
**Structure:**
|
||||
```
|
||||
arc/
|
||||
├── test/
|
||||
│ ├── arc_test.dart # Platform interface tests
|
||||
│ └── arc_method_channel_test.dart # Method channel tests
|
||||
├── example/
|
||||
│ └── test/
|
||||
│ └── widget_test.dart # Widget tests
|
||||
└── android/
|
||||
└── src/
|
||||
└── test/
|
||||
└── java/
|
||||
└── com/xiarui/arc/
|
||||
└── ArcPluginTest.java
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
**Dart Unit Tests:**
|
||||
```dart
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final ArcPlatform initialPlatform = ArcPlatform.instance;
|
||||
|
||||
test('$MethodChannelArc is the default instance', () {
|
||||
expect(initialPlatform, isInstanceOf<MethodChannelArc>());
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
Arc arcPlugin = Arc();
|
||||
MockArcPlatform fakePlatform = MockArcPlatform();
|
||||
ArcPlatform.instance = fakePlatform;
|
||||
|
||||
expect(await arcPlugin.getPlatformVersion(), '42');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Java Unit Tests:**
|
||||
```java
|
||||
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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Mocking
|
||||
|
||||
**Dart Mocking:**
|
||||
- `MockPlatformInterfaceMixin` from plugin_platform_interface
|
||||
- Custom mock class implementing platform interface
|
||||
|
||||
**Example:**
|
||||
```dart
|
||||
class MockArcPlatform
|
||||
with MockPlatformInterfaceMixin
|
||||
implements ArcPlatform {
|
||||
|
||||
@override
|
||||
Future<String?> getPlatformVersion() => Future.value('42');
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> activeOnline({
|
||||
required String appId,
|
||||
required String sdkKey,
|
||||
required String activeKey,
|
||||
}) => Future.value({'success': true, 'errorCode': 0, 'message': 'success'});
|
||||
}
|
||||
```
|
||||
|
||||
**Method Channel Mocking:**
|
||||
```dart
|
||||
setUp(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
channel,
|
||||
(MethodCall methodCall) async {
|
||||
return '42';
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
```
|
||||
|
||||
**Java Mocking:**
|
||||
- Mockito framework
|
||||
- `mock(Class.class)` for mock objects
|
||||
- `verify(mock).method(args)` for verification
|
||||
|
||||
**What to Mock:**
|
||||
- Platform interfaces for unit testing
|
||||
- Method channels for integration testing
|
||||
- Native engine calls
|
||||
|
||||
**What NOT to Mock:**
|
||||
- Data transformation logic (test directly)
|
||||
- Simple utility functions
|
||||
|
||||
## Fixtures and Factories
|
||||
|
||||
**Test Data:**
|
||||
- Mock return values defined inline
|
||||
- Simple string/int values: `'42'`, `0`, `true`
|
||||
|
||||
**Example Mock Data:**
|
||||
```dart
|
||||
@override
|
||||
Future<Map<String, dynamic>?> detectFaces({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
int format = 2050,
|
||||
}) => Future.value({
|
||||
'success': true,
|
||||
'errorCode': 0,
|
||||
'faceList': [],
|
||||
'rgbLiveness': 1,
|
||||
'isRgbAlive': true
|
||||
});
|
||||
```
|
||||
|
||||
**Location:**
|
||||
- Mock implementations in test files
|
||||
- No separate fixture files
|
||||
|
||||
## Coverage
|
||||
|
||||
**Requirements:** None enforced
|
||||
|
||||
**View Coverage:**
|
||||
```bash
|
||||
flutter test --coverage
|
||||
genhtml coverage/lcov.info -o coverage/html
|
||||
```
|
||||
|
||||
## Test Types
|
||||
|
||||
**Unit Tests:**
|
||||
- Platform interface instantiation
|
||||
- Method channel default instance verification
|
||||
- Mock platform behavior testing
|
||||
- File: `test/arc_test.dart`, `test/arc_method_channel_test.dart`
|
||||
|
||||
**Integration Tests:**
|
||||
- Method channel handler testing
|
||||
- Native plugin method call verification
|
||||
- File: `android/src/test/java/com/xiarui/arc/ArcPluginTest.java`
|
||||
|
||||
**Widget Tests:**
|
||||
- Basic widget rendering verification
|
||||
- File: `example/test/widget_test.dart`
|
||||
|
||||
**E2E Tests:** Not used
|
||||
|
||||
## Common Patterns
|
||||
|
||||
**Async Testing:**
|
||||
```dart
|
||||
test('getPlatformVersion', () async {
|
||||
expect(await arcPlugin.getPlatformVersion(), '42');
|
||||
});
|
||||
```
|
||||
|
||||
**Platform Instance Mocking:**
|
||||
```dart
|
||||
test('methodName', () async {
|
||||
Arc arcPlugin = Arc();
|
||||
MockArcPlatform fakePlatform = MockArcPlatform();
|
||||
ArcPlatform.instance = fakePlatform; // Override instance
|
||||
|
||||
expect(await arcPlugin.method(), expectedResult);
|
||||
});
|
||||
```
|
||||
|
||||
**Setup/Teardown:**
|
||||
```dart
|
||||
setUp(() {
|
||||
// Initialize test binding, set up mocks
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
// Clean up mocks
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
```
|
||||
|
||||
**Error Testing:**
|
||||
Current tests do not explicitly test error scenarios. Recommended pattern for future:
|
||||
```dart
|
||||
test('throws on invalid params', () async {
|
||||
expect(
|
||||
() => arcPlugin.detectFaces(data: Uint8List(0), width: 0, height: 0),
|
||||
throwsA(isA<PlatformException>()),
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
## Test Naming Convention
|
||||
|
||||
**Dart:**
|
||||
- Simple descriptive names: `'getPlatformVersion'`
|
||||
- Interpolated class names: `'$MethodChannelArc is the default instance'`
|
||||
|
||||
**Java:**
|
||||
- Pattern: `methodName_scenario_expectedResult`
|
||||
- Example: `onMethodCall_getPlatformVersion_returnsExpectedValue`
|
||||
|
||||
## Key Test Files
|
||||
|
||||
**Plugin Core Tests:**
|
||||
- `test/arc_test.dart` - Platform interface and Arc class tests
|
||||
- `test/arc_method_channel_test.dart` - Method channel implementation tests
|
||||
|
||||
**Example App Tests:**
|
||||
- `example/test/widget_test.dart` - Basic widget test (template, needs updating)
|
||||
|
||||
**Native Tests:**
|
||||
- `android/src/test/java/com/xiarui/arc/ArcPluginTest.java` - Android plugin tests
|
||||
|
||||
## Test Coverage Gaps
|
||||
|
||||
**Untested Methods:**
|
||||
- `activeOnline()` - No test coverage
|
||||
- `init()` - No test coverage
|
||||
- `detectFaces()` - No test coverage
|
||||
- `extractFaceFeature()` - No test coverage
|
||||
- `compareFaceFeature()` - No test coverage
|
||||
- `registerFaceFeature()` - No test coverage
|
||||
- `registerFaceFeatureBatch()` - No test coverage
|
||||
|
||||
**Untested Scenarios:**
|
||||
- Error handling (invalid parameters)
|
||||
- Edge cases (null data, empty arrays)
|
||||
- RGB liveness detection flow
|
||||
- Face feature extraction and comparison
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-03-30*
|
||||
Reference in New Issue
Block a user