feat(ch934x_serial): 添加CH934X串口设备权限管理和端口切换功能
- 新增 setActivePort 方法用于在同一USB设备内部切换活跃串口 - 新增 requestUsbPermission 方法用于主动申请指定设备的USB权限 - 实现Android侧USB权限申请广播接收器和同步等待机制 - 在示例应用中集成权限预申请和端口切换UI逻辑 - 优化设备打开后自动拉取真实串口列表的功能 - 更新端口选择下拉框在打开状态下支持实时切换串口
This commit is contained in:
+46
-5
@@ -86,6 +86,9 @@ class _Ch934xSerialExamplePageState extends State<Ch934xSerialExamplePage> {
|
||||
}
|
||||
|
||||
/// 触发设备列表刷新(对应 4.1.4)。
|
||||
///
|
||||
/// 刷新时主动给每个设备申请一次 USB 权限,避免用户点
|
||||
/// "打开"时再被弹框打断。已授权的设备 SDK 会立即返回 true。
|
||||
Future<void> _refreshDeviceList() async {
|
||||
try {
|
||||
final devices = await _plugin.getDeviceList();
|
||||
@@ -109,6 +112,10 @@ class _Ch934xSerialExamplePageState extends State<Ch934xSerialExamplePage> {
|
||||
}
|
||||
_status = '已扫描到 ${devices.length} 台 CH934X 设备';
|
||||
});
|
||||
// 后台并发请求权限,失败也不阻塞 UI。
|
||||
for (final device in devices) {
|
||||
unawaited(_plugin.requestUsbPermission(device.deviceId));
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() => _status = '设备扫描失败: $e');
|
||||
@@ -117,8 +124,8 @@ class _Ch934xSerialExamplePageState extends State<Ch934xSerialExamplePage> {
|
||||
|
||||
/// 把设备的串口描述转换为可用索引列表。
|
||||
///
|
||||
/// 优先使用 SDK 返回的 [Ch934xSerialPortInfo.portIndex],缺
|
||||
/// 失时回退到 0..N-1,确保 UI 始终有可选项。
|
||||
/// 若 SDK 还未在原生侧拿到真实串口数(需要 openPort 之后才能问出来),
|
||||
/// 退回到 [Ch934xDeviceInfo.interfaceCount] 或最小占位 [0]。
|
||||
List<int> _resolvePortIndices(Ch934xDeviceInfo device) {
|
||||
if (device.serialPorts.isNotEmpty) {
|
||||
return device.serialPorts.map((p) => p.portIndex).toList()..sort();
|
||||
@@ -126,10 +133,30 @@ class _Ch934xSerialExamplePageState extends State<Ch934xSerialExamplePage> {
|
||||
if (device.interfaceCount > 0) {
|
||||
return List<int>.generate(device.interfaceCount, (i) => i);
|
||||
}
|
||||
// 最坏情况下,允许打开串口 0;用户可通过业务调用切换。
|
||||
return const <int>[0];
|
||||
}
|
||||
|
||||
/// 打开设备后,主动从原生侧拉取真实串口列表。
|
||||
Future<void> _refreshPortListAfterOpen(Ch934xDeviceInfo device) async {
|
||||
final ports = await _plugin.getSerialPortList(
|
||||
device.deviceId,
|
||||
interfaceNumber: 0,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
// 用 SDK 返回的端口索引覆盖,确保与真实设备保持一致。
|
||||
_availablePortIndices =
|
||||
ports.map((p) => p.portIndex).toList()..sort();
|
||||
if (_availablePortIndices.isEmpty) {
|
||||
_availablePortIndices = const <int>[0];
|
||||
}
|
||||
if (_selectedPortIndex == null ||
|
||||
!_availablePortIndices.contains(_selectedPortIndex)) {
|
||||
_selectedPortIndex = _availablePortIndices.first;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _openPort() async {
|
||||
final device = _selectedDevice;
|
||||
final portIndex = _selectedPortIndex;
|
||||
@@ -149,9 +176,14 @@ class _Ch934xSerialExamplePageState extends State<Ch934xSerialExamplePage> {
|
||||
}
|
||||
setState(() {
|
||||
_portOpened = true;
|
||||
_status = '串口 $portIndex 已打开';
|
||||
_status = '串口 $portIndex 已打开,正在拉取真实串口列表';
|
||||
});
|
||||
_startReceiving();
|
||||
// openDevice 之后 SDK 才能正确返回串口数,刷新下拉框。
|
||||
await _refreshPortListAfterOpen(device);
|
||||
if (mounted && _portOpened) {
|
||||
setState(() => _status = '已打开串口 $portIndex,可用 ${_availablePortIndices.length} 路');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _closePort() async {
|
||||
@@ -275,7 +307,16 @@ class _Ch934xSerialExamplePageState extends State<Ch934xSerialExamplePage> {
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (idx) => setState(() => _selectedPortIndex = idx),
|
||||
onChanged: _portOpened
|
||||
? (idx) async {
|
||||
if (idx == null) return;
|
||||
setState(() => _selectedPortIndex = idx);
|
||||
await _plugin.setActivePort(idx);
|
||||
if (mounted) {
|
||||
setState(() => _status = '已切换到串口 $idx');
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Reference in New Issue
Block a user