1
This commit is contained in:
198
.planning/codebase/ARCHITECTURE.md
Normal file
198
.planning/codebase/ARCHITECTURE.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# Architecture
|
||||
|
||||
**Analysis Date:** 2026-04-16
|
||||
|
||||
## Pattern Overview
|
||||
|
||||
**Overall:** Flutter Platform Plugin (Federated Plugin Pattern)
|
||||
|
||||
This is an Android-only Flutter plugin that wraps the WCH CH34X UART library (`CH34XUARTDriver.jar`). It bridges Flutter/Dart code to native Android USB serial communication via `MethodChannel` and `EventChannel`.
|
||||
|
||||
**Key Characteristics:**
|
||||
- Static facade pattern for public API (`Ch34Manager`)
|
||||
- Platform interface pattern with token-based verification (`Ch34Platform`)
|
||||
- MethodChannel for command invocation, EventChannel for streaming callbacks
|
||||
- Single platform implementation (Android only, Kotlin/Java)
|
||||
- Singleton WCH UART manager on the native side
|
||||
|
||||
## Layers
|
||||
|
||||
### Dart Public API Layer
|
||||
- **Purpose:** Static facade providing the consumer-facing API
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart`
|
||||
- **Contains:** Static methods delegating to `Ch34Platform.instance`
|
||||
- **Depends on:** `Ch34Platform`
|
||||
- **Used by:** Flutter app consuming the plugin
|
||||
|
||||
### Dart Platform Interface Layer
|
||||
- **Purpose:** Abstract interface defining all plugin methods with token verification
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart`
|
||||
- **Contains:** `abstract class Ch34Platform extends PlatformInterface` with 33 method signatures
|
||||
- **Depends on:** `plugin_platform_interface` package, `ch34_types.dart`
|
||||
- **Used by:** `MethodChannelCh34` for concrete implementation
|
||||
|
||||
### Dart MethodChannel Implementation Layer
|
||||
- **Purpose:** Bridges abstract platform interface to native Android via Flutter's platform channels
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart`
|
||||
- **Contains:** `class MethodChannelCh34 extends Ch34Platform` with actual channel communication
|
||||
- **Channels:**
|
||||
- `MethodChannel('ch34')` - synchronous method calls
|
||||
- `EventChannel('ch34/data')` - serial data stream
|
||||
- `EventChannel('ch34/modem')` - modem status stream
|
||||
- `EventChannel('ch34/usb_state')` - USB hotplug events
|
||||
- **Depends on:** `dart:async`, `flutter/services.dart`
|
||||
- **Used by:** `Ch34Manager` (via `Ch34Platform.instance`)
|
||||
|
||||
### Dart Types Layer
|
||||
- **Purpose:** Shared data types and enums for the plugin
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\types\ch34_types.dart`
|
||||
- **Contains:**
|
||||
- Enums: `DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`
|
||||
- Data classes: `GpioStatus`, `SerialParameter`, `ModemStatus`, `UsbDeviceInfo`
|
||||
- Exception: `Ch34Exception` (defined in `ch34_method_channel.dart`)
|
||||
|
||||
### Android Native Layer
|
||||
- **Purpose:** Actual WCH UART library interaction, USB device management
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\`
|
||||
- **Contains:**
|
||||
- `Ch34Plugin.java` - Main plugin entry point, MethodCallHandler
|
||||
- `Ch34DataStreamHandler.java` - Data EventChannel stream handler with WCH callback bridging
|
||||
- `Ch34ModemStreamHandler.java` - Modem status EventChannel stream handler
|
||||
- `Ch34UsbStateStreamHandler.java` - USB hotplug EventChannel with BroadcastReceiver
|
||||
- `Ch34TypeConverter.java` - Type conversion between Dart and WCH types
|
||||
- **Depends on:** `CH34XUARTDriver.jar` (bundled in `android/libs/`)
|
||||
- **Used by:** Dart MethodChannel layer
|
||||
|
||||
### Backward Compatibility Layer
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\ch34_platform_interface.dart`, `D:\code\new_git_code\flutter\ch34\lib\ch34_method_channel.dart`
|
||||
- **Purpose:** Re-exports from `src/` for consumers using old import paths
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Device Discovery Flow
|
||||
1. Consumer calls `Ch34Manager.enumDevice()`
|
||||
2. Delegates to `Ch34Platform.instance.enumDevice()`
|
||||
3. `MethodChannelCh34.enumDevice()` invokes `methodChannel.invokeMethod('enumDevice')`
|
||||
4. `Ch34Plugin.enumDevice()` calls `WCHUARTManager.enumDevice()` on native side
|
||||
5. Native iterates devices, extracts VID/PID/serialCount/chipType
|
||||
6. Returns `List<Map>` through MethodChannel
|
||||
7. Dart side deserializes into `List<UsbDeviceInfo>`
|
||||
|
||||
### Data Read Flow (Callback Mode)
|
||||
1. Consumer calls `Ch34Manager.registerDataCallback(deviceName, serialNumber, onData)`
|
||||
2. `MethodChannelCh34` subscribes to `EventChannel('ch34/data')`
|
||||
3. Invokes `methodChannel.invokeMethod('registerDataCallback')`
|
||||
4. `Ch34Plugin` registers `Ch34DataStreamHandler.getWchCallback()` with `WCHUARTManager.registerDataCallback()`
|
||||
5. When data arrives, WCH triggers `IDataCallback.onData()`
|
||||
6. `Ch34DataStreamHandler` bridges data to EventChannel
|
||||
7. Dart receives via `StreamSubscription`, invokes user callback
|
||||
|
||||
### Data Write Flow
|
||||
1. Consumer calls `Ch34Manager.writeData(deviceName, serialNumber, data)`
|
||||
2. Delegates through platform interface to `MethodChannelCh34`
|
||||
3. `methodChannel.invokeMethod('writeData', {data: data, ...})`
|
||||
4. `Ch34Plugin.writeData()` calls `WCHUARTManager.writeData(device, serial, data, length, timeout)`
|
||||
5. Returns bytes written count
|
||||
|
||||
### USB Hotplug Event Flow
|
||||
1. `Ch34UsbStateStreamHandler` registers `BroadcastReceiver` for `ACTION_USB_DEVICE_ATTACHED/DETACHED`
|
||||
2. Android system sends broadcast on USB events
|
||||
3. BroadcastReceiver extracts `UsbDevice` and calls `notifyStateChanged()`
|
||||
4. EventChannel sends `{deviceName, connected}` to Dart
|
||||
5. `MethodChannelCh34` invokes `_usbStateCallback`
|
||||
|
||||
### Device Open Flow
|
||||
1. Consumer calls `Ch34Manager.openDevice(deviceName)`
|
||||
2. `Ch34Plugin.openDevice()` attempts `WCHUARTManager.openDevice(device)`
|
||||
3. If `NoPermissionException`, requests permission then retries after 2s delay
|
||||
4. On success, adds to `openedDevices` map and notifies `usbStateStreamHandler`
|
||||
|
||||
## State Management
|
||||
|
||||
**Native State:**
|
||||
- `WCHUARTManager` - Singleton instance, lazy-initialized in `ensureManagerInitialized()`
|
||||
- `openedDevices: Map<String, UsbDevice>` - tracks currently opened devices
|
||||
- Three `EventChannel.StreamHandler` instances - each manages its own `EventSink`
|
||||
|
||||
**Dart State:**
|
||||
- `Ch34Platform._instance` - Singleton platform instance
|
||||
- `MethodChannelCh34` maintains `_dataSubscription`, `_modemSubscription`, `_usbStateSubscription` - StreamSubscriptions for event channels
|
||||
- Callback references: `_dataCallback`, `_modemCallback`, `_usbStateCallback`
|
||||
|
||||
**State Cleanup:**
|
||||
- `Ch34Plugin.close()` calls `closeAllDevices()` to disconnect all and clear map
|
||||
- `Ch34UsbStateStreamHandler.onCancel()` unregisters BroadcastReceiver
|
||||
- `Ch34Manager.close()` triggers platform cleanup and cancels all subscriptions
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
### Platform Interface (`Ch34Platform`)
|
||||
- **Purpose:** Abstract contract that decouples Flutter code from native implementation
|
||||
- **Example:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart`
|
||||
- **Pattern:** Token-based platform interface verification from `plugin_platform_interface` package
|
||||
- **Pattern:**
|
||||
```dart
|
||||
static final Object _token = Object();
|
||||
static Ch34Platform get instance { ... }
|
||||
static void registerDefaultInstance(Ch34Platform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
```
|
||||
|
||||
### Manager Facade (`Ch34Manager`)
|
||||
- **Purpose:** Static facade simplifying API usage for consumers
|
||||
- **Example:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart`
|
||||
- **Pattern:** All static methods, all delegate to `Ch34Platform.instance`
|
||||
- **Pattern:**
|
||||
```dart
|
||||
static Future<List<UsbDeviceInfo>> enumDevice() {
|
||||
return Ch34Platform.instance.enumDevice();
|
||||
}
|
||||
```
|
||||
|
||||
### Type Converter (`Ch34TypeConverter`)
|
||||
- **Purpose:** Bridge between Dart enum indices and WCH native enum values
|
||||
- **Example:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34TypeConverter.java`
|
||||
- **Pattern:** Static utility class with conversion methods for GPIO direction/value, serial error types
|
||||
|
||||
## Entry Points
|
||||
|
||||
### Dart Library Entry
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\ch34.dart`
|
||||
- **Triggers:** Consumer imports `package:ch34/ch34.dart`
|
||||
- **Responsibilities:** Exports `Ch34Manager`, `Ch34Platform`, all types, and `Ch34Exception`
|
||||
|
||||
### Plugin Initialization (Android)
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java`
|
||||
- **Trigger:** Flutter engine attaches plugin via `onAttachedToEngine()`
|
||||
- **Responsibilities:** Creates MethodChannel, 3 EventChannels, sets MethodCallHandler
|
||||
- **Declaration:** `D:\code\new_git_code\flutter\ch34\android\build.gradle` + `pubspec.yaml` declares `package: com.example.ch34`, `pluginClass: Ch34Plugin`
|
||||
|
||||
### MethodChannel Dispatcher
|
||||
- **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` line 80-187
|
||||
- **Trigger:** Dart invokes `methodChannel.invokeMethod()`
|
||||
- **Responsibilities:** Switch dispatch to 33 method handlers, error handling wrapper
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Strategy:** Native exceptions caught and converted to MethodChannel error responses with error codes; Dart side uses null-coalescing defaults and `Ch34Exception` for explicit failures.
|
||||
|
||||
**Patterns:**
|
||||
- **Native:** Each method handler has try-catch, returns `result.error(ERROR_CODE, message, null)`
|
||||
- Error codes: `ENUM_DEVICE_FAILED`, `OPEN_DEVICE_FAILED`, `PERMISSION_DENIED`, `WRITE_DATA_FAILED`, etc.
|
||||
- **Dart:** Default fallbacks on null results (e.g., `result ?? false`, `result ?? Uint8List(0)`)
|
||||
- **Exception class:** `Ch34Exception` in `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` for throwing on Dart side
|
||||
- **Logging:** All errors logged via `Log.e(TAG, "...")` on Android native side
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
**Logging:** Android `android.util.Log` with per-class TAGs (`Ch34Plugin`, `Ch34DataStream`, `Ch34ModemStream`, `Ch34UsbStateStream`). Dart side uses `flutter/foundation.dart` `debugPrint()` for callback errors.
|
||||
|
||||
**Validation:** Device name resolution via `getDeviceOrThrow()` which searches `openedDevices` map first, then falls back to `manager.enumDevice()` lookup.
|
||||
|
||||
**Authentication/Permission:** USB permission flow handled in `openDevice()` - attempts open, if `NoPermissionException`, calls `manager.requestPermission()` then retries after 2-second delay.
|
||||
|
||||
---
|
||||
|
||||
*Architecture analysis: 2026-04-16*
|
||||
167
.planning/codebase/CONCERNS.md
Normal file
167
.planning/codebase/CONCERNS.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Codebase Concerns
|
||||
|
||||
**Analysis Date:** 2026-04-16
|
||||
|
||||
## Technical Debt
|
||||
|
||||
### Placeholder Content in LICENSE and CHANGELOG
|
||||
- Issue: `LICENSE` contains "TODO: Add your license here" and `CHANGELOG.md` contains "TODO: Describe initial release." No license or changelog content exists.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\LICENSE`, `D:\code\new_git_code\flutter\ch34\CHANGELOG.md`
|
||||
- Impact: Legal risk - the package has no defined license. No version history for consumers.
|
||||
- Fix approach: Add an appropriate open-source license (e.g., MIT). Document release history in CHANGELOG.
|
||||
|
||||
### Unimplemented Modem Error Callbacks
|
||||
- Issue: In `registerModemStatusCallback`, the `IModemStatus` interface has three error callbacks (`onOverrunError`, `onParityError`, `onFrameError`) with empty bodies - they silently discard errors.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 667-677)
|
||||
- Impact: Serial communication errors are invisible to the Flutter layer. Users cannot detect data corruption.
|
||||
- Fix approach: Forward these errors through the EventChannel or the data callback, or expose a separate error stream.
|
||||
|
||||
### GPIO Status Map Always Returns Hardcoded Values
|
||||
- Issue: `Ch34TypeConverter.gpioStatusToMap()` hardcodes `index` and `value` to 0, regardless of the actual `GPIO_Status` object. Only `enabled` and `direction` are read from the status.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34TypeConverter.java` (lines 28-29)
|
||||
- Impact: Dart side `queryGpioStatus()` and `queryAllGpioStatus()` always return incorrect `index` and `value` fields.
|
||||
- Fix approach: Read `status.getIndex()` and `status.getValue()` from the WCH `GPIO_Status` object.
|
||||
|
||||
## Known Bugs
|
||||
|
||||
### Data Callback Only Supports Single Device Per Serial Number
|
||||
- Issue: `Ch34DataStreamHandler` uses a single `_dataCallback` field. When multiple devices register data callbacks, each new registration overwrites the previous one. Only the last registered device's data is received.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` (line 44), `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 388-407)
|
||||
- Impact: Multi-device scenarios lose data from all but the most recently registered device.
|
||||
- Fix approach: Use a map keyed by `(deviceName, serialNumber)` to manage multiple independent subscriptions.
|
||||
|
||||
### Hardcoded USB Permission Wait Time
|
||||
- Issue: In `openDevice`, when the initial open fails, a `Handler.postDelayed` waits 2000ms and retries. This is a magic number that may be insufficient on slow devices or excessive on fast ones.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (line 278)
|
||||
- Impact: Permission may not be granted in time on some devices, leading to false failure.
|
||||
- Fix approach: Use a proper permission result callback or `PendingIntent` instead of a fixed delay.
|
||||
|
||||
### `tryOpenDevice` Swallows Non-Permission Exceptions
|
||||
- Issue: In `tryOpenDevice`, any non-`NoPermissionException` error logs the error but returns `true`, treating it as success.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 291-300)
|
||||
- Impact: Failed device opens are reported as successful, leading to downstream errors that are harder to debug.
|
||||
- Fix approach: Return `false` for any exception and let the caller handle the error properly.
|
||||
|
||||
## Security Concerns
|
||||
|
||||
### No Input Validation on Native Method Arguments
|
||||
- Issue: The Java plugin directly casts method call arguments without null checks or type validation (e.g., `call.argument("deviceName")` cast to `String` without null guards).
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (throughout)
|
||||
- Impact: Malformed or malicious method call arguments from Flutter can cause `ClassCastException` or `NullPointerException` on the Android side.
|
||||
- Fix approach: Add null/type checks before using arguments. Return error results for invalid inputs.
|
||||
|
||||
### `pubspec.lock` Ignored for Library Packages
|
||||
- Issue: `pubspec.lock` is in `.gitignore`, which is correct for library packages. However, `example/pubspec.lock` is not explicitly tracked, meaning the example app's dependency versions are not pinned.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\.gitignore` (line 26)
|
||||
- Impact: Example app may resolve to different dependency versions on different machines, causing inconsistent test results.
|
||||
- Fix approach: Consider committing `example/pubspec.lock` for reproducible example builds.
|
||||
|
||||
## Performance Bottlenecks
|
||||
|
||||
### Synchronous `enumDevice` in `getDeviceOrThrow`
|
||||
- Issue: `getDeviceOrThrow` calls `manager.enumDevice()` to enumerate all devices just to find one by name. This is called for nearly every method invocation.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 772-787)
|
||||
- Impact: USB enumeration is slow and repeated unnecessarily. Every method call (read, write, set parameters) triggers this scan if the device is not in `openedDevices`.
|
||||
- Fix approach: Cache device references when they are discovered, or maintain a device lookup map.
|
||||
|
||||
### Blocking `readData` Without Timeout
|
||||
- Issue: `readData` calls `manager.readData(device, serialNumber)` synchronously with no timeout. If no data arrives, the method blocks indefinitely.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 373-385), `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` (lines 176-185)
|
||||
- Impact: Flutter UI thread could freeze if a `readData` call blocks on a quiet serial line.
|
||||
- Fix approach: Use the async callback-based approach for reading, or enforce a configurable timeout.
|
||||
|
||||
## Fragile Areas
|
||||
|
||||
### Single Global WCHUARTManager Instance
|
||||
- Issue: The entire plugin uses a single `WCHUARTManager` instance stored as a field. It is lazily initialized in `ensureManagerInitialized()` and never reset except on engine detach.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 57, 757-770)
|
||||
- Impact: If the WCH library is in a bad state (e.g., after an unhandled error), there is no recovery mechanism. Hot restart during development may cause initialization issues.
|
||||
- Fix approach: Add a `reset()` or `reinitialize()` method for recovery scenarios.
|
||||
|
||||
### EventChannel Single Instance for All Devices
|
||||
- Issue: Each EventChannel (`ch34/data`, `ch34/modem`, `ch34/usb_state`) has a single StreamHandler instance for all devices. The data handler must multiplex all device data through one channel.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 66-76), `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34DataStreamHandler.java`
|
||||
- Impact: Adding device identification to each data event is the responsibility of the consumer. No per-device stream isolation.
|
||||
- Fix approach: Include `deviceName` and `serialNumber` in each data event payload, or create per-device EventChannels.
|
||||
|
||||
### `removeDataCallback` Ignores `serialNumber`
|
||||
- Issue: The Dart API `removeDataCallback` takes only `deviceName`, but `registerDataCallback` takes both `deviceName` and `serialNumber`. If a device has multiple serial ports, removing the callback for one serial number removes all.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart` (line 166), `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart` (lines 160-162)
|
||||
- Impact: Multi-port devices cannot independently manage callbacks per serial port.
|
||||
- Fix approach: Add `serialNumber` parameter to `removeDataCallback`.
|
||||
|
||||
### Swallowed Exceptions in `enumDevice` Loop
|
||||
- Issue: In `enumDevice`, exceptions during `getSerialCount` or `getChipType` are silently caught and ignored. The device is still reported but with incomplete data.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 215)
|
||||
- Impact: Consumers may receive devices with `serialCount = -1` and `chipType = null` with no indication of what went wrong.
|
||||
- Fix approach: At minimum log the exception, or distinguish between "no data available" and "error reading data".
|
||||
|
||||
## Missing Patterns
|
||||
|
||||
### No Structured Error Handling
|
||||
- Issue: The plugin uses `result.error(code, message, null)` with string codes for error reporting. The Dart side never throws typed exceptions (except `Ch34Exception` which is only used for GPIO failures). Most errors silently return `false` or `0`.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` (throughout), `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (throughout)
|
||||
- Impact: Consumers cannot distinguish between different failure modes. Debugging requires enabling debug mode and reading logcat.
|
||||
- Fix approach: Throw `Ch34Exception` with error code and message from all `result.error` responses. Define error code constants.
|
||||
|
||||
### No Lifecycle Management
|
||||
- Issue: The plugin does not handle Android lifecycle events (e.g., app going to background). Active USB connections may be left open or become invalid.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java`
|
||||
- Impact: When the app resumes, existing connections may be in an undefined state.
|
||||
- Fix approach: Implement `ActivityAware` or use `AppLifecycleState` in Dart to pause/resume or clean up connections.
|
||||
|
||||
### No Unit Test Coverage for Core Logic
|
||||
- Issue: The only unit tests cover `getPlatformVersion` and `enumDevice` with stub returns. None of the data conversion, parameter handling, or error paths are tested.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\test\ch34_test.dart`, `D:\code\new_git_code\flutter\ch34\test\ch34_method_channel_test.dart`, `D:\code\new_git_code\flutter\ch34\android\src\test\java\com\example\ch34\Ch34PluginTest.java`
|
||||
- Impact: Refactoring or bug fixes risk introducing regressions with no automated detection.
|
||||
- Fix approach: Add tests for type conversion, error handling, and the mock platform covering all API methods.
|
||||
|
||||
## Dependencies Risk
|
||||
|
||||
### Bundled JAR Dependency Without Version Pinning
|
||||
- Issue: The WCH `CH34XUARTDriver.jar` is included as a local JAR file in `android/libs/` with no version information in the build file. It is loaded via `flatDir` repository.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\build.gradle` (line 47)
|
||||
- Impact: No easy way to track which version of the WCH library is used. Cannot update to newer versions without manual replacement. No transitive dependency management.
|
||||
- Fix approach: Document the JAR version used. Consider hosting in a Maven repository for proper version management.
|
||||
|
||||
### Android Gradle Plugin Version is Outdated
|
||||
- Issue: The build uses `com.android.tools.build:gradle:7.3.0` which is significantly outdated. Current versions are 8.x.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\build.gradle` (line 11)
|
||||
- Impact: May not be compatible with newer Flutter/Android SDK versions. Missing bug fixes and performance improvements.
|
||||
- Fix approach: Update to AGP 8.x and test thoroughly. Update `compileSdk` and `targetSdk` accordingly.
|
||||
|
||||
### Java 8 Target Compatibility
|
||||
- Issue: The plugin compiles with `JavaVersion.VERSION_1_8` as source and target.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\build.gradle` (lines 35-36)
|
||||
- Impact: Cannot use modern Java language features. May limit compatibility with future Android tooling.
|
||||
- Fix approach: Consider updating to Java 11+ if the minimum SDK and tooling support it.
|
||||
|
||||
## Test Coverage Gaps
|
||||
|
||||
### No Integration Tests for USB Operations
|
||||
- Issue: The single integration test only checks `getPlatformVersion`. No tests for actual USB device operations.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\example\integration_test\plugin_integration_test.dart`
|
||||
- Risk: USB-specific bugs are only caught through manual testing.
|
||||
- Priority: High
|
||||
|
||||
### No Tests for Type Conversions
|
||||
- Issue: `Ch34TypeConverter` has zero test coverage. The GPIO and error type conversions are not verified.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\android\src\test\java\com\example\ch34\Ch34PluginTest.java`
|
||||
- Risk: Conversion bugs (like the hardcoded GPIO values) go undetected.
|
||||
- Priority: High
|
||||
|
||||
### No Tests for EventChannel Streams
|
||||
- Issue: No tests verify that data callbacks, modem status, or USB state events flow correctly from native to Dart.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\test\ch34_method_channel_test.dart`, `D:\code\new_git_code\flutter\ch34\test\ch34_test.dart`
|
||||
- Risk: Stream subscription and cancellation logic may have bugs that only manifest at runtime.
|
||||
- Priority: Medium
|
||||
|
||||
### No Tests for Exception Paths
|
||||
- Issue: All tests use happy-path mock returns. No tests simulate errors, timeouts, or null responses.
|
||||
- Files: `D:\code\new_git_code\flutter\ch34\test\ch34_test.dart`
|
||||
- Risk: Error handling code is untested and may fail silently or crash.
|
||||
- Priority: Medium
|
||||
|
||||
---
|
||||
|
||||
*Concerns audit: 2026-04-16*
|
||||
237
.planning/codebase/CONVENTIONS.md
Normal file
237
.planning/codebase/CONVENTIONS.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-04-16
|
||||
|
||||
## Overview
|
||||
|
||||
This is a Flutter plugin package (`ch34`) that provides USB-to-serial communication for WCH CH34X series chips. The codebase is written in Dart with Android native code (Java). The plugin follows the standard Flutter plugin federated architecture pattern.
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
**Files:**
|
||||
- Dart files use `snake_case`: `ch34_manager.dart`, `ch34_platform_interface.dart`, `ch34_method_channel.dart`, `ch34_types.dart`
|
||||
- Test files mirror source file names: `ch34_test.dart`, `ch34_method_channel_test.dart`
|
||||
- Java native files use `PascalCase`: `Ch34Plugin.java`, `Ch34UsbStateStreamHandler.java`
|
||||
|
||||
**Classes:**
|
||||
- PascalCase with `Ch34` prefix for public API: `Ch34Manager`, `Ch34Platform`, `MethodChannelCh34`, `Ch34Exception`
|
||||
- Domain models without prefix: `GpioStatus`, `SerialParameter`, `ModemStatus`, `UsbDeviceInfo`
|
||||
- Enum naming: `DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`
|
||||
|
||||
**Functions/Methods:**
|
||||
- camelCase for all methods: `getPlatformVersion()`, `enumDevice()`, `openDevice()`, `setSerialParameter()`
|
||||
- Callback-style methods use descriptive names: `setUsbStateListener()`, `registerDataCallback()`, `registerModemStatusCallback()`
|
||||
- Private methods use underscore prefix: `_cancelDataSubscription()`
|
||||
- Private constructor pattern: `Ch34Manager._()` (static-only class)
|
||||
|
||||
**Variables:**
|
||||
- camelCase: `deviceName`, `serialNumber`, `gpioIndex`, `onStateChanged`
|
||||
- Private fields use underscore prefix: `_dataSubscription`, `_modemCallback`
|
||||
- Constants use const: `const MethodChannel('ch34')`
|
||||
|
||||
**Parameters:**
|
||||
- Named parameters use curly braces for optional: `{int timeout = 0}`
|
||||
- Required named parameters use `required` keyword in data classes
|
||||
|
||||
## Code Style
|
||||
|
||||
**Formatting:**
|
||||
- Uses `flutter_lints` package (`^3.0.0`) with default `flutter.yaml` ruleset
|
||||
- Config: `D:/code/new_git_code/flutter/ch34/analysis_options.yaml`
|
||||
- Example app config: `D:/code/new_git_code/flutter/ch34/example/analysis_options.yaml` (same base, with commented-out custom rules)
|
||||
- No custom lint rules are enabled beyond the default Flutter set
|
||||
- No `.prettierrc` or other formatter config (uses Dart formatter defaults)
|
||||
|
||||
**SDK Constraints:**
|
||||
- SDK: `>=3.4.3 <4.0.0` (`D:/code/new_git_code/flutter/ch34/pubspec.yaml`)
|
||||
- Flutter: `>=3.3.0`
|
||||
|
||||
**Import Organization:**
|
||||
1. Dart SDK imports first: `import 'dart:typed_data';`, `import 'dart:async';`
|
||||
2. Flutter framework imports: `import 'package:flutter/services.dart';`
|
||||
3. Third-party package imports: `import 'package:plugin_platform_interface/plugin_platform_interface.dart';`
|
||||
4. Relative local imports last: `import 'ch34_platform_interface.dart';`, `import 'types/ch34_types.dart';`
|
||||
- Groups separated by blank lines
|
||||
|
||||
**Path Aliases:**
|
||||
- No path aliases detected; all imports use relative paths within the package
|
||||
|
||||
## Patterns
|
||||
|
||||
**Static Manager Pattern:**
|
||||
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_manager.dart` uses a private constructor (`Ch34Manager._()`) with all static methods, delegating to `Ch34Platform.instance`. This provides a clean public API without requiring instantiation.
|
||||
|
||||
```dart
|
||||
class Ch34Manager {
|
||||
Ch34Manager._();
|
||||
|
||||
static Future<String?> getPlatformVersion() {
|
||||
return Ch34Platform.instance.getPlatformVersion();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Platform Interface (Federated Plugin):**
|
||||
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_platform_interface.dart` extends `PlatformInterface` from `plugin_platform_interface`. Uses token-based verification for instance safety:
|
||||
|
||||
```dart
|
||||
abstract class Ch34Platform extends PlatformInterface {
|
||||
Ch34Platform() : super(token: _token);
|
||||
static final Object _token = Object();
|
||||
static Ch34Platform? _instance;
|
||||
|
||||
static Ch34Platform get instance { ... }
|
||||
static set instance(Ch34Platform instance) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**MethodChannel + EventChannel Pattern:**
|
||||
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_method_channel.dart` uses:
|
||||
- `MethodChannel` for request-response calls: `methodChannel.invokeMethod<bool>('openDevice', {...})`
|
||||
- `EventChannel` for streaming data: `dataEventChannel.receiveBroadcastStream({'deviceName': deviceName}).listen(...)`
|
||||
- `@visibleForTesting` annotation for testable fields (channel names)
|
||||
|
||||
**Data Class Pattern:**
|
||||
`D:/code/new_git_code/flutter/ch34/lib/src/types/ch34_types.dart` uses immutable classes with:
|
||||
- `const` constructors
|
||||
- `fromMap()` factory for deserialization
|
||||
- `toMap()` method for serialization
|
||||
- `toString()` override for debugging
|
||||
- `copyWith()` for mutable updates (e.g., `SerialParameter.copyWith()`)
|
||||
- `==`/`hashCode` override for equality (e.g., `UsbDeviceInfo`)
|
||||
|
||||
```dart
|
||||
class SerialParameter {
|
||||
const SerialParameter({
|
||||
this.baud = 115200,
|
||||
this.dataBits = DataBits.bits8,
|
||||
...
|
||||
});
|
||||
|
||||
factory SerialParameter.fromMap(Map<dynamic, dynamic> map) { ... }
|
||||
Map<String, dynamic> toMap() { ... }
|
||||
SerialParameter copyWith({ ... }) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Enum with Associated Values:**
|
||||
Enums use associated integer values with `fromValue()` factory:
|
||||
|
||||
```dart
|
||||
enum DataBits {
|
||||
bits5(5), bits6(6), bits7(7), bits8(8);
|
||||
const DataBits(this.value);
|
||||
final int value;
|
||||
static DataBits fromValue(int value) =>
|
||||
DataBits.values.firstWhere((e) => e.value == value, orElse: () => DataBits.bits8);
|
||||
}
|
||||
```
|
||||
|
||||
**Null-Safe Default Handling:**
|
||||
Method channel results consistently use null-coalescing for safe defaults:
|
||||
```dart
|
||||
return result ?? false; // boolean methods
|
||||
return result ?? 0; // integer methods
|
||||
return result ?? []; // list methods
|
||||
return result ?? Uint8List(0); // byte data
|
||||
```
|
||||
|
||||
**Section Comments:**
|
||||
Code is organized with clear separator comments:
|
||||
```dart
|
||||
/// ==================== 基础方法 ====================
|
||||
/// ==================== 设备枚举与识别 ====================
|
||||
/// ==================== 数据读写 ====================
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Custom Exception:**
|
||||
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_method_channel.dart` defines `Ch34Exception`:
|
||||
```dart
|
||||
class Ch34Exception implements Exception {
|
||||
const Ch34Exception(this.message);
|
||||
final String message;
|
||||
@override
|
||||
String toString() => 'Ch34Exception: $message';
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Patterns:**
|
||||
- Platform interface methods document `@throws Ch34Exception` in JSDoc comments for methods that may fail
|
||||
- Actual throwing is limited: only GPIO methods throw `Ch34Exception` when native returns null
|
||||
- Most boolean methods return `false` on failure rather than throwing
|
||||
- Example app uses generic `catch (e)` for error display in UI:
|
||||
```dart
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_status = '扫描失败: $e';
|
||||
});
|
||||
}
|
||||
```
|
||||
- `StateError` thrown when platform instance not initialized:
|
||||
```dart
|
||||
throw StateError('Ch34Platform.instance has not been initialized...');
|
||||
```
|
||||
|
||||
**Stream Error Handling:**
|
||||
EventChannel streams use `onError` callback with `debugPrint`:
|
||||
```dart
|
||||
onError: (error) {
|
||||
debugPrint('CH34 data callback error: $error');
|
||||
}
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
**Framework:** Flutter's `debugPrint` from `package:flutter/foundation.dart`
|
||||
|
||||
**Patterns:**
|
||||
- Used only in error callbacks within the method channel implementation
|
||||
- Log messages are descriptive with context prefix: `'CH34 USB state error: $error'`
|
||||
- No logging framework beyond `debugPrint`
|
||||
- Debug mode toggle available via `Ch34Manager.setDebug(bool)` (passes to native side)
|
||||
|
||||
## Comments & Documentation
|
||||
|
||||
**API Documentation:**
|
||||
- All public methods have `///` Dart doc comments in Chinese
|
||||
- Consistent param/return documentation using `@param` and `@return`
|
||||
- Exception documentation using `@throws`
|
||||
- Library-level docs at `D:/code/new_git_code/flutter/ch34/lib/ch34.dart` include usage example in code block
|
||||
|
||||
**Inline Comments:**
|
||||
- Chinese section headers for method grouping
|
||||
- Method-level comments explain purpose, parameters, and return values
|
||||
|
||||
**JSDoc/TSDoc Style:**
|
||||
- Uses `///` (not `/** */`) for doc comments
|
||||
- Parameter docs: `/// @param deviceName 设备名称。`
|
||||
- Return docs: `/// @return \`true\` 成功,\`false\` 失败。`
|
||||
|
||||
## Module Design
|
||||
|
||||
**Exports:**
|
||||
- Main entry `D:/code/new_git_code/flutter/ch34/lib/ch34.dart` exports all public types:
|
||||
```dart
|
||||
export 'src/ch34_manager.dart';
|
||||
export 'src/types/ch34_types.dart';
|
||||
export 'src/ch34_platform_interface.dart' show Ch34Platform;
|
||||
export 'src/ch34_method_channel.dart' show Ch34Exception;
|
||||
```
|
||||
- Backward-compatible barrel files at `lib/` root:
|
||||
- `D:/code/new_git_code/flutter/ch34/lib/ch34_platform_interface.dart`
|
||||
- `D:/code/new_git_code/flutter/ch34/lib/ch34_method_channel.dart`
|
||||
|
||||
**Layer Separation:**
|
||||
```
|
||||
lib/ch34.dart -> Public API entry (exports)
|
||||
lib/src/ch34_manager.dart -> Static facade (public-facing)
|
||||
lib/src/ch34_platform_interface.dart -> Abstract interface
|
||||
lib/src/ch34_method_channel.dart -> Concrete implementation
|
||||
lib/src/types/ch34_types.dart -> Data types and enums
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-04-16*
|
||||
95
.planning/codebase/INTEGRATIONS.md
Normal file
95
.planning/codebase/INTEGRATIONS.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# External Integrations
|
||||
|
||||
**Analysis Date:** 2026-04-16
|
||||
|
||||
## Hardware/Device
|
||||
|
||||
**WCH CH34X Series USB-to-Serial Chips:**
|
||||
- **CH340, CH341, CH342, CH343, CH344, CH347** - Standard USB-to-serial converters
|
||||
- **CH9101, CH9102, CH9103, CH9104** - Newer generation USB-to-serial chips
|
||||
- **CH9143** - Multi-port USB-to-serial chip
|
||||
|
||||
**SDK/Driver:**
|
||||
- Library: `CH34XUARTDriver.jar` (local JAR, 140KB)
|
||||
- Location: `android/libs/CH34XUARTDriver.jar`
|
||||
- Package: `cn.wch.uartlib`
|
||||
- Manager: `WCHUARTManager.getInstance()` - Singleton pattern
|
||||
|
||||
**USB Host Protocol:**
|
||||
- Android USB Host API (`android.hardware.usb.UsbManager`)
|
||||
- Feature declaration: `android.hardware.usb.host` (optional, in `android/src/main/AndroidManifest.xml`)
|
||||
- Permission model: Runtime USB permission via `WCHUARTManager.requestPermission()`
|
||||
|
||||
**VID/PID:**
|
||||
- Default VID/PID managed by WCH driver
|
||||
- Custom VID/PID supported via `Ch34Manager.addNewHardware(vid, pid)` - maps to `WCHUARTManager.addNewHardware()`
|
||||
|
||||
## Communication Channels
|
||||
|
||||
**MethodChannel (`'ch34'`):**
|
||||
- Location: `lib/src/ch34_method_channel.dart` (Dart), `android/src/main/java/com/example/ch34/Ch34Plugin.java` (Java)
|
||||
- 33 methods mapped: `enumDevice`, `openDevice`, `requestPermission`, `writeData`, `readData`, `setDtr`, `setRts`, `setBreak`, `enableGpio`, `setGpioVal`, `getGpioVal`, etc.
|
||||
- Error codes: `ENUM_DEVICE_FAILED`, `OPEN_DEVICE_FAILED`, `PERMISSION_DENIED`, `WRITE_DATA_FAILED`, `GPIO_CHECK_FAILED`, etc.
|
||||
|
||||
**EventChannel (`'ch34/data'`):**
|
||||
- Location: `android/src/main/java/com/example/ch34/Ch34DataStreamHandler.java`
|
||||
- Payload: `byte[]` serial data bytes
|
||||
- Registered via `manager.registerDataCallback(device, callback)`
|
||||
|
||||
**EventChannel (`'ch34/modem'`):**
|
||||
- Location: `android/src/main/java/com/example/ch34/Ch34ModemStreamHandler.java`
|
||||
- Payload: Map with `cts`, `dsr`, `ri`, `dcd` boolean values
|
||||
- Registered via `manager.registerModemStatusCallback(device, IModemStatus)`
|
||||
|
||||
**EventChannel (`'ch34/usb_state'`):**
|
||||
- Location: `android/src/main/java/com/example/ch34/Ch34UsbStateStreamHandler.java`
|
||||
- Payload: Map with `deviceName` (String) and `connected` (boolean)
|
||||
- Triggered by Android broadcast receiver (`UsbManager.ACTION_USB_DEVICE_ATTACHED`, `UsbManager.ACTION_USB_DEVICE_DETACHED`)
|
||||
|
||||
## Data Sources
|
||||
|
||||
**Serial Data:**
|
||||
- Read: `Ch34Manager.readData(deviceName, serialNumber)` -> `Uint8List` (blocking)
|
||||
- Stream: `Ch34Manager.registerDataCallback(deviceName, serialNumber, onData)` -> EventChannel push
|
||||
- Write: `Ch34Manager.writeData(deviceName, serialNumber, data)` -> `int` bytes written
|
||||
|
||||
**GPIO Data:**
|
||||
- Query: `Ch34Manager.queryGpioStatus(deviceName, gpioIndex)` -> `GpioStatus`
|
||||
- Query all: `Ch34Manager.queryAllGpioStatus(deviceName)` -> `List<GpioStatus>`
|
||||
- Control: `Ch34Manager.setGpioVal(deviceName, gpioIndex, value)` / `Ch34Manager.getGpioVal(deviceName, gpioIndex)`
|
||||
|
||||
**Modem Status:**
|
||||
- `ModemStatus` class with fields: `cts` (Clear To Send), `dsr` (Data Set Ready), `ri` (Ring Indicator), `dcd` (Data Carrier Detect)
|
||||
- Registered via `Ch34Manager.registerModemStatusCallback(deviceName, onModemStatus)`
|
||||
|
||||
**Error Types:**
|
||||
- `SerialErrorType.framingError` - Framing error
|
||||
- `SerialErrorType.parityError` - Parity error
|
||||
- `SerialErrorType.overrunError` - Overrun error
|
||||
- `SerialErrorType.breakInterrupt` - Break interrupt
|
||||
- Query via `Ch34Manager.querySerialErrorCount(deviceName, serialNumber, errorType)`
|
||||
|
||||
## Authentication
|
||||
|
||||
**USB Device Permission:**
|
||||
- Android runtime USB permission dialog via `WCHUARTManager.requestPermission(context, device)`
|
||||
- No API keys, tokens, or credentials required
|
||||
- Permission flow: `openDevice` -> if no permission, auto-request -> retry after 2000ms delay
|
||||
|
||||
## Third-party Services
|
||||
|
||||
**None.** This is a pure hardware driver plugin with no external API integrations, cloud services, or network dependencies.
|
||||
|
||||
## CI/CD & Deployment
|
||||
|
||||
**Hosting:** Not applicable (Flutter plugin package, published to pub.dev)
|
||||
**CI Pipeline:** Not detected (no `.github/`, `.gitlab-ci.yml`, or `.circleci/` found)
|
||||
|
||||
## Webhooks & Callbacks
|
||||
|
||||
**Incoming:** None
|
||||
**Outgoing:** None
|
||||
|
||||
---
|
||||
|
||||
*Integration audit: 2026-04-16*
|
||||
104
.planning/codebase/STACK.md
Normal file
104
.planning/codebase/STACK.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Technology Stack
|
||||
|
||||
**Analysis Date:** 2026-04-16
|
||||
|
||||
## Languages
|
||||
|
||||
**Primary:**
|
||||
- **Dart** >=3.4.3 - Flutter plugin Dart-side code (`lib/`, `test/`)
|
||||
- **Java** 8 - Android native plugin implementation (`android/src/main/java/`)
|
||||
|
||||
**Secondary:**
|
||||
- **Kotlin** 1.7.10 - Declared in example app Gradle plugins (`example/android/settings.gradle`) but not actively used in plugin code
|
||||
|
||||
## Runtime
|
||||
|
||||
**Environment:**
|
||||
- **Flutter** >=3.3.0
|
||||
- **Android SDK** - compileSdk 34, minSdk 21
|
||||
|
||||
**Package Manager:**
|
||||
- **pub** (Dart) - Lockfile: `pubspec.lock` present
|
||||
- **Gradle** 7.3.0 - Android build system
|
||||
|
||||
## Frameworks
|
||||
|
||||
**Core:**
|
||||
- **Flutter** >=3.3.0 - Cross-platform plugin framework
|
||||
- **Flutter Plugin API** - `FlutterPlugin`, `MethodCallHandler`, `MethodChannel`, `EventChannel`
|
||||
|
||||
**Testing:**
|
||||
- **flutter_test** (Flutter SDK) - Unit testing framework
|
||||
- **JUnit** 4.13.2 - Android native unit testing
|
||||
- **Mockito** 5.0.0 - Java mocking for Android tests
|
||||
- **integration_test** (Flutter SDK) - Integration testing
|
||||
|
||||
**Build/Dev:**
|
||||
- **flutter_lints** ^3.0.0 - Lint rules
|
||||
- **Gradle** 7.3.0 - Android build automation
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
**Critical:**
|
||||
- **plugin_platform_interface** ^2.0.2 - Federated plugin platform interface pattern (`Ch34Platform` extends `PlatformInterface`)
|
||||
- **CH34XUARTDriver** (local JAR, 140KB) - WCH official UART library, located at `android/libs/CH34XUARTDriver.jar`. Provides `WCHUARTManager`, `ChipType2`, `IDataCallback`, `IModemStatus`, `GPIO_Status` etc.
|
||||
|
||||
**Infrastructure:**
|
||||
- **cupertino_icons** ^1.0.6 - Example app only, Material/Cupertino icon support
|
||||
|
||||
**Android Native Dependencies (WCH SDK):**
|
||||
- `cn.wch.uartlib.WCHUARTManager` - Core manager singleton
|
||||
- `cn.wch.uartlib.chipImpl.type.ChipType2` - Chip type identification
|
||||
- `cn.wch.uartlib.callback.IDataCallback` - Serial data callback interface
|
||||
- `cn.wch.uartlib.callback.IModemStatus` - Modem status callback interface
|
||||
- `cn.wch.uartlib.callback.IUsbStateChange` - USB state change callback
|
||||
- `cn.wch.uartlib.exception.*` - Exception classes (ChipException, NoPermissionException, UartLibException)
|
||||
|
||||
## Configuration
|
||||
|
||||
**Environment:**
|
||||
- `analysis_options.yaml` - Includes `package:flutter_lints/flutter.yaml`
|
||||
- No `.env` or environment variable configuration required
|
||||
- Secrets stored in example `local.properties` (flutter.sdk path)
|
||||
|
||||
**Build:**
|
||||
- `pubspec.yaml` - Main plugin manifest, package: `ch34`, version `1.0.0`
|
||||
- `pubspec.lock` - Dependency lockfile
|
||||
- `android/build.gradle` - Android library build config (namespace: `com.example.ch34`)
|
||||
- `android/settings.gradle` - Project name: `ch34`
|
||||
- `example/android/build.gradle` - Example app root build
|
||||
- `example/android/app/build.gradle` - Example app module (namespace: `com.example.ch34_example`)
|
||||
- `example/android/settings.gradle` - Gradle plugins including dev.flutter.flutter-plugin-loader
|
||||
|
||||
## Platform Requirements
|
||||
|
||||
**Development:**
|
||||
- Flutter SDK >=3.3.0
|
||||
- Dart SDK >=3.4.3
|
||||
- Android SDK compileSdk 34
|
||||
- Java 8+ (sourceCompatibility/targetCompatibility = JavaVersion.VERSION_1_8)
|
||||
|
||||
**Production:**
|
||||
- Android device with USB Host support (`android.hardware.usb.host` feature declared in `android/src/main/AndroidManifest.xml`)
|
||||
- Android minSdk 21 (Android 5.0 Lollipop)
|
||||
- Physical CH34X series USB-to-serial hardware connected via USB OTG
|
||||
|
||||
## Supported Chip Models
|
||||
|
||||
CH340, CH341, CH342, CH343, CH344, CH347, CH9101, CH9102, CH9103, CH9104, CH9143
|
||||
|
||||
## Build System
|
||||
|
||||
**Flutter Plugin:** Standard Flutter plugin structure with Android platform implementation.
|
||||
|
||||
**Communication Pattern:**
|
||||
- `MethodChannel('ch34')` - 33 method calls for device control
|
||||
- `EventChannel('ch34/data')` - Serial data streaming
|
||||
- `EventChannel('ch34/modem')` - Modem status streaming
|
||||
- `EventChannel('ch34/usb_state')` - USB plug/unplug events
|
||||
|
||||
**Architecture Pattern:** Federated plugin with platform interface (`Ch34Platform`), method channel implementation (`MethodChannelCh34`), and manager facade (`Ch34Manager`).
|
||||
|
||||
---
|
||||
|
||||
*Stack analysis: 2026-04-16*
|
||||
217
.planning/codebase/STRUCTURE.md
Normal file
217
.planning/codebase/STRUCTURE.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# Codebase Structure
|
||||
|
||||
**Analysis Date:** 2026-04-16
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
ch34/ # Flutter plugin root
|
||||
├── lib/ # Dart source code
|
||||
│ ├── ch34.dart # Main library entry point (exports)
|
||||
│ ├── ch34_platform_interface.dart # Backward compat: re-exports src/
|
||||
│ ├── ch34_method_channel.dart # Backward compat: re-exports src/
|
||||
│ └── src/ # Core implementation
|
||||
│ ├── ch34_manager.dart # Public static API facade
|
||||
│ ├── ch34_platform_interface.dart # Abstract platform interface
|
||||
│ ├── ch34_method_channel.dart # MethodChannel implementation
|
||||
│ └── types/
|
||||
│ └── ch34_types.dart # All data types and enums
|
||||
├── android/ # Android native code
|
||||
│ ├── build.gradle # Android library build config
|
||||
│ ├── settings.gradle # Gradle settings
|
||||
│ ├── libs/ # Native dependency JARs
|
||||
│ │ └── CH34XUARTDriver.jar # WCH UART library
|
||||
│ └── src/
|
||||
│ ├── main/java/com/example/ch34/
|
||||
│ │ ├── Ch34Plugin.java # Main plugin + MethodCallHandler
|
||||
│ │ ├── Ch34DataStreamHandler.java # Data EventChannel handler
|
||||
│ │ ├── Ch34ModemStreamHandler.java # Modem EventChannel handler
|
||||
│ │ ├── Ch34UsbStateStreamHandler.java # USB hotplug handler
|
||||
│ │ └── Ch34TypeConverter.java # Type conversion utilities
|
||||
│ └── test/java/com/example/ch34/
|
||||
│ └── Ch34PluginTest.java # Unit tests
|
||||
├── example/ # Example Flutter app
|
||||
│ ├── lib/
|
||||
│ │ └── main.dart # Example app with device scan/connect/send
|
||||
│ ├── test/
|
||||
│ │ └── widget_test.dart # Widget test (stub)
|
||||
│ └── integration_test/
|
||||
│ └── plugin_integration_test.dart # Integration test
|
||||
├── test/ # Plugin unit tests
|
||||
│ ├── ch34_test.dart # Platform interface + manager tests
|
||||
│ └── ch34_method_channel_test.dart # MethodChannel mock tests
|
||||
├── docs/
|
||||
│ └── CH34X-api_docs.md # WCH API documentation reference
|
||||
├── pubspec.yaml # Plugin package manifest
|
||||
├── analysis_options.yaml # Dart linting config
|
||||
├── CHANGELOG.md # Version history
|
||||
├── LICENSE # License file
|
||||
├── README.md # Plugin documentation
|
||||
└── .gitignore # Git ignore rules
|
||||
```
|
||||
|
||||
## Directory Purposes
|
||||
|
||||
### `lib/` - Dart Source
|
||||
- **Purpose:** All Dart code for the Flutter plugin
|
||||
- **Key files:**
|
||||
- `lib/ch34.dart` - Primary consumer import: `import 'package:ch34/ch34.dart';`
|
||||
- `lib/src/ch34_manager.dart` - Static facade, all public APIs
|
||||
- `lib/src/ch34_platform_interface.dart` - Abstract interface with 33 methods
|
||||
- `lib/src/ch34_method_channel.dart` - MethodChannel + EventChannel implementation
|
||||
- `lib/src/types/ch34_types.dart` - Shared data models and enums
|
||||
|
||||
### `android/` - Android Native
|
||||
- **Purpose:** Java implementation bridging to WCH CH34X UART library
|
||||
- **Key files:**
|
||||
- `android/src/main/java/com/example/ch34/Ch34Plugin.java` - Plugin entry point (788 lines)
|
||||
- `android/build.gradle` - Library configuration (compileSdk 34, minSdk 21, Java 8)
|
||||
- `android/libs/CH34XUARTDriver.jar` - WCH vendor SDK (binary dependency)
|
||||
|
||||
### `example/` - Sample Application
|
||||
- **Purpose:** Demonstrates plugin usage for development and testing
|
||||
- **Key files:**
|
||||
- `example/lib/main.dart` - Full working example: scan devices, open, set params, send/receive data
|
||||
|
||||
### `test/` - Plugin Tests
|
||||
- **Purpose:** Unit tests for the Dart plugin code
|
||||
- **Contains:** MethodChannel mock tests, platform interface mock implementation
|
||||
|
||||
## Key File Locations
|
||||
|
||||
**Entry Points:**
|
||||
- `lib/ch34.dart`: Main library entry, consumer-facing import
|
||||
- `android/src/main/java/com/example/ch34/Ch34Plugin.java`: Android plugin entry, registered via `pubspec.yaml`
|
||||
|
||||
**Configuration:**
|
||||
- `pubspec.yaml`: Plugin declaration, Android platform registration (`package: com.example.ch34`, `pluginClass: Ch34Plugin`)
|
||||
- `android/build.gradle`: Android library build configuration
|
||||
- `analysis_options.yaml`: Dart linting (extends `flutter_lints`)
|
||||
|
||||
**Core Logic:**
|
||||
- `lib/src/ch34_manager.dart`: Static API facade (all consumer methods)
|
||||
- `lib/src/ch34_platform_interface.dart`: Abstract interface contract
|
||||
- `lib/src/ch34_method_channel.dart`: Channel communication implementation
|
||||
- `android/src/main/java/com/example/ch34/Ch34Plugin.java`: Native bridge logic
|
||||
|
||||
**Types:**
|
||||
- `lib/src/types/ch34_types.dart`: All data classes (`UsbDeviceInfo`, `SerialParameter`, `ModemStatus`, `GpioStatus`) and enums (`DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`)
|
||||
|
||||
**Testing:**
|
||||
- `test/ch34_method_channel_test.dart`: Unit tests with mock platform
|
||||
- `test/ch34_test.dart`: Integration-style tests with real MethodChannel mock
|
||||
- `example/integration_test/plugin_integration_test.dart`: On-device integration test
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
**Files:**
|
||||
- Dart: `snake_case.dart` for all files (e.g., `ch34_manager.dart`, `ch34_types.dart`)
|
||||
- Java: `PascalCase.java` for all classes (e.g., `Ch34Plugin.java`, `Ch34TypeConverter.java`)
|
||||
|
||||
**Dart Classes:**
|
||||
- Public API class: `Ch34Manager` (static methods, facade pattern)
|
||||
- Platform interface: `Ch34Platform` (abstract, extends `PlatformInterface`)
|
||||
- Channel implementation: `MethodChannelCh34` (extends `Ch34Platform`)
|
||||
- Data types: `UsbDeviceInfo`, `SerialParameter`, `ModemStatus`, `GpioStatus`
|
||||
- Enums: `DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`
|
||||
- Exception: `Ch34Exception`
|
||||
|
||||
**Java Classes:**
|
||||
- Main plugin: `Ch34Plugin` (implements `FlutterPlugin`, `MethodCallHandler`)
|
||||
- Stream handlers: `Ch34DataStreamHandler`, `Ch34ModemStreamHandler`, `Ch34UsbStateStreamHandler`
|
||||
- Utility: `Ch34TypeConverter` (static methods only, private constructor)
|
||||
|
||||
**MethodChannel names:**
|
||||
- Method channel: `'ch34'`
|
||||
- Data event channel: `'ch34/data'`
|
||||
- Modem event channel: `'ch34/modem'`
|
||||
- USB state event channel: `'ch34/usb_state'`
|
||||
|
||||
**Method names:** Match Dart method names exactly (e.g., `enumDevice`, `openDevice`, `setSerialParameter`, `writeData`, `readData`)
|
||||
|
||||
## Where to Add New Code
|
||||
|
||||
**New Dart API Method:**
|
||||
1. Add abstract method signature to `lib/src/ch34_platform_interface.dart`
|
||||
2. Add concrete implementation to `lib/src/ch34_method_channel.dart` (using `methodChannel.invokeMethod()`)
|
||||
3. Add static wrapper to `lib/src/ch34_manager.dart` (delegating to `Ch34Platform.instance`)
|
||||
4. Add mock implementation in `test/ch34_method_channel_test.dart` for testing
|
||||
5. Add native handler in `android/src/main/java/com/example/ch34/Ch34Plugin.java`
|
||||
|
||||
**New EventChannel (streaming):**
|
||||
1. Create new `*StreamHandler.java` in `android/src/main/java/com/example/ch34/`
|
||||
2. Register in `Ch34Plugin.onAttachedToEngine()`
|
||||
3. Add `EventChannel` and `StreamSubscription` in `lib/src/ch34_method_channel.dart`
|
||||
4. Add platform interface methods to `Ch34Platform`
|
||||
5. Add static wrappers to `Ch34Manager`
|
||||
|
||||
**New Data Type:**
|
||||
- Add to `lib/src/types/ch34_types.dart`
|
||||
- Add corresponding conversion in `android/src/main/java/com/example/ch34/Ch34TypeConverter.java`
|
||||
|
||||
**New Native Utility:**
|
||||
- Add to `android/src/main/java/com/example/ch34/` as a new Java class or extend existing
|
||||
|
||||
## Module Boundaries
|
||||
|
||||
### Dart Layer
|
||||
```
|
||||
lib/ch34.dart (exports)
|
||||
|
|
||||
v
|
||||
lib/src/ch34_manager.dart (static facade)
|
||||
|
|
||||
v
|
||||
lib/src/ch34_platform_interface.dart (abstract contract)
|
||||
|
|
||||
v
|
||||
lib/src/ch34_method_channel.dart (concrete implementation)
|
||||
|
|
||||
+---> lib/src/types/ch34_types.dart (shared types)
|
||||
```
|
||||
|
||||
### Native Layer
|
||||
```
|
||||
Ch34Plugin.java (MethodCallHandler + 3 EventChannels)
|
||||
|
|
||||
+---> Ch34DataStreamHandler.java (data streaming)
|
||||
+---> Ch34ModemStreamHandler.java (modem status streaming)
|
||||
+---> Ch34UsbStateStreamHandler.java (USB hotplug streaming)
|
||||
+---> Ch34TypeConverter.java (type conversion utilities)
|
||||
|
|
||||
v
|
||||
CH34XUARTDriver.jar (WCH vendor SDK)
|
||||
```
|
||||
|
||||
### Platform Bridge
|
||||
```
|
||||
Dart: MethodChannel('ch34') <---> Java: Ch34Plugin (MethodCallHandler)
|
||||
Dart: EventChannel('ch34/data') <---> Java: Ch34DataStreamHandler
|
||||
Dart: EventChannel('ch34/modem') <---> Java: Ch34ModemStreamHandler
|
||||
Dart: EventChannel('ch34/usb_state') <---> Java: Ch34UsbStateStreamHandler
|
||||
```
|
||||
|
||||
## Special Directories
|
||||
|
||||
**`android/libs/`:**
|
||||
- Purpose: Contains binary JAR dependency (`CH34XUARTDriver.jar`)
|
||||
- Generated: No, committed to repo
|
||||
- Referenced by: `android/build.gradle` via `flatDir` repository
|
||||
|
||||
**`example/`:**
|
||||
- Purpose: Self-contained Flutter app demonstrating plugin usage
|
||||
- Generated: No, committed to repo
|
||||
- Note: The example app package is `ch34_example`
|
||||
|
||||
**`docs/`:**
|
||||
- Purpose: API documentation reference for WCH WCHUARTManager
|
||||
- Contains: `CH34X-api_docs.md` - comprehensive API documentation in Markdown
|
||||
|
||||
**`lib/` root-level files:**
|
||||
- Purpose: Backward compatibility re-exports
|
||||
- `lib/ch34_platform_interface.dart` exports `src/ch34_platform_interface.dart`
|
||||
- `lib/ch34_method_channel.dart` exports `src/ch34_method_channel.dart`
|
||||
|
||||
---
|
||||
|
||||
*Structure analysis: 2026-04-16*
|
||||
232
.planning/codebase/TESTING.md
Normal file
232
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-04-16
|
||||
|
||||
## Framework
|
||||
|
||||
**Runner:**
|
||||
- `flutter_test` (Flutter SDK built-in)
|
||||
- `integration_test` (Flutter SDK built-in, for E2E/integration tests)
|
||||
- No explicit `test` package dependency (uses Flutter's bundled test framework)
|
||||
|
||||
**Assertion Library:**
|
||||
- Built-in `flutter_test` matchers (`expect`, `findsOneWidget`, `isEmpty`, `isInstanceOf`)
|
||||
|
||||
**Dependencies:**
|
||||
- `flutter_test` (sdk: flutter) - Unit and widget testing
|
||||
- `flutter_lints` (^3.0.0) - Code analysis
|
||||
- `plugin_platform_interface` (^2.0.2) - Required for mock platform interface mixin
|
||||
|
||||
**Run Commands:**
|
||||
```bash
|
||||
flutter test # Run all unit tests
|
||||
flutter test --coverage # Run tests with coverage
|
||||
flutter test test/ch34_test.dart # Run a specific test file
|
||||
flutter test integration_test/ # Run integration tests (from example/)
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
**Test File Organization:**
|
||||
- Plugin tests in `D:/code/new_git_code/flutter/ch34/test/`:
|
||||
- `ch34_test.dart` - Unit tests with mock platform
|
||||
- `ch34_method_channel_test.dart` - MethodChannel-level tests with mock handler
|
||||
- Example app tests in `D:/code/new_git_code/flutter/ch34/example/test/`:
|
||||
- `widget_test.dart` - Widget tests for example app
|
||||
- Integration tests in `D:/code/new_git_code/flutter/ch34/example/integration_test/`:
|
||||
- `plugin_integration_test.dart` - End-to-end plugin tests on real device
|
||||
|
||||
**Naming:**
|
||||
- Test files: `{source_file}_test.dart` pattern
|
||||
- Integration test files: `{feature}_integration_test.dart`
|
||||
|
||||
**Test Entry Pattern:**
|
||||
All tests call framework initialization before running:
|
||||
```dart
|
||||
// Unit tests
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Integration tests
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
```
|
||||
|
||||
**Suite Organization:**
|
||||
Unit tests follow a consistent pattern:
|
||||
|
||||
```dart
|
||||
// D:/code/new_git_code/flutter/ch34/test/ch34_test.dart
|
||||
void main() {
|
||||
MethodChannelCh34.registerDefault();
|
||||
final Ch34Platform initialPlatform = Ch34Platform.instance;
|
||||
|
||||
test('$MethodChannelCh34 is the default instance', () {
|
||||
expect(initialPlatform, isInstanceOf<MethodChannelCh34>());
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
MockCh34Platform fakePlatform = MockCh34Platform();
|
||||
Ch34Platform.instance = fakePlatform;
|
||||
|
||||
expect(await Ch34Manager.getPlatformVersion(), '42');
|
||||
});
|
||||
|
||||
test('enumDevice returns empty list', () async {
|
||||
MockCh34Platform fakePlatform = MockCh34Platform();
|
||||
Ch34Platform.instance = fakePlatform;
|
||||
|
||||
final devices = await Ch34Manager.enumDevice();
|
||||
expect(devices, isEmpty);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Mocking
|
||||
|
||||
**Framework:** Manual mock classes (no mockito or mocktail dependency)
|
||||
|
||||
**Platform Mock Pattern:**
|
||||
`D:/code/new_git_code/flutter/ch34/test/ch34_test.dart` uses `MockPlatformInterfaceMixin` for creating test doubles:
|
||||
|
||||
```dart
|
||||
class MockCh34Platform
|
||||
with MockPlatformInterfaceMixin
|
||||
implements Ch34Platform {
|
||||
|
||||
@override
|
||||
Future<String?> getPlatformVersion() => Future.value('42');
|
||||
|
||||
@override
|
||||
Future<List<UsbDeviceInfo>> enumDevice() => Future.value([]);
|
||||
|
||||
// ... all 30+ methods must be overridden
|
||||
}
|
||||
```
|
||||
|
||||
**Mock Usage Pattern:**
|
||||
Mock instances are swapped into the platform interface before each test:
|
||||
```dart
|
||||
MockCh34Platform fakePlatform = MockCh34Platform();
|
||||
Ch34Platform.instance = fakePlatform;
|
||||
|
||||
expect(await Ch34Manager.getPlatformVersion(), '42');
|
||||
```
|
||||
|
||||
**MethodChannel Mock Pattern:**
|
||||
`D:/code/new_git_code/flutter/ch34/test/ch34_method_channel_test.dart` uses `setMockMethodCallHandler` for low-level channel mocking:
|
||||
|
||||
```dart
|
||||
const MethodChannel channel = MethodChannel('ch34');
|
||||
|
||||
setUp(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(
|
||||
channel,
|
||||
(MethodCall methodCall) async {
|
||||
return '42'; // Returns same value for all method calls
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
```
|
||||
|
||||
**What to Mock:**
|
||||
- Platform interface layer (`Ch34Platform`) for testing `Ch34Manager` delegation
|
||||
- `MethodChannel` for testing `MethodChannelCh34` channel mapping
|
||||
|
||||
**What NOT to Mock:**
|
||||
- Native Android code (tested via integration tests on real device)
|
||||
- EventChannel streams (not currently mocked in any test)
|
||||
|
||||
## Coverage
|
||||
|
||||
**Requirements:** No coverage target enforced
|
||||
|
||||
**Current State:**
|
||||
- Only 2 unit test assertions beyond boilerplate (`getPlatformVersion`, `enumDevice returns empty list`)
|
||||
- MethodChannel test has only 1 assertion (`getPlatformVersion`)
|
||||
- Integration test has 1 assertion (`getPlatformVersion` on real device)
|
||||
- Most platform methods have zero test coverage (GPIO, serial I/O, modem status, signal control, etc.)
|
||||
|
||||
**View Coverage:**
|
||||
```bash
|
||||
flutter test --coverage
|
||||
# Coverage written to coverage/lcov.info
|
||||
```
|
||||
|
||||
## Test Locations
|
||||
|
||||
**Unit Tests:**
|
||||
- `D:/code/new_git_code/flutter/ch34/test/ch34_test.dart` - Ch34Manager + mock platform
|
||||
- `D:/code/new_git_code/flutter/ch34/test/ch34_method_channel_test.dart` - MethodChannel tests
|
||||
|
||||
**Integration Tests:**
|
||||
- `D:/code/new_git_code/flutter/ch34/example/integration_test/plugin_integration_test.dart` - Real device tests
|
||||
|
||||
**Widget Tests:**
|
||||
- `D:/code/new_git_code/flutter/ch34/example/test/widget_test.dart` - Example app widget test (currently outdated - looks for "Running on:" text that no longer exists)
|
||||
|
||||
## Test Types
|
||||
|
||||
**Unit Tests:**
|
||||
- Scope: Test `Ch34Manager` delegation to mocked platform
|
||||
- Approach: Replace `Ch34Platform.instance` with `MockCh34Platform`
|
||||
- Current coverage: 2 methods tested out of 30+ API methods
|
||||
|
||||
**Integration Tests:**
|
||||
- Framework: `integration_test` package
|
||||
- Approach: Run on real Android device with actual USB hardware
|
||||
- Current coverage: 1 test (`getPlatformVersion`)
|
||||
|
||||
**E2E Tests:**
|
||||
- No dedicated E2E test framework beyond `integration_test`
|
||||
|
||||
## Common Patterns
|
||||
|
||||
**Async Testing:**
|
||||
All platform method tests use `async/await`:
|
||||
```dart
|
||||
test('enumDevice returns empty list', () async {
|
||||
MockCh34Platform fakePlatform = MockCh34Platform();
|
||||
Ch34Platform.instance = fakePlatform;
|
||||
|
||||
final devices = await Ch34Manager.enumDevice();
|
||||
expect(devices, isEmpty);
|
||||
});
|
||||
```
|
||||
|
||||
**Error Testing:**
|
||||
Mock throws exceptions to test error paths:
|
||||
```dart
|
||||
@override
|
||||
Future<GpioStatus> queryGpioStatus(String deviceName, int gpioIndex) =>
|
||||
throw const Ch34Exception('Not supported');
|
||||
```
|
||||
|
||||
**Setup/Teardown Pattern:**
|
||||
```dart
|
||||
setUp(() {
|
||||
// Configure mock method channel handler
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, handler);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
// Clean up mock handler
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
```
|
||||
|
||||
## CI Testing
|
||||
|
||||
**CI Pipeline:** Not detected - no `.github/workflows/`, `.gitlab-ci.yml`, or `azure-pipelines.yml` found.
|
||||
|
||||
**Recommendation:** Tests should be run via `flutter analyze && flutter test` in any CI pipeline.
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-04-16*
|
||||
Reference in New Issue
Block a user