diff --git a/lib/core/bindings/initial_binding.dart b/lib/core/bindings/initial_binding.dart index c3ee74f7b..01564b26a 100644 --- a/lib/core/bindings/initial_binding.dart +++ b/lib/core/bindings/initial_binding.dart @@ -10,6 +10,8 @@ import '../../data/services/bluetooth_manager.dart'; import '../../data/services/ble_manager.dart'; import '../../data/services/account_service.dart'; import '../../data/services/version_update_service.dart'; +import '../../modules/music/controllers/music_controller.dart'; +import '../../modules/music/services/music_service_facade.dart'; /// 初始绑定,用于管理全局依赖 class InitialBinding extends Bindings { @@ -35,6 +37,10 @@ class InitialBinding extends Bindings { Get.lazyPut(() => BleManager(), fenix: true); // MusicManager Get.lazyPut(() => MusicManager(), fenix: true); + + // 音乐模块 + Get.lazyPut(() => MusicController(), fenix: true); + Get.lazyPut(() => MusicServiceFacade(), fenix: true); Get.lazyPut(() => NavigationManager(), fenix: true); Get.lazyPut(() => LocationManager(), fenix: true); // 火山AI服务 diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 65288449d..d1ae83ce3 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -40,20 +40,8 @@ class AgentController extends GetxController { final TextEditingController textController = TextEditingController(); final ScrollController scrollController = ScrollController(); - //音乐播放管理 - final _musiceManager = Get.find(); + // 导航管理 final _navigationManager = Get.find(); - var isPlaying = false.obs; - var currentId = 0.obs; - - // 播放进度相关状态 - var currentPosition = 0.obs; // 当前播放位置(毫秒) - var duration = 0.obs; // 总时长(毫秒) - var progress = 0.0.obs; // 播放进度(0.0-1.0) - - // 当前播放列表管理 - final currentPlaylist = >[].obs; // 当前播放列表 - final currentPlaylistIndex = 0.obs; // 当前播放歌曲在列表中的索引 // 各种状态标志 final isInitialized = false.obs; @@ -110,9 +98,6 @@ class AgentController extends GetxController { // BLE事件订阅 StreamSubscription? _bleEventSubscription; - // 播放进度更新定时器 - Timer? _progressTimer; - // 用于跟踪AI回复状态 bool _isNewAssistantResponse = true; String _currentResponseId = ''; @@ -125,16 +110,6 @@ class AgentController extends GetxController { // 是否已加载聊天记录 final isHistoryLoaded = false.obs; - // 添加加载状态跟踪 - final RxBool isLoadingMusic = false.obs; - final RxString loadingMusicId = ''.obs; - - // 设置加载状态的方法 - void setLoadingMusic(String id, bool loading) { - loadingMusicId.value = loading ? id : ''; - isLoadingMusic.value = loading; - } - @override void onInit() { super.onInit(); @@ -142,37 +117,10 @@ class AgentController extends GetxController { _loadChatHistory(); //加载聊天历史记录 _subscribeToEvents(); //订阅代理服务事件 _subscribeToBleEvents(); //订阅BLE事件 - _initMusicStatus(); //初始化音乐状态 - _setupMediaSessionCallbacks(); //设置媒体会话回调 _setupTextInputListener(); //设置文本输入监听器 } - // 初始化音乐播放状态 - Future _initMusicStatus() async { - try { - await musicStatus(); - Logger.d('AgentController', '音乐状态初始化完成'); - } catch (e) { - Logger.e('AgentController', '音乐状态初始化失败: $e'); - } - } - // 设置媒体会话回调 - void _setupMediaSessionCallbacks() async { - try { - await _musiceManager.setMediaSessionCallbacks( - onPlay: onMediaSessionPlay, - onPause: onMediaSessionPause, - onNext: onMediaSessionNext, - onPrevious: onMediaSessionPrevious, - onSeekTo: onMediaSessionSeekTo, - onStop: onMediaSessionStop, - ); - Logger.d('AgentController', '媒体会话回调设置完成'); - } catch (e) { - Logger.e('AgentController', '媒体会话回调设置失败: $e'); - } - } @override void onClose() { @@ -184,7 +132,6 @@ class AgentController extends GetxController { _eventSubscription?.cancel(); _amapeventSubscription?.cancel(); _bleEventSubscription?.cancel(); - _progressTimer?.cancel(); super.onClose(); } @@ -1321,241 +1268,12 @@ class AgentController extends GetxController { await stopVoiceInput(); } - // 获取音乐播放状态 - Future musicStatus() async { - try { - var status = await _musiceManager.getStatus(); - isPlaying.value = status['isPlaying'] ?? false; - currentId.value = status['id'] ?? 0; - Logger.d('AgentController', - '音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}'); - } catch (e) { - Logger.e('AgentController', '获取音乐状态失败: $e'); - } - } - - // 播放音乐 - Future musicPlay(Map song) async { - try { - Logger.d('AgentController', '开始播放音乐: ${song['title'] ?? song['name']}'); - - // 立即更新UI状态,提供即时反馈 - isPlaying.value = true; - // 修复:确保currentId接收int类型 - final songId = song['id']; - currentId.value = songId is int - ? songId - : (int.tryParse(songId?.toString() ?? '0') ?? 0); - - // 重置进度状态 - currentPosition.value = 0; - duration.value = 0; - progress.value = 0.0; - - final id = song['id'] ?? ""; - final timestampMs = song['ts'] ?? 0; - - // 检查URL是否需要刷新(只有当有时间戳时才检查) - if (timestampMs > 0) { - final now = DateTime.now(); - final tsTime = DateTime.fromMillisecondsSinceEpoch(timestampMs).add( - const Duration(minutes: 10), // 转换为北京时间 - ); - - // 判断是否过期 - if (tsTime.isBefore(now)) { - Logger.d('AgentController', '音乐URL已过期,正在获取新URL...'); - try { - var response = await Api.getMusicUrl({ - "ids": [id] - }); - print('getMusicUrl 响应数据: ${response.toString()}'); // 调试输出原始数据 - final urldata = response['urls'][0] as Map; - song["url"] = urldata['url'] ?? ''; - // 修复:确保currentId接收int类型 - final newSongId = song['id']; - currentId.value = newSongId is int - ? newSongId - : (int.tryParse(newSongId?.toString() ?? '0') ?? 0); - Logger.d('AgentController', '获取新URL成功'); - } catch (e) { - Logger.e('AgentController', '获取新URL失败: $e'); - // 如果获取新URL失败,尝试使用原URL - } - } - } - // 转换为MusicManager期望的格式 - final musicData = { - 'id': song['id']?.toString() ?? '', - 'url': song['url']?.toString() ?? '', - 'title': song['title']?.toString() ?? song['name']?.toString() ?? '', - 'artist': - song['artist']?.toString() ?? song['sgener']?.toString() ?? '', - 'coverUrl': - song['coverUrl']?.toString() ?? song['image']?.toString() ?? '', - }; - // 调用音乐管理器播放 - await _musiceManager.play(musicData); - // 更新媒体会话元数据和播放状态 - await _updateMediaSessionMetadata(song); - await _updateMediaSessionPlaybackState(true); - // 启动进度定时器 - _startProgressTimer(); - Logger.d('AgentController', '音乐播放启动成功'); - } catch (e) { - Logger.e('AgentController', '播放音乐失败: $e'); - // 发生错误时重置状态 - isPlaying.value = false; - currentId.value = 0; - } - } - - // 暂停音乐 - Future musicPause() async { - try { - Logger.d('AgentController', '暂停音乐播放'); - await _musiceManager.pause(); - isPlaying.value = false; - _stopProgressTimer(); - // 更新媒体会话播放状态 - await _updateMediaSessionPlaybackState(false); - } catch (e) { - Logger.e('AgentController', '暂停音乐失败: $e'); - } - } - - // 恢复音乐播放 - Future musicResume() async { - try { - Logger.d('AgentController', '恢复播放音乐'); - await _musiceManager.resume(); - isPlaying.value = true; - _startProgressTimer(); - - // 更新媒体会话播放状态 - await _updateMediaSessionPlaybackState(true); - - Logger.d('AgentController', '音乐恢复播放成功'); - } catch (e) { - Logger.e('AgentController', '恢复播放音乐失败: $e'); - isPlaying.value = false; - } - } - - // 启动进度更新定时器 - void _startProgressTimer() { - _stopProgressTimer(); // 先停止现有定时器 - - Logger.d('AgentController', '启动播放进度定时器'); - _progressTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) { - if (isPlaying.value && currentId.value != 0) { - updateMusicProgress(); - } else if (!isPlaying.value) { - // 如果不在播放状态,停止定时器 - timer.cancel(); - _progressTimer = null; - Logger.d('AgentController', '播放已停止,取消进度定时器'); - } - }); - } - - // 停止进度更新定时器 - void _stopProgressTimer() { - if (_progressTimer != null) { - Logger.d('AgentController', '停止播放进度定时器'); - _progressTimer?.cancel(); - _progressTimer = null; - } - } - - // 获取播放进度 - Future updateMusicProgress() async { - try { - if (!isPlaying.value || currentId.value == 0) { - return; // 如果没有在播放,直接返回 - } - - var progressInfo = await _musiceManager.getProgress(); - final newPosition = progressInfo['currentPosition'] ?? 0; - final newDuration = progressInfo['duration'] ?? 0; - final newProgress = progressInfo['progress'] ?? 0.0; - - bool hasUpdate = false; - - // 只有当值发生变化时才更新,避免不必要的UI刷新 - if (currentPosition.value != newPosition) { - currentPosition.value = newPosition; - hasUpdate = true; - } - if (duration.value != newDuration) { - duration.value = newDuration; - hasUpdate = true; - } - if ((progress.value - newProgress).abs() > 0.001) { - // 避免浮点数精度问题 - progress.value = newProgress; - hasUpdate = true; - } - - // 如果有进度更新,通知系统媒体会话更新进度信息 - if (hasUpdate) { - await _updateMediaSessionProgress(); - } - } catch (e) { - Logger.e('AgentController', '获取播放进度失败: $e'); - } - } - - /// 更新系统媒体会话的进度信息 - Future _updateMediaSessionProgress() async { - try { - // 这里将调用music_service插件的方法来更新系统媒体会话进度 - // 具体实现将在music_service插件中添加 - await _musiceManager.updateMediaSessionProgress( - currentPosition.value, - duration.value, - progress.value, - ); - } catch (e) { - // 忽略错误,避免影响正常播放 - Logger.d('AgentController', '更新媒体会话进度失败: $e'); - } - } - - /// 更新系统媒体会话的元数据 - Future _updateMediaSessionMetadata(Map song) async { - try { - await _musiceManager.updateMediaSessionMetadata(song); - } catch (e) { - Logger.d('AgentController', '更新媒体会话元数据失败: $e'); - } - } - - /// 更新系统媒体会话的播放状态 - Future _updateMediaSessionPlaybackState(bool isPlaying) async { - try { - await _musiceManager.updateMediaSessionPlaybackState(isPlaying); - } catch (e) { - Logger.d('AgentController', '更新媒体会话播放状态失败: $e'); - } - } - - // 跳转到指定位置 - Future seekToPosition(int positionMs) async { - try { - await _musiceManager.seekTo(positionMs); - // 立即更新进度状态 - await updateMusicProgress(); - } catch (e) { - Logger.e('AgentController', '跳转播放位置失败: $e'); - } - } // 修改自动停止处理,遵循TTS设置 void _handleAutoStop(int index) { @@ -1595,179 +1313,7 @@ class AgentController extends GetxController { ); } - /// 播放音乐列表 - Future playPlaylist(List> playlist) async { - try { - if (playlist.isEmpty) { - Logger.e(TAG, '播放列表为空,无法播放'); - return; - } - - Logger.d(TAG, '开始播放音乐列表,共${playlist.length}首歌曲'); - - // 保存当前播放列表 - currentPlaylist.assignAll(playlist); - currentPlaylistIndex.value = 0; - - // 设置加载状态 - isLoadingMusic.value = true; - loadingMusicId.value = playlist[0]['id']?.toString() ?? ''; - - // 重置播放状态 - isPlaying.value = true; - currentPosition.value = 0; - duration.value = 0; - progress.value = 0.0; - - // 格式化歌曲列表数据 - final formattedPlaylist = playlist.map((song) { - return { - 'id': song['id']?.toString() ?? '', - 'url': song['url']?.toString() ?? '', - 'title': song['title']?.toString() ?? song['name']?.toString() ?? '', - 'artist': - song['artist']?.toString() ?? song['sgener']?.toString() ?? '', - 'coverUrl': - song['coverUrl']?.toString() ?? song['image']?.toString() ?? '', - }; - }).toList(); - - // 调用音乐管理器播放列表 - await _musiceManager.playPlaylist(formattedPlaylist); - - // 更新当前播放歌曲ID - if (formattedPlaylist.isNotEmpty) { - currentId.value = int.tryParse(formattedPlaylist[0]['id'] ?? '0') ?? 0; - } - - // 启动进度定时器 - _startProgressTimer(); - - Logger.d(TAG, '音乐列表播放启动成功'); - } catch (e) { - Logger.e(TAG, '播放音乐列表失败: $e'); - isPlaying.value = false; - currentId.value = 0; - - // 显示错误提示 - Get.snackbar( - '播放失败', - '无法播放音乐列表,请稍后重试', - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.red[400], - colorText: Colors.white, - duration: const Duration(seconds: 2), - ); - } finally { - // 清除加载状态 - isLoadingMusic.value = false; - loadingMusicId.value = ''; - } - } - - /// 播放上一曲 - Future playPrevious() async { - if (currentPlaylist.isEmpty) { - Logger.w(TAG, '当前播放列表为空,无法播放上一曲'); - return; - } - - // 计算上一曲的索引 - int previousIndex = currentPlaylistIndex.value - 1; - if (previousIndex < 0) { - previousIndex = currentPlaylist.length - 1; // 循环到最后一首 - } - - currentPlaylistIndex.value = previousIndex; - final previousSong = currentPlaylist[previousIndex]; - - Logger.d(TAG, '播放上一曲: ${previousSong['name'] ?? previousSong['title']}'); - await musicPlay(previousSong); - } - - /// 播放下一曲 - Future playNext() async { - if (currentPlaylist.isEmpty) { - Logger.w(TAG, '当前播放列表为空,无法播放下一曲'); - return; - } - // 计算下一曲的索引 - int nextIndex = currentPlaylistIndex.value + 1; - if (nextIndex >= currentPlaylist.length) { - nextIndex = 0; // 循环到第一首 - } - currentPlaylistIndex.value = nextIndex; - final nextSong = currentPlaylist[nextIndex]; - Logger.d(TAG, '播放下一曲: ${nextSong['name'] ?? nextSong['title']}'); - await musicPlay(nextSong); - } - - /// 更新当前播放列表和索引(当单独播放某首歌曲时调用) - void updateCurrentPlaylistContext( - List> playlist, int songId) { - currentPlaylist.assignAll(playlist); - - // 查找当前歌曲在列表中的索引 - int index = -1; - for (int i = 0; i < playlist.length; i++) { - if (int.tryParse(playlist[i]['id']?.toString() ?? '0') == songId) { - index = i; - break; - } - } - - if (index != -1) { - currentPlaylistIndex.value = index; - Logger.d(TAG, '更新播放列表上下文,当前歌曲索引: $index'); - } - } - - // ========== 媒体会话回调处理方法 ========== - - /// 处理来自系统通知栏的播放操作 - Future onMediaSessionPlay() async { - Logger.d(TAG, '收到媒体会话播放回调'); - if (!isPlaying.value) { - await musicResume(); - } - } - - /// 处理来自系统通知栏的暂停操作 - Future onMediaSessionPause() async { - Logger.d(TAG, '收到媒体会话暂停回调'); - if (isPlaying.value) { - await musicPause(); - } - } - - /// 处理来自系统通知栏的下一曲操作 - Future onMediaSessionNext() async { - Logger.d(TAG, '收到媒体会话下一曲回调'); - await playNext(); - } - - /// 处理来自系统通知栏的上一曲操作 - Future onMediaSessionPrevious() async { - Logger.d(TAG, '收到媒体会话上一曲回调'); - await playPrevious(); - } - - /// 处理来自系统通知栏的跳转操作 - Future onMediaSessionSeekTo(int positionMs) async { - Logger.d(TAG, '收到媒体会话跳转回调: ${positionMs}ms'); - await seekToPosition(positionMs); - } - - /// 处理来自系统通知栏的停止操作 - Future onMediaSessionStop() async { - Logger.d(TAG, '收到媒体会话停止回调'); - await musicPause(); - // 重置播放状态 - currentPosition.value = 0; - progress.value = 0.0; - _stopProgressTimer(); - } } diff --git a/lib/modules/agent/views/music_card.dart b/lib/modules/agent/views/music_card.dart index f72af95be..f64564c1d 100644 --- a/lib/modules/agent/views/music_card.dart +++ b/lib/modules/agent/views/music_card.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../controllers/agent_controller.dart'; +import '../../music/services/music_service_facade.dart'; import 'circular_progress_button.dart'; /// 构建音乐项目 Widget buildMusicItem(BuildContext context, int id, String name, String url, String singer, String image) { - final controller = Get.find(); // 获取控制器 + final musicFacade = Get.find(); // 获取音乐服务门面 final isDarkMode = Theme.of(context).brightness == Brightness.dark; Map song = { @@ -16,7 +16,7 @@ Widget buildMusicItem(BuildContext context, int id, String name, String url, 'artist': singer, 'coverUrl': image, }; - controller.musicStatus(); //刷新状态 + // 音乐状态由MusicController自动管理,无需手动刷新 return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( @@ -82,11 +82,11 @@ Widget buildMusicItem(BuildContext context, int id, String name, String url, ), Obx(() { final isThisPlaying = - controller.isPlaying.value && controller.currentId.value == id; + musicFacade.isPlaying.value && musicFacade.currentId.value == id; // 只有当前播放的歌曲才显示进度 final currentProgress = - isThisPlaying ? controller.progress.value : 0.0; + isThisPlaying ? musicFacade.progress.value : 0.0; return CircularProgressButton( isPlaying: isThisPlaying, @@ -97,21 +97,21 @@ Widget buildMusicItem(BuildContext context, int id, String name, String url, strokeWidth: 2.0, onPlayPause: () async { try { - if (controller.isPlaying.value) { - if (controller.currentId.value == id) { + if (musicFacade.isPlaying.value) { + if (musicFacade.currentId.value == id) { // 当前歌曲正在播放,暂停它 - await controller.musicPause(); + await musicFacade.musicPause(); } else { // 其他歌曲正在播放,播放这首歌 - await controller.musicPlay(song); + await musicFacade.musicPlay(song); } } else { - if (controller.currentId.value == id) { + if (musicFacade.currentId.value == id) { // 当前歌曲已暂停,恢复播放 - await controller.musicResume(); + await musicFacade.musicResume(); } else { // 播放新歌曲 - await controller.musicPlay(song); + await musicFacade.musicPlay(song); } } } catch (e) { @@ -122,8 +122,8 @@ Widget buildMusicItem(BuildContext context, int id, String name, String url, ? (seekProgress) { // 只有当前播放的歌曲才允许跳转 final targetPosition = - (controller.duration.value * seekProgress).round(); - controller.seekToPosition(targetPosition); + (musicFacade.duration.value * seekProgress).round(); + musicFacade.seekToPosition(targetPosition); } : null, ); diff --git a/lib/modules/music/bindings/music_binding.dart b/lib/modules/music/bindings/music_binding.dart new file mode 100644 index 000000000..231934cfa --- /dev/null +++ b/lib/modules/music/bindings/music_binding.dart @@ -0,0 +1,15 @@ +import 'package:get/get.dart'; +import '../controllers/music_controller.dart'; + +/// 音乐模块绑定 +/// 负责注册音乐相关的控制器和服务 +class MusicBinding extends Bindings { + @override + void dependencies() { + // 注册音乐控制器为单例 + Get.lazyPut( + () => MusicController(), + fenix: true, // 允许重新创建 + ); + } +} \ No newline at end of file diff --git a/lib/modules/music/controllers/music_controller.dart b/lib/modules/music/controllers/music_controller.dart new file mode 100644 index 000000000..a092d69f7 --- /dev/null +++ b/lib/modules/music/controllers/music_controller.dart @@ -0,0 +1,493 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../core/utils/logger.dart'; +import '../../../data/services/music_manager.dart'; +import '../../../data/services/network/api.dart'; + +/// 音乐播放控制器 +/// 负责管理音乐播放状态、播放列表和播放控制 +class MusicController extends GetxController { + static const String TAG = 'MusicController'; + + // 音乐播放管理器 + final _musicManager = Get.find(); + + // 播放状态 + final isPlaying = false.obs; + final currentId = 0.obs; + + // 播放进度相关状态 + final currentPosition = 0.obs; // 当前播放位置(毫秒) + final duration = 0.obs; // 总时长(毫秒) + final progress = 0.0.obs; // 播放进度(0.0-1.0) + + // 当前播放列表管理 + final currentPlaylist = >[].obs; // 当前播放列表 + final currentPlaylistIndex = 0.obs; // 当前播放歌曲在列表中的索引 + + // 加载状态跟踪 + final isLoadingMusic = false.obs; + final loadingMusicId = ''.obs; + + // 进度更新定时器 + Timer? _progressTimer; + + @override + void onInit() { + super.onInit(); + _initMusicStatus(); + _setupMediaSessionCallbacks(); + } + + @override + void onClose() { + _stopProgressTimer(); + super.onClose(); + } + + // 初始化音乐播放状态 + Future _initMusicStatus() async { + try { + await musicStatus(); + Logger.d(TAG, '音乐状态初始化完成'); + } catch (e) { + Logger.e(TAG, '音乐状态初始化失败: $e'); + } + } + + // 设置媒体会话回调 + void _setupMediaSessionCallbacks() async { + try { + await _musicManager.setMediaSessionCallbacks( + onPlay: onMediaSessionPlay, + onPause: onMediaSessionPause, + onNext: onMediaSessionNext, + onPrevious: onMediaSessionPrevious, + onSeekTo: onMediaSessionSeekTo, + onStop: onMediaSessionStop, + ); + Logger.d(TAG, '媒体会话回调设置完成'); + } catch (e) { + Logger.e(TAG, '媒体会话回调设置失败: $e'); + } + } + + // 设置加载状态的方法 + void setLoadingMusic(String id, bool loading) { + loadingMusicId.value = loading ? id : ''; + isLoadingMusic.value = loading; + } + + // 获取音乐播放状态 + Future musicStatus() async { + try { + var status = await _musicManager.getStatus(); + isPlaying.value = status['isPlaying'] ?? false; + currentId.value = status['id'] ?? 0; + Logger.d(TAG, '音乐状态更新: playing=${isPlaying.value}, id=${currentId.value}'); + } catch (e) { + Logger.e(TAG, '获取音乐状态失败: $e'); + } + } + + // 播放音乐 + Future musicPlay(Map song) async { + try { + Logger.d(TAG, '开始播放音乐: ${song['title'] ?? song['name']}'); + + // 立即更新UI状态,提供即时反馈 + isPlaying.value = true; + // 修复:确保currentId接收int类型 + final songId = song['id']; + currentId.value = songId is int + ? songId + : (int.tryParse(songId?.toString() ?? '0') ?? 0); + + // 重置进度状态 + currentPosition.value = 0; + duration.value = 0; + progress.value = 0.0; + + final id = song['id'] ?? ""; + final timestampMs = song['ts'] ?? 0; + + // 检查URL是否需要刷新(只有当有时间戳时才检查) + if (timestampMs > 0) { + final now = DateTime.now(); + final tsTime = DateTime.fromMillisecondsSinceEpoch(timestampMs).add( + const Duration(minutes: 10), // 转换为北京时间 + ); + + // 判断是否过期 + if (tsTime.isBefore(now)) { + Logger.d(TAG, '音乐URL已过期,正在获取新URL...'); + try { + var response = await Api.getMusicUrl({ + "ids": [id] + }); + print('getMusicUrl 响应数据: ${response.toString()}'); // 调试输出原始数据 + final urldata = response['urls'][0] as Map; + song["url"] = urldata['url'] ?? ''; + // 修复:确保currentId接收int类型 + final newSongId = song['id']; + currentId.value = newSongId is int + ? newSongId + : (int.tryParse(newSongId?.toString() ?? '0') ?? 0); + Logger.d(TAG, '获取新URL成功'); + } catch (e) { + Logger.e(TAG, '获取新URL失败: $e'); + // 如果获取新URL失败,尝试使用原URL + } + } + } + + // 转换为MusicManager期望的格式 + final musicData = { + 'id': song['id']?.toString() ?? '', + 'url': song['url']?.toString() ?? '', + 'title': song['title']?.toString() ?? song['name']?.toString() ?? '', + 'artist': + song['artist']?.toString() ?? song['sgener']?.toString() ?? '', + 'coverUrl': + song['coverUrl']?.toString() ?? song['image']?.toString() ?? '', + }; + + // 调用音乐管理器播放 + await _musicManager.play(musicData); + + // 更新媒体会话元数据和播放状态 + await _updateMediaSessionMetadata(song); + await _updateMediaSessionPlaybackState(true); + + // 启动进度定时器 + _startProgressTimer(); + + Logger.d(TAG, '音乐播放启动成功'); + } catch (e) { + Logger.e(TAG, '播放音乐失败: $e'); + // 发生错误时重置状态 + isPlaying.value = false; + currentId.value = 0; + } + } + + // 暂停音乐 + Future musicPause() async { + try { + Logger.d(TAG, '暂停音乐播放'); + await _musicManager.pause(); + isPlaying.value = false; + _stopProgressTimer(); + + // 更新媒体会话播放状态 + await _updateMediaSessionPlaybackState(false); + } catch (e) { + Logger.e(TAG, '暂停音乐失败: $e'); + } + } + + // 恢复音乐播放 + Future musicResume() async { + try { + Logger.d(TAG, '恢复播放音乐'); + await _musicManager.resume(); + isPlaying.value = true; + _startProgressTimer(); + + // 更新媒体会话播放状态 + await _updateMediaSessionPlaybackState(true); + + Logger.d(TAG, '音乐恢复播放成功'); + } catch (e) { + Logger.e(TAG, '恢复播放音乐失败: $e'); + isPlaying.value = false; + } + } + + // 启动进度更新定时器 + void _startProgressTimer() { + _stopProgressTimer(); // 先停止现有定时器 + + Logger.d(TAG, '启动播放进度定时器'); + _progressTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) { + if (isPlaying.value && currentId.value != 0) { + updateMusicProgress(); + } else if (!isPlaying.value) { + // 如果不在播放状态,停止定时器 + timer.cancel(); + _progressTimer = null; + Logger.d(TAG, '播放已停止,取消进度定时器'); + } + }); + } + + // 停止进度更新定时器 + void _stopProgressTimer() { + if (_progressTimer != null) { + Logger.d(TAG, '停止播放进度定时器'); + _progressTimer?.cancel(); + _progressTimer = null; + } + } + + // 获取播放进度 + Future updateMusicProgress() async { + try { + if (!isPlaying.value || currentId.value == 0) { + return; // 如果没有在播放,直接返回 + } + + var progressInfo = await _musicManager.getProgress(); + final newPosition = progressInfo['currentPosition'] ?? 0; + final newDuration = progressInfo['duration'] ?? 0; + final newProgress = progressInfo['progress'] ?? 0.0; + + bool hasUpdate = false; + + // 只有当值发生变化时才更新,避免不必要的UI刷新 + if (currentPosition.value != newPosition) { + currentPosition.value = newPosition; + hasUpdate = true; + } + if (duration.value != newDuration) { + duration.value = newDuration; + hasUpdate = true; + } + if ((progress.value - newProgress).abs() > 0.001) { + // 避免浮点数精度问题 + progress.value = newProgress; + hasUpdate = true; + } + + // 如果有进度更新,通知系统媒体会话更新进度信息 + if (hasUpdate) { + await _updateMediaSessionProgress(); + } + } catch (e) { + Logger.e(TAG, '获取播放进度失败: $e'); + } + } + + // 跳转到指定位置 + Future seekToPosition(int positionMs) async { + try { + await _musicManager.seekTo(positionMs); + // 立即更新进度状态 + await updateMusicProgress(); + } catch (e) { + Logger.e(TAG, '跳转播放位置失败: $e'); + } + } + + /// 播放音乐列表 + Future playPlaylist(List> playlist) async { + try { + if (playlist.isEmpty) { + Logger.e(TAG, '播放列表为空,无法播放'); + return; + } + + Logger.d(TAG, '开始播放音乐列表,共${playlist.length}首歌曲'); + + // 保存当前播放列表 + currentPlaylist.assignAll(playlist); + currentPlaylistIndex.value = 0; + + // 设置加载状态 + isLoadingMusic.value = true; + loadingMusicId.value = playlist[0]['id']?.toString() ?? ''; + + // 重置播放状态 + isPlaying.value = true; + currentPosition.value = 0; + duration.value = 0; + progress.value = 0.0; + + // 格式化歌曲列表数据 + final formattedPlaylist = playlist.map((song) { + return { + 'id': song['id']?.toString() ?? '', + 'url': song['url']?.toString() ?? '', + 'title': song['title']?.toString() ?? song['name']?.toString() ?? '', + 'artist': + song['artist']?.toString() ?? song['sgener']?.toString() ?? '', + 'coverUrl': + song['coverUrl']?.toString() ?? song['image']?.toString() ?? '', + }; + }).toList(); + + // 调用音乐管理器播放列表 + await _musicManager.playPlaylist(formattedPlaylist); + + // 更新当前播放歌曲ID + if (formattedPlaylist.isNotEmpty) { + currentId.value = int.tryParse(formattedPlaylist[0]['id'] ?? '0') ?? 0; + } + + // 启动进度定时器 + _startProgressTimer(); + + Logger.d(TAG, '音乐列表播放启动成功'); + } catch (e) { + Logger.e(TAG, '播放音乐列表失败: $e'); + isPlaying.value = false; + currentId.value = 0; + + // 显示错误提示 + Get.snackbar( + '播放失败', + '无法播放音乐列表,请稍后重试', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red[400], + colorText: Colors.white, + duration: const Duration(seconds: 2), + ); + } finally { + // 清除加载状态 + isLoadingMusic.value = false; + loadingMusicId.value = ''; + } + } + + /// 播放上一曲 + Future playPrevious() async { + if (currentPlaylist.isEmpty) { + Logger.w(TAG, '当前播放列表为空,无法播放上一曲'); + return; + } + + // 计算上一曲的索引 + int previousIndex = currentPlaylistIndex.value - 1; + if (previousIndex < 0) { + previousIndex = currentPlaylist.length - 1; // 循环到最后一首 + } + + currentPlaylistIndex.value = previousIndex; + final previousSong = currentPlaylist[previousIndex]; + + Logger.d(TAG, '播放上一曲: ${previousSong['name'] ?? previousSong['title']}'); + await musicPlay(previousSong); + } + + /// 播放下一曲 + Future playNext() async { + if (currentPlaylist.isEmpty) { + Logger.w(TAG, '当前播放列表为空,无法播放下一曲'); + return; + } + + // 计算下一曲的索引 + int nextIndex = currentPlaylistIndex.value + 1; + if (nextIndex >= currentPlaylist.length) { + nextIndex = 0; // 循环到第一首 + } + + currentPlaylistIndex.value = nextIndex; + final nextSong = currentPlaylist[nextIndex]; + + Logger.d(TAG, '播放下一曲: ${nextSong['name'] ?? nextSong['title']}'); + await musicPlay(nextSong); + } + + /// 更新当前播放列表和索引(当单独播放某首歌曲时调用) + void updateCurrentPlaylistContext( + List> playlist, int songId) { + currentPlaylist.assignAll(playlist); + + // 查找当前歌曲在列表中的索引 + int index = -1; + for (int i = 0; i < playlist.length; i++) { + if (int.tryParse(playlist[i]['id']?.toString() ?? '0') == songId) { + index = i; + break; + } + } + + if (index != -1) { + currentPlaylistIndex.value = index; + Logger.d(TAG, '更新播放列表上下文,当前歌曲索引: $index'); + } + } + + // ========== 媒体会话回调处理方法 ========== + + /// 处理来自系统通知栏的播放操作 + Future onMediaSessionPlay() async { + Logger.d(TAG, '收到媒体会话播放回调'); + if (!isPlaying.value) { + await musicResume(); + } + } + + /// 处理来自系统通知栏的暂停操作 + Future onMediaSessionPause() async { + Logger.d(TAG, '收到媒体会话暂停回调'); + if (isPlaying.value) { + await musicPause(); + } + } + + /// 处理来自系统通知栏的下一曲操作 + Future onMediaSessionNext() async { + Logger.d(TAG, '收到媒体会话下一曲回调'); + await playNext(); + } + + /// 处理来自系统通知栏的上一曲操作 + Future onMediaSessionPrevious() async { + Logger.d(TAG, '收到媒体会话上一曲回调'); + await playPrevious(); + } + + /// 处理来自系统通知栏的跳转操作 + Future onMediaSessionSeekTo(int positionMs) async { + Logger.d(TAG, '收到媒体会话跳转回调: ${positionMs}ms'); + await seekToPosition(positionMs); + } + + /// 处理来自系统通知栏的停止操作 + Future onMediaSessionStop() async { + Logger.d(TAG, '收到媒体会话停止回调'); + await musicPause(); + // 重置播放状态 + currentPosition.value = 0; + progress.value = 0.0; + _stopProgressTimer(); + } + + // ========== 私有辅助方法 ========== + + /// 更新系统媒体会话的进度信息 + Future _updateMediaSessionProgress() async { + try { + // 具体实现将在music_service插件中添加 + await _musicManager.updateMediaSessionProgress( + currentPosition.value, + duration.value, + progress.value, + ); + } catch (e) { + // 忽略错误,避免影响正常播放 + Logger.d(TAG, '更新媒体会话进度失败: $e'); + } + } + + /// 更新系统媒体会话的元数据 + Future _updateMediaSessionMetadata(Map song) async { + try { + await _musicManager.updateMediaSessionMetadata(song); + } catch (e) { + Logger.d(TAG, '更新媒体会话元数据失败: $e'); + } + } + + /// 更新系统媒体会话的播放状态 + Future _updateMediaSessionPlaybackState(bool isPlaying) async { + try { + await _musicManager.updateMediaSessionPlaybackState(isPlaying); + } catch (e) { + Logger.d(TAG, '更新媒体会话播放状态失败: $e'); + } + } +} \ No newline at end of file diff --git a/lib/modules/music/services/music_service_facade.dart b/lib/modules/music/services/music_service_facade.dart new file mode 100644 index 000000000..689d249f7 --- /dev/null +++ b/lib/modules/music/services/music_service_facade.dart @@ -0,0 +1,125 @@ +import 'package:get/get.dart'; +import '../controllers/music_controller.dart'; +import '../../../data/services/music_manager.dart'; + +/// 音乐服务门面 +/// 作为音乐功能的统一入口,简化模块间的交互 +class MusicServiceFacade { + // 单例实现 + static final MusicServiceFacade _instance = MusicServiceFacade._internal(); + + factory MusicServiceFacade() => _instance; + + MusicServiceFacade._internal(); + + // 获取音乐控制器实例 + MusicController get musicController => Get.find(); + + // 获取音乐管理器实例 + MusicManager get musicManager => Get.find(); + + /// 播放音乐 + Future musicPlay(Map song) async { + await musicController.musicPlay(song); + } + + /// 暂停音乐 + Future musicPause() async { + await musicController.musicPause(); + } + + /// 恢复播放 + Future musicResume() async { + await musicController.musicResume(); + } + + /// 播放音乐(兼容旧接口) + Future playMusic(Map song) async { + await musicPlay(song); + } + + /// 暂停音乐(兼容旧接口) + Future pauseMusic() async { + await musicPause(); + } + + /// 恢复播放(兼容旧接口) + Future resumeMusic() async { + await musicResume(); + } + + /// 播放/暂停切换 + Future togglePlayPause() async { + if (musicController.isPlaying.value) { + await musicPause(); + } else { + await musicResume(); + } + } + + /// 播放上一曲 + Future playPrevious() async { + await musicController.playPrevious(); + } + + /// 播放下一曲 + Future playNext() async { + await musicController.playNext(); + } + + /// 播放音乐列表 + Future playPlaylist(List> playlist) async { + await musicController.playPlaylist(playlist); + } + + /// 跳转到指定位置 + Future seekToPosition(int positionMs) async { + await musicController.seekToPosition(positionMs); + } + + /// 获取当前播放状态 + bool get isPlayingValue => musicController.isPlaying.value; + + /// 获取当前播放歌曲ID + int get currentIdValue => musicController.currentId.value; + + /// 获取当前播放位置(毫秒) + int get currentPositionValue => musicController.currentPosition.value; + + /// 获取当前歌曲总时长(毫秒) + int get durationValue => musicController.duration.value; + + /// 获取当前播放进度(0.0-1.0) + double get progressValue => musicController.progress.value; + + /// 获取响应式播放状态(用于Obx) + get isPlaying => musicController.isPlaying; + + /// 获取响应式当前播放歌曲ID(用于Obx) + get currentId => musicController.currentId; + + /// 获取响应式当前播放位置(用于Obx) + get currentPosition => musicController.currentPosition; + + /// 获取响应式当前歌曲总时长(用于Obx) + get duration => musicController.duration; + + /// 获取响应式当前播放进度(用于Obx) + get progress => musicController.progress; + + /// 获取当前播放列表 + List> get currentPlaylist => musicController.currentPlaylist; + + /// 获取当前播放索引 + int get currentPlaylistIndex => musicController.currentPlaylistIndex.value; + + /// 设置加载状态 + void setLoadingMusic(String id, bool loading) { + musicController.setLoadingMusic(id, loading); + } + + /// 更新当前播放列表上下文 + void updateCurrentPlaylistContext(List> playlist, int songId) { + musicController.updateCurrentPlaylistContext(playlist, songId); + } +} \ No newline at end of file diff --git a/lib/modules/music/views/music_playlist_view.dart b/lib/modules/music/views/music_playlist_view.dart index bdf72da2a..bda2fb878 100644 --- a/lib/modules/music/views/music_playlist_view.dart +++ b/lib/modules/music/views/music_playlist_view.dart @@ -1,9 +1,11 @@ +import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; -import '../../agent/controllers/agent_controller.dart'; -import '../../agent/views/circular_progress_button.dart'; +import 'package:flutter/services.dart'; + +import '../services/music_service_facade.dart'; import '../../../core/utils/logger.dart'; +import '../../agent/views/circular_progress_button.dart'; class MusicPlaylistView extends StatelessWidget { const MusicPlaylistView({Key? key}) : super(key: key); @@ -11,7 +13,7 @@ class MusicPlaylistView extends StatelessWidget { @override Widget build(BuildContext context) { ScreenUtil.init(context, designSize: const Size(375, 812)); // 添加设计尺寸参考 - final controller = Get.find(); + final musicFacade = Get.find(); final List> playlist = Get.arguments ?? []; final isDarkMode = Theme.of(context).brightness == Brightness.dark; @@ -53,14 +55,14 @@ class MusicPlaylistView extends StatelessWidget { Container( height: MediaQuery.of(context).size.height * 0.38, // 增大高度比例 child: _buildCurrentPlayingSection( - controller, playlist, isDarkMode), + musicFacade, playlist, isDarkMode), ), // 中部:播放全部按钮 - _buildPlayAllSection(controller, playlist, isDarkMode), + _buildPlayAllSection(musicFacade, playlist, isDarkMode), // 下部:歌单列表 - 使用剩余空间 Expanded( child: - _buildPlaylistSection(controller, playlist, isDarkMode), + _buildPlaylistSection(musicFacade, playlist, isDarkMode), ), ], ), @@ -68,19 +70,19 @@ class MusicPlaylistView extends StatelessWidget { } // 构建当前播放音乐详情部分 - Widget _buildCurrentPlayingSection(AgentController controller, + Widget _buildCurrentPlayingSection(MusicServiceFacade musicFacade, List> playlist, bool isDarkMode) { return SingleChildScrollView( // 添加滚动支持 child: Obx(() { // 查找当前播放的歌曲 Map? currentSong; - if (controller.currentId.value != 0) { + if (musicFacade.currentId.value != 0) { try { currentSong = playlist.firstWhere( (song) => int.tryParse(song['id']?.toString() ?? '0') == - controller.currentId.value, + musicFacade.currentId.value, ); } catch (e) { // 如果没找到,使用第一首歌曲 @@ -135,10 +137,10 @@ class MusicPlaylistView extends StatelessWidget { _buildSongInfo(title, artist, isDarkMode), SizedBox(height: 16.h), // 进度条 - _buildProgressBar(controller, isDarkMode), + _buildProgressBar(musicFacade, isDarkMode), SizedBox(height: 15.h), // 播放控制按钮 - _buildPlayControls(controller, playlist, isDarkMode), + _buildPlayControls(musicFacade, playlist, isDarkMode), ], ), ); @@ -147,7 +149,7 @@ class MusicPlaylistView extends StatelessWidget { } // 构建播放全部按钮部分 - Widget _buildPlayAllSection(AgentController controller, + Widget _buildPlayAllSection(MusicServiceFacade musicFacade, List> playlist, bool isDarkMode) { return Container( padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.h), @@ -157,7 +159,7 @@ class MusicPlaylistView extends StatelessWidget { child: ElevatedButton.icon( onPressed: () async { if (playlist.isNotEmpty) { - await controller.playPlaylist(playlist); + await musicFacade.playPlaylist(playlist); } }, icon: Icon(Icons.play_arrow, color: Colors.white, size: 20.w), @@ -183,7 +185,7 @@ class MusicPlaylistView extends StatelessWidget { } // 构建歌单列表部分 - Widget _buildPlaylistSection(AgentController controller, + Widget _buildPlaylistSection(MusicServiceFacade musicFacade, List> playlist, bool isDarkMode) { return ListView.builder( padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 8.h), @@ -198,7 +200,7 @@ class MusicPlaylistView extends StatelessWidget { return _buildMusicItem( context, - controller, + musicFacade, id, name, url, @@ -212,7 +214,7 @@ class MusicPlaylistView extends StatelessWidget { Widget _buildMusicItem( BuildContext context, - AgentController controller, + MusicServiceFacade musicFacade, String id, String name, String url, @@ -230,7 +232,7 @@ class MusicPlaylistView extends StatelessWidget { return GestureDetector( onTap: () { - _playSelectedSong(controller, song, Get.arguments ?? []); + _playSelectedSong(musicFacade, song, Get.arguments ?? []); }, child: Container( margin: EdgeInsets.only(bottom: 10.h), @@ -322,10 +324,10 @@ class MusicPlaylistView extends StatelessWidget { // 播放按钮 Obx(() { final currentIdInt = int.tryParse(id) ?? 0; - final isThisPlaying = controller.isPlaying.value && - controller.currentId.value == currentIdInt; + final isThisPlaying = musicFacade.isPlaying.value && + musicFacade.currentId.value == currentIdInt; final currentProgress = - isThisPlaying ? controller.progress.value : 0.0; + isThisPlaying ? musicFacade.progress.value : 0.0; return CircularProgressButton( isPlaying: isThisPlaying, @@ -337,20 +339,20 @@ class MusicPlaylistView extends StatelessWidget { onPlayPause: () async { try { Logger.d('MusicPlaylistView', - '播放按钮被点击,当前播放状态: ${controller.isPlaying.value}'); + '播放按钮被点击,当前播放状态: ${musicFacade.isPlaying.value}'); Logger.d('MusicPlaylistView', - '当前歌曲ID: ${controller.currentId.value}, 点击歌曲ID: $currentIdInt'); - if (controller.isPlaying.value) { - if (controller.currentId.value == currentIdInt) { + '当前歌曲ID: ${musicFacade.currentId.value}, 点击歌曲ID: $currentIdInt'); + if (musicFacade.isPlaying.value) { + if (musicFacade.currentId.value == currentIdInt) { Logger.d('MusicPlaylistView', '暂停当前播放的歌曲'); - await controller.musicPause(); + await musicFacade.musicPause(); } else { Logger.d('MusicPlaylistView', '播放新歌曲'); - await controller.musicPlay(song); + await musicFacade.musicPlay(song); } } else { Logger.d('MusicPlaylistView', '开始播放歌曲'); - await controller.musicPlay(song); + await musicFacade.musicPlay(song); } } catch (e) { Logger.e('MusicPlaylistView', '播放音乐时发生错误: $e'); @@ -360,8 +362,8 @@ class MusicPlaylistView extends StatelessWidget { ? (seekProgress) { // 只有当前播放的歌曲才允许跳转 final targetPosition = - (controller.duration.value * seekProgress).round(); - controller.seekToPosition(targetPosition); + (musicFacade.duration.value * seekProgress).round(); + musicFacade.seekToPosition(targetPosition); } : null, ); @@ -447,14 +449,14 @@ class MusicPlaylistView extends StatelessWidget { } // 构建进度条 - Widget _buildProgressBar(AgentController controller, bool isDarkMode) { + Widget _buildProgressBar(MusicServiceFacade musicFacade, bool isDarkMode) { return Obx(() { return Row( children: [ SizedBox( width: 40.w, child: Text( - _formatDuration(controller.currentPosition.value), + _formatDuration(musicFacade.currentPosition.value), style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[600], @@ -478,11 +480,11 @@ class MusicPlaylistView extends StatelessWidget { overlayShape: const RoundSliderOverlayShape(overlayRadius: 14), ), child: Slider( - value: controller.progress.value.clamp(0.0, 1.0), + value: musicFacade.progress.value.clamp(0.0, 1.0), onChanged: (value) { final targetPosition = - (controller.duration.value * value).round(); - controller.seekToPosition(targetPosition); + (musicFacade.duration.value * value).round(); + musicFacade.seekToPosition(targetPosition); }, ), ), @@ -491,7 +493,7 @@ class MusicPlaylistView extends StatelessWidget { SizedBox( width: 40.w, child: Text( - _formatDuration(controller.duration.value), + _formatDuration(musicFacade.duration.value), style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[600], @@ -506,14 +508,14 @@ class MusicPlaylistView extends StatelessWidget { } // 构建播放控制按钮 - Widget _buildPlayControls(AgentController controller, + Widget _buildPlayControls(MusicServiceFacade musicFacade, List> playlist, bool isDarkMode) { return Obx(() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - onPressed: () => _playPrevious(controller, playlist), + onPressed: () => _playPrevious(musicFacade, playlist), iconSize: 32.w, padding: EdgeInsets.all(8.w), // 增大触摸区域 icon: Icon( @@ -538,10 +540,10 @@ class MusicPlaylistView extends StatelessWidget { ], ), child: IconButton( - onPressed: () => _togglePlayPause(controller), + onPressed: () => _togglePlayPause(musicFacade), padding: EdgeInsets.zero, icon: Icon( - controller.isPlaying.value ? Icons.pause : Icons.play_arrow, + musicFacade.isPlaying.value ? Icons.pause : Icons.play_arrow, size: 28.w, color: Colors.white, ), @@ -549,7 +551,7 @@ class MusicPlaylistView extends StatelessWidget { ), SizedBox(width: 16.w), IconButton( - onPressed: () => _playNext(controller, playlist), + onPressed: () => _playNext(musicFacade, playlist), iconSize: 32.w, padding: EdgeInsets.all(8.w), // 增大触摸区域 icon: Icon( @@ -572,35 +574,35 @@ class MusicPlaylistView extends StatelessWidget { } // 播放/暂停切换 - Future _togglePlayPause(AgentController controller) async { - if (controller.isPlaying.value) { - await controller.musicPause(); + Future _togglePlayPause(MusicServiceFacade musicFacade) async { + if (musicFacade.isPlaying.value) { + await musicFacade.musicPause(); } else { - await controller.musicResume(); + await musicFacade.musicResume(); } } // 播放上一曲 void _playPrevious( - AgentController controller, List> playlist) { - controller.updateCurrentPlaylistContext( - playlist, controller.currentId.value); - controller.playPrevious(); + MusicServiceFacade musicFacade, List> playlist) { + musicFacade.updateCurrentPlaylistContext( + playlist, musicFacade.currentId.value); + musicFacade.playPrevious(); } // 播放下一曲 void _playNext( - AgentController controller, List> playlist) { - controller.updateCurrentPlaylistContext( - playlist, controller.currentId.value); - controller.playNext(); + MusicServiceFacade musicFacade, List> playlist) { + musicFacade.updateCurrentPlaylistContext( + playlist, musicFacade.currentId.value); + musicFacade.playNext(); } // 播放选中的歌曲 - void _playSelectedSong(AgentController controller, Map song, + void _playSelectedSong(MusicServiceFacade musicFacade, Map song, List> playlist) { final songId = int.tryParse(song['id']?.toString() ?? '0') ?? 0; - controller.updateCurrentPlaylistContext(playlist, songId); - controller.musicPlay(song); + musicFacade.updateCurrentPlaylistContext(playlist, songId); + musicFacade.musicPlay(song); } } diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index ef7c04602..5cce91556 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -548,49 +548,49 @@ class SettingsView extends GetView { // ? Colors.white.withOpacity(0.1) // : Colors.grey[200]), // 面对面翻译测试 - _buildSimpleNavigationSetting( - title: '面对面翻译测试', - subtitle: '测试面对面翻译', - icon: Icons.bluetooth_searching, - iconBgColor: isDarkMode - ? Colors.green[900]!.withOpacity(0.3) - : Colors.green[100]!, - iconColor: - isDarkMode ? Colors.green[300]! : Colors.green[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - Get.toNamed(Routes.ftftranslation); - }, - isDarkMode: isDarkMode, - ), - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), - // BLE测试 - _buildSimpleNavigationSetting( - title: 'opus解码测试', - subtitle: '测试opus解码', - icon: Icons.bluetooth_searching, - iconBgColor: isDarkMode - ? Colors.green[900]!.withOpacity(0.3) - : Colors.green[100]!, - iconColor: - isDarkMode ? Colors.green[300]! : Colors.green[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - Get.toNamed(Routes.opusTest); - }, - isDarkMode: isDarkMode, - ), - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), + // _buildSimpleNavigationSetting( + // title: '面对面翻译测试', + // subtitle: '测试面对面翻译', + // icon: Icons.bluetooth_searching, + // iconBgColor: isDarkMode + // ? Colors.green[900]!.withOpacity(0.3) + // : Colors.green[100]!, + // iconColor: + // isDarkMode ? Colors.green[300]! : Colors.green[600]!, + // titleColor: isDarkMode ? Colors.white : null, + // subtitleColor: isDarkMode ? Colors.white70 : null, + // onTap: () { + // Get.toNamed(Routes.ftftranslation); + // }, + // isDarkMode: isDarkMode, + // ), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), + // // BLE测试 + // _buildSimpleNavigationSetting( + // title: 'opus解码测试', + // subtitle: '测试opus解码', + // icon: Icons.bluetooth_searching, + // iconBgColor: isDarkMode + // ? Colors.green[900]!.withOpacity(0.3) + // : Colors.green[100]!, + // iconColor: + // isDarkMode ? Colors.green[300]! : Colors.green[600]!, + // titleColor: isDarkMode ? Colors.white : null, + // subtitleColor: isDarkMode ? Colors.white70 : null, + // onTap: () { + // Get.toNamed(Routes.opusTest); + // }, + // isDarkMode: isDarkMode, + // ), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), // BLE测试 // _buildSimpleNavigationSetting( diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt index 46b8bfaba..40e9c1861 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt @@ -136,8 +136,6 @@ class AzureAsrHelper(private val context: Context) { audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager originalAudioMode = audioManager?.mode ?: AudioManager.MODE_NORMAL - // 5. 初始化麦克风数据 - initAudioRecord() // 保存配置 this.subscriptionKey = subscriptionKey @@ -381,6 +379,8 @@ class AzureAsrHelper(private val context: Context) { } try { + // 5. 初始化麦克风 + initAudioRecord() // 设置各种事件监听 setupEventListeners(callback) @@ -508,7 +508,7 @@ class AzureAsrHelper(private val context: Context) { // 停止音频处理 stopAudioProcessing() - // 停止录音 + // 停止录音 stopMicrophoneCapture() // 尝试强制关闭识别器 try { @@ -734,18 +734,18 @@ class AzureAsrHelper(private val context: Context) { // 获取已确定的采样率 val format = getOptimalAudioFormat() val supportedSampleRates = intArrayOf(16000, 8000, 11025, 22050, 44100) - + val channelConfig = AudioFormat.CHANNEL_IN_MONO // 单声道输入 + val audioFormat = AudioFormat.ENCODING_PCM_16BIT // 16位PCM格式 // 查找设备支持的最佳采样率 val sampleRate = supportedSampleRates.firstOrNull { rate -> val bufferSize = AudioRecord.getMinBufferSize( rate, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT + channelConfig, + audioFormat ) bufferSize > 0 // 返回正值表示支持 } ?: 16000 // 默认回退值 - val channelConfig = AudioFormat.CHANNEL_IN_MONO // 单声道输入 - val audioFormat = AudioFormat.ENCODING_PCM_16BIT // 16位PCM格式 + // 计算最小缓冲区大小(避免出现音频欠载) val minBufferSize = AudioRecord.getMinBufferSize( @@ -808,16 +808,17 @@ class AzureAsrHelper(private val context: Context) { while (!Thread.interrupted() && audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { - // 读取音频 - val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0 - - if (!isPaused && bytesRead > 0) { - // 写入Azure流 - audioStream?.write(buffer) + if (!isPaused) { + // 读取音频 + val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0 + if (bytesRead > 0) { + // 写入Azure流 + audioStream?.write(buffer) - // 保存录音 - recordfile?.saveAudioDataToWav(buffer) + // 保存录音 + recordfile?.saveAudioDataToWav(buffer) + } } } }.apply { start() }