feat: enhance audio input handling and add message polling functionality

This commit is contained in:
Nedifinita
2025-12-05 18:12:20 +08:00
parent 99bc081583
commit 2b856c1a4a
7 changed files with 467 additions and 22 deletions

View File

@@ -21,6 +21,7 @@ class _ConnectionStatusWidget extends StatefulWidget {
final RtlTcpService rtlTcpService;
final DateTime? lastReceivedTime;
final DateTime? rtlTcpLastReceivedTime;
final DateTime? audioLastReceivedTime;
final InputSource inputSource;
final bool rtlTcpConnected;
@@ -29,6 +30,7 @@ class _ConnectionStatusWidget extends StatefulWidget {
required this.rtlTcpService,
required this.lastReceivedTime,
required this.rtlTcpLastReceivedTime,
required this.audioLastReceivedTime,
required this.inputSource,
required this.rtlTcpConnected,
});
@@ -92,7 +94,7 @@ class _ConnectionStatusWidgetState extends State<_ConnectionStatusWidget> {
isConnected = AudioInputService().isListening;
statusColor = isConnected ? Colors.green : Colors.red;
statusText = isConnected ? '监听中' : '已停止';
displayTime = widget.rtlTcpLastReceivedTime ?? widget.lastReceivedTime;
displayTime = widget.audioLastReceivedTime;
break;
case InputSource.bluetooth:
isConnected = _isConnected;
@@ -229,13 +231,17 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
StreamSubscription? _connectionSubscription;
StreamSubscription? _rtlTcpConnectionSubscription;
StreamSubscription? _audioConnectionSubscription;
StreamSubscription? _dataSubscription;
StreamSubscription? _rtlTcpDataSubscription;
StreamSubscription? _audioDataSubscription;
StreamSubscription? _lastReceivedTimeSubscription;
StreamSubscription? _rtlTcpLastReceivedTimeSubscription;
StreamSubscription? _audioLastReceivedTimeSubscription;
StreamSubscription? _settingsSubscription;
DateTime? _lastReceivedTime;
DateTime? _rtlTcpLastReceivedTime;
DateTime? _audioLastReceivedTime;
bool _isHistoryEditMode = false;
InputSource _inputSource = InputSource.bluetooth;
@@ -277,11 +283,13 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
final sourceStr = settings?['inputSource'] as String? ?? 'bluetooth';
if (mounted) {
final newSource = InputSource.values.firstWhere(
(e) => e.name == sourceStr,
orElse: () => InputSource.bluetooth,
);
setState(() {
_inputSource = InputSource.values.firstWhere(
(e) => e.name == sourceStr,
orElse: () => InputSource.bluetooth,
);
_inputSource = newSource;
_rtlTcpConnected = _rtlTcpService.isConnected;
});
@@ -324,6 +332,15 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
});
}
});
_audioLastReceivedTimeSubscription =
AudioInputService().lastReceivedTimeStream.listen((time) {
if (mounted) {
setState(() {
_audioLastReceivedTime = time;
});
}
});
}
void _setupSettingsListener() {
@@ -331,13 +348,10 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
DatabaseService.instance.onSettingsChanged((settings) {
if (mounted) {
final sourceStr = settings['inputSource'] as String? ?? 'bluetooth';
print('[MainScreen] Settings changed: inputSource=$sourceStr');
final newInputSource = InputSource.values.firstWhere(
(e) => e.name == sourceStr,
orElse: () => InputSource.bluetooth,
);
print('[MainScreen] Current: $_inputSource, New: $newInputSource');
setState(() {
_inputSource = newInputSource;
@@ -348,7 +362,6 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
setState(() {
_rtlTcpConnected = _rtlTcpService.isConnected;
});
print('[MainScreen] RTL-TCP mode, connected: $_rtlTcpConnected');
break;
case InputSource.audioInput:
setState(() {});
@@ -385,6 +398,12 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
});
}
});
_audioConnectionSubscription = AudioInputService().connectionStream.listen((listening) {
if (mounted) {
setState(() {});
}
});
}
Future<void> _connectToRtlTcp(String host, String port) async {
@@ -399,10 +418,13 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
void dispose() {
_connectionSubscription?.cancel();
_rtlTcpConnectionSubscription?.cancel();
_audioConnectionSubscription?.cancel();
_dataSubscription?.cancel();
_rtlTcpDataSubscription?.cancel();
_audioDataSubscription?.cancel();
_lastReceivedTimeSubscription?.cancel();
_rtlTcpLastReceivedTimeSubscription?.cancel();
_audioLastReceivedTimeSubscription?.cancel();
_settingsSubscription?.cancel();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
@@ -426,7 +448,13 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
});
_rtlTcpDataSubscription = _rtlTcpService.dataStream.listen((record) {
if (_inputSource != InputSource.bluetooth) {
if (_inputSource == InputSource.rtlTcp) {
_processRecord(record);
}
});
_audioDataSubscription = AudioInputService().dataStream.listen((record) {
if (_inputSource == InputSource.audioInput) {
_processRecord(record);
}
});
@@ -503,6 +531,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
rtlTcpService: _rtlTcpService,
lastReceivedTime: _lastReceivedTime,
rtlTcpLastReceivedTime: _rtlTcpLastReceivedTime,
audioLastReceivedTime: _audioLastReceivedTime,
inputSource: _inputSource,
rtlTcpConnected: _rtlTcpConnected,
),

View File

@@ -29,6 +29,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
late TextEditingController _rtlTcpHostController;
late TextEditingController _rtlTcpPortController;
bool _settingsLoaded = false;
String _deviceName = '';
bool _backgroundServiceEnabled = false;
bool _notificationsEnabled = true;
@@ -83,11 +85,15 @@ class _SettingsScreenState extends State<SettingsScreen> {
(e) => e.name == sourceStr,
orElse: () => InputSource.bluetooth,
);
_settingsLoaded = true;
});
}
}
Future<void> _saveSettings() async {
if (!_settingsLoaded) return;
await _databaseService.updateSettings({
'deviceName': _deviceName,
'backgroundServiceEnabled': _backgroundServiceEnabled ? 1 : 0,
@@ -667,7 +673,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
_buildActionButton(
icon: Icons.share,
title: '分享数据',
subtitle: '将记录分享为JSON文件',
subtitle: '将记录分享为 JSON 文件',
onTap: _shareData,
),
const SizedBox(height: 12),