import 'dart:async'; import 'package:audio_service/audio_service.dart'; import 'package:get/get.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'background_agent_service.dart'; import '../../data/providers/agent_provider.dart'; import '../../core/utils/logger.dart'; /// 自定义 AudioHandler,用于捕获媒体按键事件(例如蓝牙耳机按钮) class MyAudioHandler extends BaseAudioHandler { // 定义一个 StreamController 用于向 UI 发送自定义事件 final StreamController _customEventController = StreamController.broadcast(); bool _isDisposed = false; BackgroundAgentService? _backgroundAgent; // 添加一个标志,表示是否正在处理交互 bool _isHandlingInteraction = false; // 添加一个计时器,用于定期更新播放状态 Timer? _statusUpdateTimer; // 添加一个变量,记录最后一次媒体按钮事件的时间 DateTime? _lastMediaButtonEventTime; Stream get customEventStream => _customEventController.stream; MyAudioHandler() { _initializeHandler(); _initializeBackgroundAgent(); _startStatusUpdateTimer(); Logger.info('MyAudioHandler 已创建,准备接收媒体按钮事件'); } void _initializeBackgroundAgent() { try { if (Get.isRegistered()) { _backgroundAgent = Get.find(); Logger.info('BackgroundAgent 初始化成功'); } else { _backgroundAgent = Get.put(BackgroundAgentService(), permanent: true); Logger.info('BackgroundAgent 已创建并初始化成功'); } } catch (e) { Logger.error('初始化 BackgroundAgent 失败', e); _backgroundAgent = null; } } void _initializeHandler() { try { // 设置初始播放状态 playbackState.add(PlaybackState( controls: [ MediaControl.play, MediaControl.pause, MediaControl.stop, MediaControl.skipToPrevious, MediaControl.skipToNext, ], systemActions: { MediaAction.play, MediaAction.pause, MediaAction.stop, MediaAction.skipToPrevious, MediaAction.skipToNext, MediaAction.seek, }, androidCompactActionIndices: const [0, 1, 2], processingState: AudioProcessingState.idle, playing: false, )); // 设置媒体元数据 final item = MediaItem( id: 'deep_voice_audio', title: 'Deep Voice Audio', album: 'Deep Voice', artist: 'Deep Voice', genre: 'Speech', duration: const Duration(seconds: 1), artUri: null, // 移除图片URI playable: true, displayTitle: 'Deep Voice Audio', displaySubtitle: 'Voice Assistant', displayDescription: 'TTS Audio', extras: const {'source': 'deep_voice'}, ); mediaItem.add(item); Logger.info('AudioHandler 媒体项已设置'); // 立即更新播放状态,确保通知显示 _updatePlaybackState( playing: true, processingState: AudioProcessingState.ready, ); } catch (e) { Logger.error('初始化 AudioHandler 失败', e); } } // 启动定期更新播放状态的计时器 void _startStatusUpdateTimer() { _statusUpdateTimer?.cancel(); _statusUpdateTimer = Timer.periodic(Duration(minutes: 5), (timer) { try { // 定期更新播放状态,确保服务保持活跃 _updatePlaybackState( playing: true, processingState: AudioProcessingState.ready, ); Logger.info('定期更新播放状态成功'); } catch (e) { Logger.error('定期更新播放状态失败', e); } }); } // 更新播放状态 void _updatePlaybackState({ required bool playing, required AudioProcessingState processingState, }) { if (_isDisposed) return; try { playbackState.add(playbackState.value.copyWith( playing: playing, processingState: processingState, updatePosition: Duration.zero, )); } catch (e) { Logger.error('更新播放状态失败', e); } } Future _handleInteraction() async { // Logger.info('处理媒体按钮交互'); // 防止重复处理 if (_isHandlingInteraction) { Logger.info('已经在处理交互,忽略此次请求'); return; } _isHandlingInteraction = true; try { // 更新播放状态,确保通知显示 _updatePlaybackState( playing: true, processingState: AudioProcessingState.ready, ); // 直接获取 personal_assistant agent final agent = AgentProvider.getAgentById('personal_assistant'); if (agent == null) { Logger.error('无法获取personal_assistant agent,无法激活语音助手'); _isHandlingInteraction = false; return; } // 检查app是否在前台 final isInForeground = Get.context != null && WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed; if (isInForeground) { // 应用在前台,导航到聊天页面 Logger.info('应用在前台,导航到聊天页面'); // 如果BackgroundAgent正在运行,先停止它 if (_backgroundAgent != null && _backgroundAgent!.isProcessing.value) { await _backgroundAgent!.stopInteraction(); Logger.info('已停止后台代理服务,切换到前台模式'); } // 导航到聊天页面,并标记需要在进入时播放语音应答 Get.toNamed('/chat', arguments: { 'agentId': agent.id, 'agentName': agent.name, 'agentVoice': agent.voice, 'agentAvatar': agent.avatarUrl, 'agentSubtitle': agent.description, 'systemPrompt': agent.systemPrompt, 'welcomeMessage': agent.welcomeMessage, 'playVoiceOnEnter': true, }); } else { // 应用在后台,使用 BackgroundAgent 处理交互 Logger.info('应用在后台,使用 BackgroundAgent 处理交互'); if (_backgroundAgent != null) { if (_backgroundAgent!.isProcessing.value) { // 如果后台服务已在处理,则中断当前响应并回复"我在听" Logger.info('检测到媒体按钮再次按下,中断当前AI回复'); await _backgroundAgent!.respondToNewMediaEvent(); } else { // 首次启动后台服务 Logger.info('首次启动后台服务'); await _backgroundAgent!.startAgentInteraction(agent.id); } } else { Logger.error('BackgroundAgent 未初始化,无法处理交互'); // 尝试重新初始化 BackgroundAgent _initializeBackgroundAgent(); } } } catch (e) { Logger.error('处理媒体按钮交互失败', e); } finally { _isHandlingInteraction = false; } } @override Future play() async { Logger.info('收到播放命令'); try { _updatePlaybackState( playing: true, processingState: AudioProcessingState.ready, ); await _handleInteraction(); } catch (e) { Logger.error('处理播放命令失败', e); } } @override Future pause() async { Logger.info('收到暂停命令'); try { _updatePlaybackState( playing: false, processingState: AudioProcessingState.ready, ); await _handleInteraction(); } catch (e) { Logger.error('处理暂停命令失败', e); } } @override Future stop() async { Logger.info('停止 AudioHandler'); _isDisposed = true; try { _statusUpdateTimer?.cancel(); // 如果有正在运行的后台服务,停止它 if (_backgroundAgent != null && _backgroundAgent!.isProcessing.value) { await _backgroundAgent!.stopInteraction(); } await super.stop(); _customEventController.close(); _updatePlaybackState( playing: false, processingState: AudioProcessingState.idle, ); } catch (e) { Logger.error('停止 AudioHandler 时发生错误', e); } } @override Future skipToPrevious() async { Logger.info('收到上一曲命令'); try { _updatePlaybackState( playing: true, processingState: AudioProcessingState.ready, ); await _handleInteraction(); } catch (e) { Logger.error('处理上一曲命令失败', e); } } @override Future skipToNext() async { Logger.info('收到下一曲命令'); try { _updatePlaybackState( playing: true, processingState: AudioProcessingState.ready, ); await _handleInteraction(); } catch (e) { Logger.error('处理下一曲命令失败', e); } } @override Future seek(Duration position) async { Logger.info('收到跳转命令: $position'); } @override Future customAction(String name, [Map? extras]) async { Logger.info('收到自定义命令: $name'); try { if (name == 'media_button') { Logger.info('收到媒体按钮事件'); // 检查是否是重复触发(防止短时间内多次触发) final now = DateTime.now(); if (_lastMediaButtonEventTime != null) { final difference = now.difference(_lastMediaButtonEventTime!); if (difference.inMilliseconds < 500) { Logger.info('媒体按钮事件触发过于频繁,忽略此次事件'); return; } } // 更新最后一次事件时间 _lastMediaButtonEventTime = now; _updatePlaybackState( playing: true, processingState: AudioProcessingState.ready, ); await _handleInteraction(); } else if (name == 'ping') { // 用于检查服务是否响应的测试命令 Logger.info('收到 ping 命令,服务正常运行'); return 'pong'; } } catch (e) { Logger.error('处理自定义命令失败', e); } } @override Future onTaskRemoved() async { Logger.info('服务被系统移除'); try { // 如果有正在运行的后台服务,停止它 if (_backgroundAgent != null && _backgroundAgent!.isProcessing.value) { await _backgroundAgent!.stopInteraction(); } await stop(); } catch (e) { Logger.error('处理服务移除失败', e); } } @override Future onNotificationDeleted() async { Logger.info('通知被用户移除'); try { // 如果有正在运行的后台服务,停止它 if (_backgroundAgent != null && _backgroundAgent!.isProcessing.value) { await _backgroundAgent!.stopInteraction(); } await stop(); } catch (e) { Logger.error('处理通知移除失败', e); } } }