Enhance USB functionality by adding request port label management and platform support checks

This commit is contained in:
just_stuff_tm
2026-03-02 05:28:31 -05:00
committed by just-stuff-tm
parent ca5784f3f8
commit 781090243c
7 changed files with 93 additions and 34 deletions
+40 -18
View File
@@ -5,6 +5,7 @@ import 'package:flserial/flserial_exception.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../utils/platform_info.dart';
import '../utils/usb_port_labels.dart';
import 'usb_serial_frame_codec.dart';
@@ -21,28 +22,38 @@ class UsbSerialService {
);
final StreamController<Uint8List> _frameController =
StreamController<Uint8List>.broadcast();
final FlSerial _serial = FlSerial();
final UsbSerialFrameDecoder _frameDecoder = UsbSerialFrameDecoder();
StreamSubscription<dynamic>? _androidDataSubscription;
StreamSubscription<FlSerialEventArgs>? _dataSubscription;
UsbSerialStatus _status = UsbSerialStatus.disconnected;
String? _connectedPortName;
FlSerial? _serial;
UsbSerialStatus get status => _status;
String? get activePortName => _connectedPortName;
Stream<Uint8List> get frameStream => _frameController.stream;
bool get _useAndroidUsbHost =>
!kIsWeb && defaultTargetPlatform == TargetPlatform.android;
bool get _useDesktopFlSerial =>
PlatformInfo.isWindows || PlatformInfo.isLinux;
bool get _isSupportedPlatform => _useAndroidUsbHost || _useDesktopFlSerial;
FlSerial get _nativeSerial => _serial ??= FlSerial();
bool get isConnected {
if (!_isSupportedPlatform) {
return false;
}
if (_useAndroidUsbHost) {
return _status == UsbSerialStatus.connected;
}
return _status == UsbSerialStatus.connected &&
_serial.isOpen() == FlOpenStatus.open;
_serial?.isOpen() == FlOpenStatus.open;
}
Future<List<String>> listPorts() async {
if (!_isSupportedPlatform) {
return const <String>[];
}
if (_useAndroidUsbHost) {
final ports = await _androidMethodChannel.invokeListMethod<String>(
'listPorts',
@@ -60,6 +71,9 @@ class UsbSerialService {
_status == UsbSerialStatus.connecting) {
throw StateError('USB serial transport is already active');
}
if (!_isSupportedPlatform) {
throw UnsupportedError('USB serial is not supported on this platform.');
}
_status = UsbSerialStatus.connecting;
final normalizedPortName = normalizeUsbPortName(portName);
@@ -78,32 +92,35 @@ class UsbSerialService {
throw StateError(error.message ?? error.code);
}
} else {
_serial.init();
final serial = _nativeSerial;
serial.init();
try {
final status = _serial.openPort(normalizedPortName, baudRate);
final status = serial.openPort(normalizedPortName, baudRate);
if (status != FlOpenStatus.open) {
throw StateError(
'Failed to open USB port $normalizedPortName ($status)',
);
}
_serial.setByteSize8();
_serial.setBitParityNone();
_serial.setStopBits1();
_serial.setFlowControlNone();
_serial.setRTS(false);
_serial.setDTR(true);
serial.setByteSize8();
serial.setBitParityNone();
serial.setStopBits1();
serial.setFlowControlNone();
serial.setRTS(false);
serial.setDTR(true);
debugPrint(
'USB serial opened port=$normalizedPortName cts=${_serial.getCTS()} dsr=${_serial.getDSR()} dtr=true rts=false',
'USB serial opened port=$normalizedPortName cts=${serial.getCTS()} dsr=${serial.getDSR()} dtr=true rts=false',
);
} on FlSerialException catch (error) {
_serial.free();
_serial?.free();
_serial = null;
_status = UsbSerialStatus.disconnected;
throw StateError(
'Failed to open USB port $normalizedPortName: ${error.msg} (${error.error})',
);
} catch (error) {
_serial.free();
_serial?.free();
_serial = null;
_status = UsbSerialStatus.disconnected;
rethrow;
}
@@ -119,7 +136,7 @@ class UsbSerialService {
onDone: _handleSerialDone,
);
} else {
_dataSubscription = _serial.onSerialData.stream.listen(
_dataSubscription = _nativeSerial.onSerialData.stream.listen(
_handleSerialData,
onError: _handleSerialError,
onDone: _handleSerialDone,
@@ -143,7 +160,7 @@ class UsbSerialService {
throw StateError(error.message ?? error.code);
}
} else {
_serial.write(packet);
_nativeSerial.write(packet);
}
}
@@ -165,18 +182,23 @@ class UsbSerialService {
}
} else {
try {
if (_serial.isOpen() == FlOpenStatus.open) {
_serial.closePort();
if (_serial?.isOpen() == FlOpenStatus.open) {
_serial?.closePort();
}
} catch (_) {
// Ignore errors while closing.
}
_serial.free();
_serial?.free();
_serial = null;
}
_status = UsbSerialStatus.disconnected;
}
void setRequestPortLabel(String label) {
// Native implementations do not use a synthetic chooser row.
}
void updateConnectedLabel(String label) {
final trimmed = label.trim();
if (trimmed.isEmpty) {
+14 -4
View File
@@ -26,6 +26,7 @@ class UsbSerialService {
JSObject? _writer;
String? _connectedPortName;
String? _connectedPortKey;
String _requestPortLabel = 'Choose USB Device';
UsbSerialStatus get status => _status;
String? get activePortName => _connectedPortName;
@@ -49,7 +50,7 @@ class UsbSerialService {
final ports = await _getAuthorizedPorts();
if (ports.isEmpty) {
return const <String>[usbRequestPortLabel];
return <String>[_requestPortLabel];
}
return ports.map(_displayLabelForPort).toList(growable: false);
}
@@ -159,6 +160,14 @@ class UsbSerialService {
_connectedPortName = _buildDisplayLabel(portKey);
}
void setRequestPortLabel(String label) {
final trimmed = label.trim();
if (trimmed.isEmpty) {
return;
}
_requestPortLabel = trimmed;
}
void dispose() {
unawaited(disconnect().whenComplete(_closeFrameController));
}
@@ -189,7 +198,7 @@ class UsbSerialService {
if (ports.isEmpty) {
return null;
}
if (requestedPortName.isEmpty || requestedPortName == usbRequestPortLabel) {
if (requestedPortName.isEmpty || requestedPortName == _requestPortLabel) {
return ports.first;
}
for (final port in ports) {
@@ -350,7 +359,7 @@ class UsbSerialService {
try {
final info = port.callMethod<JSAny?>('getInfo'.toJS);
if (info == null) {
return usbRequestPortLabel;
return _requestPortLabel;
}
final infoObject = info as JSObject;
@@ -366,10 +375,11 @@ class UsbSerialService {
return describeWebUsbPort(
vendorId: hasVendor ? vendorId.toInt() : null,
productId: hasProduct ? productId.toInt() : null,
requestPortLabel: _requestPortLabel,
knownUsbNames: _knownUsbNames,
);
} catch (_) {
return usbRequestPortLabel;
return _requestPortLabel;
}
}