diff --git a/lib/core/bindings/initial_binding.dart b/lib/core/bindings/initial_binding.dart index 0c61157c0..c7c098a95 100644 --- a/lib/core/bindings/initial_binding.dart +++ b/lib/core/bindings/initial_binding.dart @@ -66,9 +66,13 @@ class InitialBinding extends Bindings { // 会议助手音频上传 Get.lazyPut(() => UserportraitManager(), fenix: true); + // qq音乐服务 Get.lazyPut(() => QqMusocService(), fenix: true); + // 强制初始化 qq_music 插件 + // Get.find().initializePlugin(); + // 新手引导 Get.lazyPut(() => NoviceGuideService(), fenix: true); diff --git a/lib/data/services/qq_musoc.dart b/lib/data/services/qq_musoc.dart index c9e5d6ddc..11f3b37ed 100644 --- a/lib/data/services/qq_musoc.dart +++ b/lib/data/services/qq_musoc.dart @@ -25,11 +25,31 @@ class QqMusocService extends GetxService { // 监听器列表 - 用于通知页面状态变化 final List _listeners = []; - /// 初始化 + /// 初始化插件并尝试自动登录 + Future initializePlugin() async { + try { + // 先触发插件注册 + // await _qqMusic.getPlatformVersion(); + + // 尝试自动登录 + final result = await _qqMusic.login(); + if (result['success'] == true) { + print('QQ音乐自动登录成功: ${result['openId']}'); + syncCurrentPlayInfo(); + } else { + print('QQ音乐自动登录失败: ${result['error']}'); + } + } catch (e) { + print('QQ音乐插件初始化失败: $e'); + } + } + @override void onInit() { super.onInit(); - print('🎵 [QQMusicService] 全局音乐服务初始化'); + print('QQ音乐 全局音乐服务初始化'); + // 立即初始化插件 + initializePlugin(); } @override @@ -71,8 +91,8 @@ class QqMusocService extends GetxService { // 立即执行一次同步 _syncPlaybackInfo(); - // 启动定时器,每秒同步一次 - _stateMonitorTimer = Timer.periodic(const Duration(seconds: 1), (timer) { + // 改为每2-3秒同步一次,减少频率 + _stateMonitorTimer = Timer.periodic(const Duration(seconds: 2), (timer) { _syncPlaybackInfo(); }); @@ -87,9 +107,19 @@ class QqMusocService extends GetxService { } /// 统一同步播放信息 + bool _isSyncing = false; + Future _syncPlaybackInfo() async { + if (_isSyncing) { + print('🔄 [QQMusicService] 上次同步未完成,跳过本次'); + return; + } + + _isSyncing = true; try { - final result = await _qqMusic.syncCurrentPlayInfo(); + final result = await _qqMusic + .syncCurrentPlayInfo() + .timeout(const Duration(milliseconds: 500)); // 500ms超时 if (result['success'] == true && result['data'] != null) { final data = result['data']; @@ -160,12 +190,22 @@ class QqMusocService extends GetxService { print('❌ [QQMusicService] 全局同步失败,停止监听'); stopPlaybackStateMonitor(); } + } on TimeoutException { + print('⏰ [QQMusicService] 同步超时,跳过本次更新'); } catch (e) { print('💥 [QQMusicService] 全局同步异常: $e'); stopPlaybackStateMonitor(); + } finally { + _isSyncing = false; } } + // 后台执行的同步方法 + static Future> _performSync(dynamic _) async { + final qqMusic = QqMusic(); + return await qqMusic.syncCurrentPlayInfo(); + } + /// 格式化时间 String formatDuration(int milliseconds) { final duration = Duration(milliseconds: milliseconds); @@ -234,7 +274,7 @@ class QqMusocService extends GetxService { currentPosition.value = 0; // 启动全局状态监听 - print('🎵 [QQMusicService] 开始播放,启动全局状态监听'); + print('🎵 [QQMusicService] 开始播放,启动全局状态监听'); startPlaybackStateMonitor(); _notifyListeners(); @@ -376,6 +416,52 @@ class QqMusocService extends GetxService { Future> syncCurrentPlayInfo() async { try { final result = await _qqMusic.syncCurrentPlayInfo(); + + // 如果同步成功,更新全局状态 + if (result['success'] == true && result['data'] != null) { + final data = result['data']; + + // 更新播放状态 + final newIsPlaying = data['isPlaying'] ?? false; + if (isPlaying.value != newIsPlaying) { + isPlaying.value = newIsPlaying; + } + + // 更新播放时间(Android端返回秒,转换为毫秒) + final currentTimeSec = data['currentTime'] ?? 0; + final totalTimeSec = data['totalTime'] ?? 0; + + final newCurrentPosition = (currentTimeSec * 1000).round(); + final newTotalDuration = (totalTimeSec * 1000).round(); + + if (currentPosition.value != newCurrentPosition) { + currentPosition.value = newCurrentPosition; + } + if (totalDuration.value != newTotalDuration) { + totalDuration.value = newTotalDuration; + } + + // 更新当前歌曲信息 + final songData = data['currentSong']; + if (songData != null) { + final newSong = SongModel( + mid: songData['mid'] ?? '', + name: songData['name'] ?? '', + artist: songData['singer'] ?? '', + album: songData['album'] ?? '', + albumPic: songData['coverUri'], + duration: songData['duration'] ?? 0, + ); + + if (currentSong.value?.mid != newSong.mid) { + currentSong.value = newSong; + } + } + + // 通知所有监听器 + _notifyListeners(); + } + return result; } catch (e) { return {'success': false, 'error': '同步播放信息失败: ${e.toString()}'}; diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 5330abd4c..d4f19eaa2 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'package:amap_service/amap_service.dart'; +import 'package:voitrans/data/services/qq_musoc.dart'; import '../../../data/services/novice_guide_service.dart'; import '/core/utils/upload_oss.dart'; import '/data/models/user_Info.dart'; @@ -44,7 +45,7 @@ class AgentController extends GetxController { //音乐播放管理 final _musiceManager = Get.find(); - + final _qqmusicManager = Get.find(); // 导航管理 final _navigationManager = Get.find(); RxBool isPlaying = false.obs; @@ -157,6 +158,8 @@ class AgentController extends GetxController { _setupTextInputListener(); //设置文本输入监听器 startVoiceInput(); //开启语音输入 + + _qqmusicManager.syncCurrentPlayInfo(); } // 初始化音乐播放状态 diff --git a/lib/modules/agent/views/message_bubble.dart b/lib/modules/agent/views/message_bubble.dart index 70e4f87c9..f5d602616 100644 --- a/lib/modules/agent/views/message_bubble.dart +++ b/lib/modules/agent/views/message_bubble.dart @@ -251,9 +251,9 @@ class MessageBubble extends GetView { // return buildMusicContent(context, playlist); // } // } else - if (meta!.keys.contains('qqmusic_search_songs')) { + if (meta!.keys.contains('qqmusic_searchandplay_songs')) { final qqmusic_search_songs = - meta!['qqmusic_search_songs'] as Map; + meta!['qqmusic_searchandplay_songs'] as Map; final data = qqmusic_search_songs['data']; if (data != null && data is List) { //qq音乐 diff --git a/lib/modules/agent/views/qqmusic_card.dart b/lib/modules/agent/views/qqmusic_card.dart index 25dc78daf..81b5b9846 100644 --- a/lib/modules/agent/views/qqmusic_card.dart +++ b/lib/modules/agent/views/qqmusic_card.dart @@ -2,22 +2,26 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '../../../routes/app_routes.dart'; -import '../controllers/agent_controller.dart'; +import '../../../data/services/qq_musoc.dart'; +import '../../../data/models/song_model.dart'; import 'circular_progress_button.dart'; /// 构建音乐项目 Widget buildQQMusicItem( BuildContext context, String id, String name, String singer, String image) { - final controller = Get.find(); // 获取控制器 + final qqMusicService = QqMusocService.to; // 使用QQ音乐全局服务 final isDarkMode = Theme.of(context).brightness == Brightness.dark; - Map song = { - 'id': id, - 'title': name, - 'artist': singer, - 'coverUrl': image, - }; - controller.musicStatus(); //刷新状态 + // 创建SongModel对象 - 修复字段名 + final song = SongModel( + mid: id, + name: name, + artist: singer, // 修复:使用 artist 而不是 singer + album: '', // 添加必需的 album 字段 + duration: 0, // 默认时长,实际播放时会更新 + albumPic: image, // 修复:使用 albumPic 而不是 pic + ); + return Container( padding: EdgeInsets.all(14.w), decoration: BoxDecoration( @@ -84,12 +88,16 @@ Widget buildQQMusicItem( ), ), Obx(() { - final isThisPlaying = - controller.isPlaying.value && controller.currentId.value == id; + // 使用QQ音乐服务的全局状态 + final isThisPlaying = qqMusicService.isPlaying.value && + qqMusicService.currentSong.value?.mid == id; - // 只有当前播放的歌曲才显示进度 + // 计算播放进度 final currentProgress = - isThisPlaying ? controller.progress.value : 0.0; + isThisPlaying && qqMusicService.totalDuration.value > 0 + ? qqMusicService.currentPosition.value / + qqMusicService.totalDuration.value + : 0.0; return CircularProgressButton( isPlaying: isThisPlaying, @@ -100,33 +108,43 @@ Widget buildQQMusicItem( strokeWidth: 2.0, onPlayPause: () async { try { - if (controller.isPlaying.value) { - if (controller.currentId.value == id) { + if (qqMusicService.isPlaying.value) { + if (qqMusicService.currentSong.value?.mid == id) { // 当前歌曲正在播放,暂停它 - await controller.musicPause(); + await qqMusicService.pauseMusic(); } else { // 其他歌曲正在播放,播放这首歌 - await controller.musicPlay(song); + await qqMusicService.playSongs( + songMids: [id], + index: 0, + songList: [song], + currentSongModel: song, + ); } } else { - if (controller.currentId.value == id) { + if (qqMusicService.currentSong.value?.mid == id) { // 当前歌曲已暂停,恢复播放 - await controller.musicResume(); + await qqMusicService.resumeMusic(); } else { // 播放新歌曲 - await controller.musicPlay(song); + await qqMusicService.playSongs( + songMids: [id], + index: 0, + songList: [song], + currentSongModel: song, + ); } } } catch (e) { // 静默处理错误,避免影响用户体验 + print('播放控制失败: $e'); } }, onSeek: isThisPlaying ? (seekProgress) { - // 只有当前播放的歌曲才允许跳转 - final targetPosition = - (controller.duration.value * seekProgress).round(); - controller.seekToPosition(targetPosition); + // QQ音乐暂不支持拖拽进度,可以在这里添加相关逻辑 + // 或者显示提示信息 + print('QQ音乐暂不支持拖拽进度'); } : null, ); @@ -140,8 +158,20 @@ Widget buildQQMusicItem( Widget buildQQMusicContent( BuildContext context, List> playlist) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - final controller = Get.find(); // 获取控制器 - controller.updateCurrentPlaylistContext(playlist, controller.currentId.value); + final qqMusicService = QqMusocService.to; // 使用QQ音乐全局服务 + + // 将播放列表转换为SongModel列表 - 修复字段名 + final songList = playlist + .map((item) => SongModel( + mid: item["mid"] ?? "", + name: item["name"] ?? "未知歌曲", + artist: item["singer"] ?? "未知歌手", // 修复:使用 artist 而不是 singer + album: '', // 添加必需的 album 字段 + duration: 0, + albumPic: item["pic"] ?? "", // 修复:使用 albumPic 而不是 pic + )) + .toList(); + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -209,7 +239,7 @@ Widget buildQQMusicContent( child: TextButton( onPressed: () { // 跳转到完整歌单页面,传递完整的歌单数据 - Get.toNamed(Routes.qqmusic, arguments: playlist); + Get.toNamed(Routes.qqmusic, arguments: {"playlist": playlist}); }, style: TextButton.styleFrom( minimumSize: Size.zero, diff --git a/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart b/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart index d043f7a28..0c0733191 100644 --- a/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart +++ b/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart @@ -1,6 +1,7 @@ import 'package:get/get.dart'; import '../../../data/models/song_model.dart'; import '../../../data/services/qq_musoc.dart'; +import '../../../core/utils/logger.dart'; class QQMusicPlaylistController extends GetxController { final QqMusocService _qqMusicService = QqMusocService.to; @@ -25,22 +26,83 @@ class QQMusicPlaylistController extends GetxController { final arguments = Get.arguments; if (arguments != null && arguments['playlist'] != null) { final List playlistData = arguments['playlist']; - final songList = - playlistData.map((item) => SongModel.fromJson(item)).toList(); - _qqMusicService.playlist.assignAll(songList); - - if (songList.isNotEmpty) { - _qqMusicService.currentSong.value = songList.first; + try { + // 解析播放列表数据 + final songList = playlistData + .map((item) { + try { + return SongModel.fromJson(item); + } catch (e) { + Logger.e('QQMusicPlaylist', '解析歌曲数据失败: $e, 数据: $item'); + return null; + } + }) + .where((song) => song != null) + .cast() + .toList(); + + Logger.d('QQMusicPlaylist', '成功解析歌曲数量: ${songList.length}'); + + // 更新播放列表 + _qqMusicService.playlist.assignAll(songList); + + // 安全地设置当前歌曲 + if (songList.isNotEmpty) { + _qqMusicService.currentSong.value = songList.first; + _qqMusicService.currentIndex.value = 0; + Logger.d('QQMusicPlaylist', '设置当前歌曲: ${songList.first.name}'); + } else { + _qqMusicService.currentSong.value = null; + _qqMusicService.currentIndex.value = 0; + Logger.w('QQMusicPlaylist', '播放列表为空,无法设置当前歌曲'); + } + } catch (e) { + Logger.e('QQMusicPlaylist', '处理播放列表时发生异常: $e'); + // 确保状态一致性 + _qqMusicService.currentSong.value = null; _qqMusicService.currentIndex.value = 0; } + } else { + Logger.w('QQMusicPlaylist', '未接收到播放列表参数'); + } + + // 确保 playlist 初始化 + if (_qqMusicService.playlist.isEmpty) { + Logger.w('QQMusicPlaylist', 'Playlist is empty on init'); } // 添加状态监听器 _qqMusicService.addListener(_onMusicStateChanged); + // 界面初始化时同步播放状态和进度 + _syncInitialState(); + print('🎵 [QQMusicPlaylistController] 控制器初始化完成'); } + /// 同步初始播放状态 + Future _syncInitialState() async { + try { + Logger.d('QQMusicPlaylist', '开始同步初始播放状态'); + + // 同步当前播放信息 + await _qqMusicService.syncCurrentPlayInfo(); + + // 如果当前有音乐在播放,启动状态监听 + if (_qqMusicService.isPlaying.value) { + Logger.d('QQMusicPlaylist', '检测到音乐正在播放,启动状态监听'); + _qqMusicService.startPlaybackStateMonitor(); + } + + // 触发UI更新 + update(); + + Logger.d('QQMusicPlaylist', '初始状态同步完成'); + } catch (e) { + Logger.e('QQMusicPlaylist', '初始状态同步失败: $e'); + } + } + @override void onClose() { // 移除状态监听器 @@ -57,10 +119,29 @@ class QQMusicPlaylistController extends GetxController { /// 播放歌曲 Future playSong(SongModel song, {List? songList}) async { try { - final List finalSongList = songList ?? [song]; + // 如果没有传入歌曲列表,使用当前播放列表 + final List finalSongList = songList ?? playlist.toList(); final songMids = finalSongList.map((s) => s.mid).toList(); final playIndex = finalSongList.indexOf(song); + // 如果歌曲不在当前列表中,则只播放这一首 + if (playIndex == -1) { + final singleSongList = [song]; + final result = await _qqMusicService.playSongs( + songMids: [song.mid], + index: 0, + songList: singleSongList, + currentSongModel: song, + ); + + if (result['success'] == true) { + Get.snackbar('开始播放', song.name); + } else { + Get.snackbar('播放失败', result['error'] ?? '未知错误'); + } + return; + } + final result = await _qqMusicService.playSongs( songMids: songMids, index: playIndex, diff --git a/lib/modules/qqmusic/views/qqmusic_playlist_view.dart b/lib/modules/qqmusic/views/qqmusic_playlist_view.dart index 59da35edc..5d8dd1f6d 100644 --- a/lib/modules/qqmusic/views/qqmusic_playlist_view.dart +++ b/lib/modules/qqmusic/views/qqmusic_playlist_view.dart @@ -26,7 +26,7 @@ class QqmusicPlaylistView extends GetView { elevation: 0, ), backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], - body: controller.playlist.isEmpty + body: controller.playlist.isEmpty || controller.currentSong.value == null ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -70,21 +70,43 @@ class QqmusicPlaylistView extends GetView { // 构建当前播放音乐详情部分 Widget _buildCurrentPlayingSection(bool isDarkMode) { return SingleChildScrollView( - // 添加滚动支持 child: Obx(() { + // 添加空值检查 + final currentSong = controller.currentSong.value; + if (currentSong == null) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.h), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.music_note, + size: 110.w, + color: Colors.grey[400], + ), + SizedBox(height: 12.h), + Text( + '暂无播放音乐', + style: TextStyle( + fontSize: 16.sp, + color: Colors.grey[600], + ), + ), + ], + ), + ); + } + return Container( padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.h), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - // 封面图片 - 响应式尺寸 - _buildCoverImage(controller.currentSong.value!.albumPic ?? '', - isDarkMode, // Handle nullable albumPic - size: 110.w), + // 封面图片 - 现在安全了 + _buildCoverImage(currentSong.albumPic ?? '', isDarkMode, size: 110.w), SizedBox(height: 12.h), - // 歌曲信息 - _buildSongInfo(controller.currentSong.value!.name, - controller.currentSong.value!.artist, isDarkMode), + // 歌曲信息 - 现在安全了 + _buildSongInfo(currentSong.name, currentSong.artist, isDarkMode), SizedBox(height: 16.h), // 进度条 _buildProgressBar(isDarkMode), @@ -133,15 +155,15 @@ class QqmusicPlaylistView extends GetView { // 构建歌单列表部分 Widget _buildPlaylistSection(bool isDarkMode) { - return ListView.builder( - padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 8.h), - itemCount: controller.playlist.length, - itemBuilder: (context, index) { - final song = controller.playlist[index]; - return _buildMusicItem( - context, - song, - isDarkMode, + return GetBuilder( + builder: (controller) { + return ListView.builder( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 8.h), + itemCount: controller.playlist.length, + itemBuilder: (context, index) { + final song = controller.playlist[index]; + return _buildMusicItem(context, song, isDarkMode); + }, ); }, ); @@ -236,9 +258,10 @@ class QqmusicPlaylistView extends GetView { SizedBox(width: 14.w), // 播放按钮 Obx(() { - final currentIdInt = song.mid; + // 使用 currentSong 的 mid 来判断是否是当前播放的歌曲 final isThisPlaying = controller.isPlaying.value && - controller.currentIndex.value == currentIdInt; + controller.currentSong.value?.mid == song.mid; + final currentProgress = isThisPlaying ? controller.totalDuration.value > 0 ? controller.currentPosition.value / @@ -257,9 +280,9 @@ class QqmusicPlaylistView extends GetView { Logger.d('MusicPlaylistView', '播放按钮被点击,当前播放状态: ${controller.isPlaying.value}'); Logger.d('MusicPlaylistView', - '当前歌曲ID: ${controller.currentIndex.value}, 点击歌曲ID: $currentIdInt'); + '当前歌曲ID: ${controller.currentSong.value?.mid}, 点击歌曲ID: ${song.mid}'); if (controller.isPlaying.value) { - if (controller.currentIndex.value == currentIdInt) { + if (controller.currentSong.value?.mid == song.mid) { Logger.d('MusicPlaylistView', '暂停当前播放的歌曲'); await controller.togglePlayPause(); } else { @@ -513,7 +536,9 @@ class QqmusicPlaylistView extends GetView { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - onPressed: () {}, + onPressed: () async { + await controller.previousSong(); + }, iconSize: 32.w, padding: EdgeInsets.all(8.w), // 增大触摸区域 icon: Icon( diff --git a/local_plugins/agent_service/android/build.gradle.kts b/local_plugins/agent_service/android/build.gradle.kts index 82eec6b49..958edf9f6 100644 --- a/local_plugins/agent_service/android/build.gradle.kts +++ b/local_plugins/agent_service/android/build.gradle.kts @@ -53,4 +53,6 @@ dependencies { add("compileOnly", project(":location_service")) add("compileOnly", project(":bytedance_speech")) add("compileOnly", project(":user_portrait")) + add("compileOnly", project(":qq_music")) + } \ No newline at end of file diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index f70805fac..56031acaa 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -13,6 +13,7 @@ import com.yunqiinnovation.music_service.MusicServiceStarter import com.yunqiinnovation.amap_service.NavigationServiceHelper import com.yunqiinnovation.user_portrait.UserPortrait import com.yunqiinnovation.location_service.LocationService +import com.yunqiinnovation.qq_music.QQMusicSingleton import android.util.Log import kotlinx.coroutines.* import java.util.Collections @@ -1673,7 +1674,40 @@ object AgentService : CoroutineScope { } else { Log.w(TAG, "card_musiclist.musics 不是一个数组") } - } else if (meta.has("card_navigation")) { //导航服务 + } else if (toolname == "qqmusic_searchandplay_songs" && meta.has("data")) { //qq音乐 + //"{\"mid\": \"$mid\", \"name\": \"$name\", \"artist\": \"$artist\", \"album\": \"$album\", \"duration\":$duration,\"pic\": \"$pic\"}" + val musics = meta.optJSONArray("data") + if (musics != null) { + val playlist = mutableListOf>() + for (i in 0 until musics.length()) { + val item = musics.optJSONObject(i) ?: continue + val mid = item.optString("mid", "") + val name = item.optString("name", "") + val artist = item.optString("artist", "") + val album = item.optString("album", "") + val duration = item.optInt("duration", 0) + val pic = item.optLong("pic", 0) + playlist.add( + mapOf( + "mid" to mid, + "name" to name, + "artist" to artist, + "album" to album, + "duration" to duration, + "pic" to pic + ) + ) + } + if (playlist.isNotEmpty()) { + processQQMusicPlayList(playlist) + } else { + Log.w(TAG, "qqmusic_searchandplay_songs 中没有有效的音乐条目") + } + } else { + Log.w(TAG, "qqmusic_searchandplay_songs.musics 不是一个数组") + } + + }else if (meta.has("card_navigation")) { //导航服务 val cardNavigation = meta.getJSONObject("card_navigation") val start = cardNavigation.optString("start", "") val end = cardNavigation.optString("end", "") @@ -1725,7 +1759,39 @@ object AgentService : CoroutineScope { Log.e(TAG, "音乐列表为空,未启动播放服务") } } - + /** + * 处理音乐播放列表 + * 将ai返回的音乐连接 在后台启动播放 + * + * @param musicUrl 图片文件路径 + * @param name 歌曲名称,默认为空 + * @param singer 歌手,默认为false + * @return 是否成功开始处理 + */ + fun processQQMusicPlayList(songs: List>) { + // 如果 playlist 中有有效的歌曲,开始播放 + if (songs.isNotEmpty()) { + val qqMusicSingleton = QQMusicSingleton.getInstance() + // 从songs中提取mid字段构建songMids列表 + val songMids = songs.mapNotNull { song -> + song["mid"] as? String + }.filter { it.isNotEmpty() } + + if (songMids.isNotEmpty()) { + qqMusicSingleton.playSongs(songMids, 0) { result -> + result.onSuccess { + Log.i(TAG, "QQ音乐播放列表启动成功,共${songMids.size}首歌曲") + }.onFailure { exception -> + Log.e(TAG, "QQ音乐播放列表启动失败: ${exception.message}") + } + } + } else { + Log.e(TAG, "音乐列表中没有有效的mid,未启动播放服务") + } + } else { + Log.e(TAG, "音乐列表为空,未启动播放服务") + } + } /** * 处理导航 * 将ai返回的音乐连接 在后台启动播放 diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt index 8bb74a317..e51192ae7 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt @@ -20,8 +20,8 @@ class QQMusicSystemFunctionHandler(private val context: Context? = null) { try { // Only register QQ Music search function client.registerLocalFunction( - "qqmusic_search_songs", - "在QQ音乐中搜索歌曲,支持歌曲、专辑、歌手等搜索类型", + "qqmusic_searchandplay_songs", + "在QQ音乐中搜索并播放歌曲,支持歌曲、专辑、歌手等搜索类型", mapOf( "type" to "object", "properties" to mapOf( @@ -41,38 +41,38 @@ class QQMusicSystemFunctionHandler(private val context: Context? = null) { ) // QQ音乐播放歌曲 - client.registerLocalFunction( - "qqmusic_play_songs", - "播放QQ音乐歌曲列表", - mapOf( - "type" to "object", - "properties" to mapOf( - "songMids" to mapOf( - "type" to "array", - "description" to "歌曲ID列表", - "items" to mapOf("type" to "string") - ), - "index" to mapOf( - "type" to "integer", - "description" to "开始播放的歌曲索引,默认为0" - ) - ), - "required" to listOf("songMids") - ), - QQMusicPlaySongsHandler(context) - ) +// client.registerLocalFunction( +// "qqmusic_play_songs", +// "播放QQ音乐歌曲列表", +// mapOf( +// "type" to "object", +// "properties" to mapOf( +// "songMids" to mapOf( +// "type" to "array", +// "description" to "歌曲ID列表", +// "items" to mapOf("type" to "string") +// ), +// "index" to mapOf( +// "type" to "integer", +// "description" to "开始播放的歌曲索引,默认为0" +// ) +// ), +// "required" to listOf("songMids") +// ), +// QQMusicPlaySongsHandler(context) +// ) // QQ音乐播放控制 - client.registerLocalFunction( - "qqmusic_play", - "播放QQ音乐", - mapOf( - "type" to "object", - "properties" to emptyMap(), - "required" to emptyList() - ), - QQMusicPlayHandler(context) - ) +// client.registerLocalFunction( +// "qqmusic_play", +// "播放QQ音乐", +// mapOf( +// "type" to "object", +// "properties" to emptyMap(), +// "required" to emptyList() +// ), +// QQMusicPlayHandler(context) +// ) client.registerLocalFunction( "qqmusic_pause", @@ -232,7 +232,7 @@ private class QQMusicSearchHandler(private val context: Context?) : FunctionHand val duration = songMap["duration"] ?: 0 "{\"mid\": \"$mid\", \"name\": \"$name\", \"artist\": \"$artist\", \"album\": \"$album\", \"duration\":$duration,\"pic\": \"$pic\"}" } - "{\"success\": true, \"message\": \"搜索成功\", \"data\": [$songsJson]}" + "{\"success\": true, \"message\": \"搜索成功,增在播放\", \"data\": [$songsJson]}" } else { val error = searchResult.exceptionOrNull() "{\"success\": false, \"message\": \"搜索失败: ${error?.message}\"}" diff --git a/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt b/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt index 90d78f91a..7635c1006 100644 --- a/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt +++ b/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt @@ -62,6 +62,7 @@ class QQMusicManager(private val activity: Activity) { } init { + Log.d(TAG, "QQ音乐服务未连接,无法发送hi消息") bindQQMusicApiService() } diff --git a/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QqMusicPlugin.kt b/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QqMusicPlugin.kt index e339988ed..2b00dc15c 100644 --- a/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QqMusicPlugin.kt +++ b/local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QqMusicPlugin.kt @@ -1,6 +1,7 @@ package com.yunqiinnovation.qq_music import android.app.Activity +import android.util.Log import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -15,10 +16,51 @@ class QqMusicPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { private lateinit var channel: MethodChannel private var activity: Activity? = null private val qqMusicSingleton = QQMusicSingleton.getInstance() + private var isRegistered = false override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + if (isRegistered) { + Log.w("QqMusicPlugin", "Plugin already registered, skipping") + return + } + + Log.d("QqMusicPlugin", "onAttachedToEngine called") channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.yunqiinnovation.qq_music") channel.setMethodCallHandler(this) + isRegistered = true + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + Log.d("QqMusicPlugin", "onDetachedFromEngine called") + if (::channel.isInitialized) { + channel.setMethodCallHandler(null) + } + isRegistered = false + } + + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + Log.d("QqMusicPlugin", "onAttachedToActivity called") + activity = binding.activity + println("QQ音乐 onAttachedToActivity") + qqMusicSingleton.initialize(activity!!) + Log.d("QqMusicPlugin", "qqMusicSingleton.initialize completed") + } + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + Log.d("QqMusicPlugin", "onReattachedToActivityForConfigChanges called") + activity = binding.activity + // qqMusicSingleton.initialize(activity!!) + } + + override fun onDetachedFromActivityForConfigChanges() { + Log.d("QqMusicPlugin", "onDetachedFromActivityForConfigChanges called") + activity = null + } + + override fun onDetachedFromActivity() { + Log.d("QqMusicPlugin", "onDetachedFromActivity called") + activity = null + qqMusicSingleton.cleanup() } override fun onMethodCall(call: MethodCall, result: Result) { @@ -200,27 +242,4 @@ class QqMusicPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } } } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } - - override fun onAttachedToActivity(binding: ActivityPluginBinding) { - activity = binding.activity - qqMusicSingleton.initialize(activity!!) - } - - override fun onDetachedFromActivityForConfigChanges() { - activity = null - } - - override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { - activity = binding.activity - qqMusicSingleton.initialize(activity!!) - } - - override fun onDetachedFromActivity() { - activity = null - qqMusicSingleton.cleanup() - } } diff --git a/local_plugins/qq_music/lib/qq_music.dart b/local_plugins/qq_music/lib/qq_music.dart index 3e3bda767..6ec79da88 100644 --- a/local_plugins/qq_music/lib/qq_music.dart +++ b/local_plugins/qq_music/lib/qq_music.dart @@ -180,6 +180,18 @@ class QqMusic { throw Exception('同步播放信息失败: $e'); } } + + /// 获取平台版本 + Future getPlatformVersion() async { + try { + final version = await _methodChannel.invokeMethod( + 'getPlatformVersion', + ); + return version; + } on PlatformException catch (e) { + throw Exception('获取平台版本失败: ${e.message}'); + } + } } /// 登录结果类