diff --git a/assets/images/qqmusic.png b/assets/images/qqmusic.png new file mode 100644 index 000000000..c90f96c15 Binary files /dev/null and b/assets/images/qqmusic.png differ diff --git a/lib/data/services/qq_musoc.dart b/lib/data/services/qq_musoc.dart index e12e2a8d4..b5f1c8c9a 100644 --- a/lib/data/services/qq_musoc.dart +++ b/lib/data/services/qq_musoc.dart @@ -35,7 +35,7 @@ class QqMusocService extends GetxService { final result = await _qqMusic.login(); if (result['success'] == true) { print('QQ音乐自动登录成功: ${result['openId']}'); - Future.delayed(Duration(milliseconds: 500), () { + Future.delayed(Duration(milliseconds: 1000), () { syncCurrentPlayInfo(); }); } else { @@ -270,8 +270,9 @@ class QqMusocService extends GetxService { currentPosition.value = 0; // 启动全局状态监听 - // print('🎵 [QQMusicService] 开始播放,启动全局状态监听'); - syncCurrentPlayInfo(); + Future.delayed(Duration(milliseconds: 500), () { + syncCurrentPlayInfo(); + }); _notifyListeners(); } @@ -288,7 +289,12 @@ class QqMusocService extends GetxService { final result = await _qqMusic.playMusic(); if (result['success'] == true) { isPlaying.value = true; - startPlaybackStateMonitor(); + currentPosition.value = 0; + // 启动全局状态监听 + Future.delayed(Duration(milliseconds: 500), () { + syncCurrentPlayInfo(); + }); + _notifyListeners(); } return result; diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 6711874a5..5ffe68ffa 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -3,6 +3,10 @@ import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'package:amap_service/amap_service.dart'; +import 'package:voitrans/data/models/song_model.dart'; +import 'package:voitrans/data/services/ble_manager.dart'; +import 'package:voitrans/data/services/bluetooth_manager.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'; @@ -733,7 +737,9 @@ class AgentController extends GetxController { as Map; final data = qqmusic_search_songs['data']; if (data != null && data is List) { - _qqmusicManager.syncCurrentPlayInfo(); + Future.delayed(Duration(seconds: 1), () { + _qqmusicManager.syncCurrentPlayInfo(); + }); } } else if (metaResult.containsKey("maps_route_navigation")) { //导航 @@ -1967,4 +1973,54 @@ class AgentController extends GetxController { await seekToProgress( currentIdInternal, newPosition, newDuration, newProgress); } + + /// 处理来自系统通知栏的停止操作 + Future playQQMusic( + List songs, SongModel song, int index) async { + try { + if (_qqmusicManager.isPlaying.value) { + if (_qqmusicManager.currentSong.value?.mid == song.mid) { + // 当前歌曲正在播放,暂停它 + await _qqmusicManager.pauseMusic(); + } else { + final songMids = songs.map((s) => s.mid).toList(); + // 其他歌曲正在播放,播放这首歌 + final result = await _qqmusicManager.playSongs( + songMids: songMids, + index: index, + songList: [song], + currentSongModel: song, + ); + if (result['success'] == true) { + // 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 + // Get.snackbar('开始播放', song.name); + _qqmusicManager.syncCurrentPlayInfo(); + } + return; + } + } else { + if (_qqmusicManager.currentSong.value?.mid == song.mid) { + // 当前歌曲已暂停,恢复播放 + await _qqmusicManager.resumeMusic(); + } else { + final songMids = songs.map((s) => s.mid).toList(); + // 播放新歌曲 + final result = await _qqmusicManager.playSongs( + songMids: songMids, + index: index, + songList: [song], + currentSongModel: song, + ); + if (result['success'] == true) { + // 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 + // Get.snackbar('开始播放', song.name); + _qqmusicManager.syncCurrentPlayInfo(); + } + } + } + } catch (e) { + // 静默处理错误,避免影响用户体验 + print('播放控制失败: $e'); + } + } } diff --git a/lib/modules/agent/views/music_card.dart b/lib/modules/agent/views/music_card.dart index 1639994d8..d2543674f 100644 --- a/lib/modules/agent/views/music_card.dart +++ b/lib/modules/agent/views/music_card.dart @@ -42,20 +42,60 @@ Widget buildMusicItem(BuildContext context, int id, String name, String url, decoration: BoxDecoration( color: isDarkMode ? Colors.grey[800] : Colors.grey[100], borderRadius: BorderRadius.circular(8.r), - image: image.isNotEmpty - ? DecorationImage( - image: NetworkImage(image), - fit: BoxFit.cover, - ) - : null, ), - child: image.isEmpty - ? Icon( - Icons.music_note, - size: 18.sp, - color: Theme.of(context).primaryColor, - ) - : null, + child: Stack( + children: [ + // 主图片 + Container( + width: 50.w, + height: 50.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.r), + image: image.isNotEmpty + ? DecorationImage( + image: NetworkImage(image), + fit: BoxFit.cover, + ) + : null, + ), + child: image.isEmpty + ? Icon( + Icons.music_note, + size: 18.sp, + color: Theme.of(context).primaryColor, + ) + : null, + ), + // 右下角音乐图标 + if (image.isNotEmpty) + Positioned( + right: 2.w, + bottom: 2.w, + child: Container( + width: 12.w, + height: 12.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(2.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 1, + offset: const Offset(0, 0.5), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(2.r), + child: Image.asset( + 'assets/images/qqmusic.png', + fit: BoxFit.contain, + ), + ), + ), + ), + ], + ), ), SizedBox(width: 12.w), Expanded( @@ -158,21 +198,6 @@ Widget buildMusicContent( ), ), const SizedBox(height: 12), - // Container( - // padding: const EdgeInsets.all(12), - // decoration: BoxDecoration( - // color: Colors.white, - // borderRadius: BorderRadius.circular(12), - // boxShadow: [ - // BoxShadow( - // color: Colors.black.withOpacity(0.03), - // blurRadius: 3, - // spreadRadius: 1, - // offset: const Offset(0, 1), - // ), - // ], - // ), - // child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/modules/agent/views/qqmusic_card.dart b/lib/modules/agent/views/qqmusic_card.dart index d5d20d6cc..f901d67c2 100644 --- a/lib/modules/agent/views/qqmusic_card.dart +++ b/lib/modules/agent/views/qqmusic_card.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:voitrans/modules/agent/widgets/circular_progress_button.dart'; import '../../../routes/app_routes.dart'; import '../../../data/services/qq_musoc.dart'; import '../../../data/models/song_model.dart'; -import 'circular_progress_button.dart'; /// 构建音乐项目 Widget buildQQMusicItem( @@ -34,20 +34,46 @@ Widget buildQQMusicItem( decoration: BoxDecoration( color: isDarkMode ? Colors.grey[800] : Colors.grey[100], borderRadius: BorderRadius.circular(8.r), - image: song.albumPic!.isNotEmpty - ? DecorationImage( - image: NetworkImage(song.albumPic!), - fit: BoxFit.cover, - ) - : null, ), - child: song.albumPic!.isEmpty - ? Icon( - Icons.music_note, - size: 18.sp, - color: Theme.of(context).primaryColor, - ) - : null, + child: Stack( + children: [ + // 主图片 + Container( + width: 50.w, + height: 50.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.r), + image: song.albumPic!.isNotEmpty + ? DecorationImage( + image: NetworkImage(song.albumPic!), + fit: BoxFit.cover, + ) + : null, + ), + child: song.albumPic!.isEmpty + ? Icon( + Icons.music_note, + size: 18.sp, + color: Theme.of(context).primaryColor, + ) + : null, + ), + // 右下角音乐图标 + if (song.albumPic!.isNotEmpty) + Positioned( + right: 0.w, + bottom: 0.w, + child: Container( + width: 12.w, + height: 12.w, + child: Image.asset( + 'assets/images/qqmusic.png', + fit: BoxFit.contain, + ), + ), + ), + ], + ), ), SizedBox(width: 12.w), Expanded( @@ -105,12 +131,19 @@ Widget buildQQMusicItem( } else { final songMids = songs.map((s) => s.mid).toList(); // 其他歌曲正在播放,播放这首歌 - await qqMusicService.playSongs( + final result = await qqMusicService.playSongs( songMids: songMids, index: index, songList: [song], currentSongModel: song, ); + if (result['success'] == true) { + // 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 + Get.snackbar('开始播放', song.name); + // qqMusicService.syncCurrentPlayInfo(); + } else { + Get.snackbar('播放失败', result['error'] ?? '未知错误'); + } } } else { if (qqMusicService.currentSong.value?.mid == song.mid) { @@ -119,12 +152,19 @@ Widget buildQQMusicItem( } else { final songMids = songs.map((s) => s.mid).toList(); // 播放新歌曲 - await qqMusicService.playSongs( + final result = await qqMusicService.playSongs( songMids: songMids, index: index, songList: [song], currentSongModel: song, ); + if (result['success'] == true) { + // 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 + Get.snackbar('开始播放', song.name); + // qqMusicService.syncCurrentPlayInfo(); + } else { + Get.snackbar('播放失败', result['error'] ?? '未知错误'); + } } } } catch (e) { @@ -168,11 +208,13 @@ Widget buildQQMusicContent(BuildContext context, List songs) { children: [ Row( children: [ - Icon( - Icons.music_note, - size: 16, - // ignore: deprecated_member_use - color: Theme.of(context).primaryColor.withOpacity(0.7), + Container( + width: 18.w, + height: 18.w, + child: Image.asset( + 'assets/images/qqmusic.png', + fit: BoxFit.contain, + ), ), const SizedBox(width: 6), Text( diff --git a/lib/modules/music/views/music_playlist_view.dart b/lib/modules/music/views/music_playlist_view.dart index d549252fe..3803f328c 100644 --- a/lib/modules/music/views/music_playlist_view.dart +++ b/lib/modules/music/views/music_playlist_view.dart @@ -265,28 +265,63 @@ class MusicPlaylistView extends StatelessWidget { ), ], ), - child: image.isNotEmpty - ? ClipRRect( - borderRadius: BorderRadius.circular(10.r), - child: Image.network( - image, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return Icon( - Icons.music_note, - color: isDarkMode - ? Colors.grey[500] - : Colors.grey[600], - size: 24.w, - ); - }, + child: Stack( + children: [ + image.isNotEmpty + ? ClipRRect( + borderRadius: BorderRadius.circular(10.r), + child: Image.network( + image, + width: 52.w, + height: 52.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.music_note, + color: isDarkMode + ? Colors.grey[500] + : Colors.grey[600], + size: 24.w, + ); + }, + ), + ) + : Icon( + Icons.music_note, + color: + isDarkMode ? Colors.grey[500] : Colors.grey[600], + size: 24.w, + ), + // 右下角音乐图标 + if (image.isNotEmpty) + Positioned( + right: 2.w, + bottom: 2.w, + child: Container( + width: 14.w, + height: 14.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(2.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 1, + offset: const Offset(0, 0.5), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(2.r), + child: Image.asset( + 'assets/images/qqmusic.png', + fit: BoxFit.contain, + ), + ), ), - ) - : Icon( - Icons.music_note, - color: isDarkMode ? Colors.grey[500] : Colors.grey[600], - size: 24.w, ), + ], + ), ), SizedBox(width: 14.w), // 歌曲信息 @@ -391,17 +426,50 @@ class MusicPlaylistView extends StatelessWidget { ), ], ), - child: ClipRRect( - borderRadius: BorderRadius.circular(16.r), - child: coverUrl.isNotEmpty - ? Image.network( - coverUrl, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return _buildDefaultCover(isDarkMode, size: size); - }, - ) - : _buildDefaultCover(isDarkMode, size: size), + child: Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(16.r), + child: coverUrl.isNotEmpty + ? Image.network( + coverUrl, + fit: BoxFit.cover, + width: size, + height: size, + errorBuilder: (context, error, stackTrace) { + return _buildDefaultCover(isDarkMode, size: size); + }, + ) + : _buildDefaultCover(isDarkMode, size: size), + ), + // 右下角音乐图标 + Positioned( + right: 4.w, + bottom: 4.w, + child: Container( + width: size * 0.25, + height: size * 0.25, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(4.r), + child: Image.asset( + 'assets/images/qqmusic.png', + fit: BoxFit.contain, + ), + ), + ), + ), + ], ), ); } diff --git a/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart b/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart index dce3ed7e2..598cf5050 100644 --- a/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart +++ b/lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart @@ -144,6 +144,7 @@ class QQMusicPlaylistController extends GetxController { /// 播放歌曲 Future playSong(SongModel song, {List? songList}) async { try { + displayIsPlaying.value = true; // 使用传入的歌曲列表或当前播放列表 final List finalSongList = songList ?? playlist.toList(); final songMids = finalSongList.map((s) => s.mid).toList(); @@ -162,8 +163,10 @@ class QQMusicPlaylistController extends GetxController { if (result['success'] == true) { // 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 Get.snackbar('开始播放', song.name); + // _qqMusicService.syncCurrentPlayInfo(); } else { Get.snackbar('播放失败', result['error'] ?? '未知错误'); + displayIsPlaying.value = false; } return; } @@ -181,7 +184,7 @@ class QQMusicPlaylistController extends GetxController { if (result['success'] == true) { displayIsPlaying.value = true; - _qqMusicService.syncCurrentPlayInfo(); + // _qqMusicService.syncCurrentPlayInfo(); Get.snackbar('开始播放', song.name); } else { displayIsPlaying.value = false; diff --git a/lib/modules/qqmusic/views/qqmusic_playlist_view.dart b/lib/modules/qqmusic/views/qqmusic_playlist_view.dart index 62ea017ec..062b56466 100644 --- a/lib/modules/qqmusic/views/qqmusic_playlist_view.dart +++ b/lib/modules/qqmusic/views/qqmusic_playlist_view.dart @@ -1,11 +1,10 @@ import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:voitrans/modules/agent/widgets/circular_progress_button.dart'; import 'package:voitrans/modules/qqmusic/controllers/qqmusic_playlist_controller.dart'; import 'package:voitrans/data/models/song_model.dart'; // Use the data model instead import '../../../core/utils/logger.dart'; -import '../../agent/controllers/agent_controller.dart'; -import '../../agent/views/circular_progress_button.dart'; class QqmusicPlaylistView extends GetView { const QqmusicPlaylistView({Key? key}) : super(key: key); @@ -211,19 +210,40 @@ class QqmusicPlaylistView extends GetView { ), ], ), - child: ClipRRect( - borderRadius: BorderRadius.circular(10.r), - child: Image.network( - song.albumPic ?? '', - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return Icon( - Icons.music_note, - color: isDarkMode ? Colors.grey[500] : Colors.grey[600], - size: 24.w, - ); - }, - ), + child: Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10.r), + child: Image.network( + song.albumPic ?? '', + width: 52.w, + height: 52.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.music_note, + color: isDarkMode + ? Colors.grey[500] + : Colors.grey[600], + size: 24.w, + ); + }, + ), + ), + // 右下角音乐图标 + Positioned( + right: 0.w, + bottom: 0.w, + child: Container( + width: 14.w, + height: 14.w, + child: Image.asset( + 'assets/images/qqmusic.png', + fit: BoxFit.contain, + ), + ), + ), + ], )), SizedBox(width: 14.w), // 歌曲信息 @@ -325,17 +345,39 @@ class QqmusicPlaylistView extends GetView { ), ], ), - child: ClipRRect( - borderRadius: BorderRadius.circular(16.r), - child: coverUrl.isNotEmpty - ? Image.network( - coverUrl, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return _buildDefaultCover(isDarkMode, size: size); - }, - ) - : _buildDefaultCover(isDarkMode, size: size), + child: Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(16.r), + child: coverUrl.isNotEmpty + ? Image.network( + coverUrl, + fit: BoxFit.cover, + width: size, + height: size, + errorBuilder: (context, error, stackTrace) { + return _buildDefaultCover(isDarkMode, size: size); + }, + ) + : _buildDefaultCover(isDarkMode, size: size), + ), + // 右下角音乐图标 + Positioned( + right: 0.w, + bottom: 0.w, + child: Container( + width: size * 0.3, + height: size * 0.3, + child: ClipRRect( + borderRadius: BorderRadius.circular(4.r), + child: Image.asset( + 'assets/images/qqmusic.png', + fit: BoxFit.contain, + ), + ), + ), + ), + ], ), ); } @@ -603,61 +645,4 @@ class QqmusicPlaylistView extends GetView { final seconds = duration.inSeconds % 60; return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; } - - // 播放/暂停切换 - // Future _togglePlayPause() async { - // if (controller.actualIsPlaying.value) { - // await controller.musicPause(); - // } else { - // await controller.musicResume(); - // } - // } - - // 播放上一曲 - Future _playPrevious( - AgentController controller, List> playlist) async { - // 防止重复点击导致的状态混乱 - if (controller.isLoadingMusic.value) { - return; - } - - try { - // 设置加载状态,防止重复操作 - controller.isLoadingMusic.value = true; - - // 更新播放列表上下文 - controller.updateCurrentPlaylistContext( - playlist, controller.currentId.value); - - // 执行上一曲操作 - await controller.playPrevious(); - } finally { - // 确保加载状态被重置 - controller.isLoadingMusic.value = false; - } - } - - // 播放下一曲 - Future _playNext( - AgentController controller, List> playlist) async { - // 防止重复点击导致的状态混乱 - if (controller.isLoadingMusic.value) { - return; - } - - try { - // 设置加载状态,防止重复操作 - controller.isLoadingMusic.value = true; - - // 更新播放列表上下文 - controller.updateCurrentPlaylistContext( - playlist, controller.currentId.value); - - // 执行下一曲操作 - await controller.playNext(); - } finally { - // 确保加载状态被重置 - controller.isLoadingMusic.value = false; - } - } } 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 973a5b4c2..6b1235040 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 @@ -212,7 +212,8 @@ private class QQMusicSearchHandler(private val context: Context?) : FunctionHand // 根据搜索类型处理结果 val processedSongs = when (searchType) { 0 -> { // 歌曲搜索,只返回第一个结果 - if (songs.isNotEmpty()) listOf(songs.first()) else emptyList() +// if (songs.isNotEmpty()) listOf(songs.first()) else emptyList() + songs } else -> { // 专辑、歌手等其他类型,返回所有结果 songs diff --git a/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/Headers/include/QPlayAutoDefine.h b/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/Headers/include/QPlayAutoDefine.h new file mode 100644 index 000000000..b1ebee15a --- /dev/null +++ b/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/Headers/include/QPlayAutoDefine.h @@ -0,0 +1,238 @@ +// +// QPlayAutoDefine.h +// QPlayAutoSDK +// +// Created by travisli(李鞠佑) on 2018/11/17. +// Copyright © 2018年 腾讯音乐. All rights reserved. +// + +#ifndef QPlayAutoDefine_h +#define QPlayAutoDefine_h + +extern NSString *const kQPlayAutoItemRootID; +extern NSString *const kQPlayAutoArgument_Count; +extern NSString *const kQPlayAutoArgument_PageIndex; +extern NSString *const kQPlayAutoArgument_ParentID; +extern NSString *const kQPlayAutoArgument_Lists; +extern NSString *const kQPlayAutoArgument_PlayMode; +extern NSString *const kQPlayAutoArgument_IsFav; +extern NSString *const kQPlayAutoArgument_Position; +extern NSString *const kQPlayAutoArgument_Song; +extern NSString *const kQPlayAutoArgument_SongID; +extern NSString *const kQPlayAutoArgument_State; +extern NSString *const kQPlayAutoArgument_Error; + +/* + QPlayAuto错误 + */ +typedef NS_ENUM(NSUInteger, QPlayAutoError) +{ + QPlayAuto_OK=0, //成功 + QPlayAuto_QueryAutoFailed=100, //查询车机信息失败 + QPlayAuto_QueryMobileFailed=101, //查询移动设备失败 + QPlayAuto_QuerySongsFailed_WithoutID=102, //查询歌单数据失败,父 ID 不存在 + QPlayAuto_QuerySongsFailed_NoNetwork=103, //查询歌单数据失败,移动设备网络不通,无法下载数据 + QPlayAuto_QuerySongsFailed_Unknow=104, //查询歌单数据失败,原因未知 + QPlayAuto_SongIDError=105, //歌曲 ID 不存在 + QPlayAuto_ReadError=106, //读取数据错误 + QPlayAuto_ParamError=107, //参数错误 + QPlayAuto_SystemError=108, //系统调用错误 + QPlayAuto_CopyRightError=109, //无法播放,没有版权 + QPlayAuto_LoginError=110, //无法读取,没有登录 + QPlayAuto_PcmRepeat=111, //PCM 请求重复 + QPlayAuto_NoPermission=112, //没有权限 + QPlayAuto_NeedBuy = 113, //无法播放(数字专辑) + QPlayAuto_NotSupportFormat=114, //无法播放(不支持格式) + QPlayAuto_NeedAuth=115, //需要授权 + QPlayAuto_NeedVIP = 116, //无法播放(需要购买VIP) + QPlayAuto_TryListen = 117, //试听歌曲,购买听完整版 + QPlayAuto_TrafficAlert = 118, //无法播放(流量弹窗阻断) + QPlayAuto_OnlyWifi = 119, //无法播放(仅Wifi弹窗阻断) + QPlayAuto_AlreadyConnected=120, //已经连接车机 + QPlayAuto_SeekError = 121, //拖拽进度错误 + QPlayAuto_NoListenRight = 122, //无法播放(无版权) + QPlayAuto_SongNotMatch=123, //歌曲不匹配 + QPlayAuto_Disconnect=124, //连接已断开 +}; + +/** + QPlayAuto连接状态 + */ +typedef NS_ENUM(NSUInteger, QPlayAutoConnectState) +{ + QPlayAutoConnectState_Connected = 0, //连接成功 + QPlayAutoConnectState_Disconnect = 1, //断开连接 + QPlayAutoConnectState_Failed = -1, //连接失败 + QPlayAutoConnectState_Cancel = -2, //连接取消 +}; + +/* + QPlayAutoListItem类型 + */ +typedef NS_ENUM(NSUInteger, QPlayAutoListItemType) +{ + QPlayAutoListItemType_Song = 1, //歌曲 + QPlayAutoListItemType_Normal = 2, //普通目录 + QPlayAutoListItemType_Radio = 3, //电台 + QPlayAutoListItemType_Folder = 4, //歌单 + QPlayAutoListItemType_Album = 5, //专辑 +}; + +typedef NS_ENUM(NSUInteger, QPlayAutoSongListType) +{ + QPlayAutoSongListType_Daily = 108, //每日30首 +}; + + +/** + QPlayAuto播放状态 + */ +typedef NS_ENUM(NSUInteger, QPlayAutoPlayState) +{ + QPlayAutoPlayState_Stop=0, //停止 + QPlayAutoPlayState_Playing=1, //播放 + QPlayAutoPlayState_Pause=2 //暂停 +}; + +typedef NS_ENUM(NSInteger, QPlayAutoAssenceMode) +{ + QPlayAutoAssenceMode_Full = 0, // 默认整首播放 + QPlayAutoAssenceMode_Part = 1, // 仅播放精华片段 +}; + +/** + QPlayAuto播放模式 + */ +typedef NS_ENUM(NSUInteger, QPlayAutoPlayMode) +{ + QPlayAutoPlayMode_SequenceCircle=0, //列表循环 + QPlayAutoPlayMode_SingleCircle=1, //单曲循环 + QPlayAutoPlayMode_RandomCircle=2 //随机播放 +}; + +/** + QPlayAuto搜索类型 + */ +typedef NS_ENUM(NSInteger, QPlayAutoSearchType) +{ + QPlayAutoSearchType_Song = 0, //歌曲 + QPlayAutoSearchType_Album = 2, //专辑 + QPlayAutoSearchType_Folder = 3, //歌单 + QPlayAutoSearchType_Composite = 100, //综合 +}; + +typedef NS_ENUM(NSInteger, QPlayAutoVipState) +{ + QPlayAutoVipState_Normal = 0, + QPlayAutoVipState_VIP = 1, + QPlayAutoVipState_SVIP = 2, +}; + +/** + 第三方App信息 + */ +@interface QPlayAutoAppInfo : NSObject + +@property (nonatomic,strong) NSString *deviceId; //AppId +@property (nonatomic,assign) NSInteger deviceType; +@property (nonatomic,strong) NSString *scheme; //跳转scheme +@property (nonatomic,strong) NSString *brand; //品牌 +@property (nonatomic,strong) NSString *name; //名称 +@property (nonatomic,strong) NSString *appId; //名称 +@property (nonatomic,strong) NSString *secretKey; //私钥 +@property (nonatomic,strong) NSString *bundleId; //bundleId +@property (nonatomic,assign) NSInteger qmCommandPort; //Q音接收命令端口 +@property (nonatomic,strong) NSString *qmHost; //Q音接收命令IP +@property (nonatomic,strong) NSDate *lastConnectDate;//上次连接的日期 +@end + +@interface QPlayAutoSinger : NSObject +@property (nonatomic,strong) NSString *ID; //ID +@property (nonatomic,strong) NSString *Mid; //Mid +@property (nonatomic,strong) NSString *name; //名称 +@end + + +/* + QPlayAuto列表项(歌单、电台、排行榜、歌曲、etc) + */ +@interface QPlayAutoListItem: NSObject + +@property (nonatomic,strong) NSString *ID; //ID +@property (nonatomic,strong) NSString *Name; //名称 +@property (nonatomic,strong) NSString *SubName; //副标题 +@property (nonatomic,strong) QPlayAutoSinger *singer; //歌手 +@property (nonatomic,strong) NSString *Album; //专辑 +@property (nonatomic,strong) NSString *Mid; +@property (nonatomic,assign) QPlayAutoVipState vipState; +@property (nonatomic,assign) QPlayAutoListItemType Type; +@property (nonatomic,assign) NSInteger Duration; //时长 +@property (nonatomic,strong) NSString *CoverUri; //封面Uri +@property (nonatomic,assign) NSInteger totalCount; //总数 +@property (nonatomic,assign) BOOL isFav; //是否收藏 +@property (nonatomic,assign) BOOL isOrigin; //是否原唱 +@property (nonatomic,assign) BOOL isNoAudioSource; //无音源 +@property (nonatomic,strong) NSMutableArray *items; //子列表 +@property (nonatomic,weak) QPlayAutoListItem *parentItem; //父节点 + +@property (nonatomic,assign) BOOL isTryListen; //是否试听歌曲 +@property (nonatomic,readonly) BOOL isSong; //歌曲 +@property (nonatomic,readonly) BOOL isFolder;//歌单 +@property (nonatomic,readonly) BOOL isAlbum; //专辑 +@property (nonatomic,readonly) BOOL isRadio; //电台 +@property (nonatomic,readonly) BOOL isDirectory; //普通目录 + +@property (nonatomic,readonly) BOOL isVIP; //VIP歌曲 +@property (nonatomic,readonly) BOOL isSVIP; //SVIP歌曲 + +- (instancetype)initWithDictionary:(NSDictionary*)dict; +- (instancetype)initSongWithId:(NSString *)identifier isFav:(BOOL)isFav; +- (instancetype)initWithId:(NSString *)identifier type:(QPlayAutoListItemType)type; + +//是否是根节点 +- (BOOL)isRoot; + +//是否有更多 +- (BOOL)hasMore; + +- (QPlayAutoListItem*)findItemWithID:(NSString*)ID; + +@end + +/** + QPlayAuto请求回调 + + @param success 是否成功 + @param dict 结果Dictionary + */ +typedef void (^QPlayAutoRequestFinishBlock)(BOOL success, NSDictionary *dict); + + +@interface QPlayAutoRequestBase : NSObject + +/** 请求类型 */ +@property (nonatomic, assign) int type; + +@end + +@interface QPlayAutoSentence : NSObject +@property (nonatomic, assign) NSInteger startTime; +@property (nonatomic, assign) NSInteger duration; +@property (nonatomic, strong) NSString *text; +@property (nonatomic, readonly) NSString *startTimeText; +@property (nonatomic, readonly) NSString *endTimeText; +@end + +@interface QPlayAutoLyric : NSObject +@property(nonatomic,assign) BOOL isTxtType;//歌词是不是纯文本的歌词,没有时间标记 +@property(nonatomic,strong) NSString *songId; +@property(nonatomic,strong) NSString *songName; +@property(nonatomic,strong) NSString *singerName; +@property(nonatomic,strong) NSString *albumName; +@property(nonatomic,readonly) NSString *text;//所有歌词文本 +@property(nonatomic,strong) NSArray *sentences; +- (NSString *)sentenceAtTime:(NSTimeInterval)time; +@end + + +#endif /* QPlayAutoDefine_h */ diff --git a/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/Headers/include/QPlayAutoSDK.h b/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/Headers/include/QPlayAutoSDK.h new file mode 100644 index 000000000..da332dc87 --- /dev/null +++ b/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/Headers/include/QPlayAutoSDK.h @@ -0,0 +1,188 @@ +// +// QPlayAutoSDK.h +// QPlayAutoSDK +// +// Created by travisli(李鞠佑) on 2018/11/5. +// Copyright © 2018年 腾讯音乐. All rights reserved. +// + +#import +#import "QPlayAutoDefine.h" + +#define APP_DEVICE_TYPE (3) + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^QPlayAutoResultItemsBlock)(NSInteger errorCode,NSArray *_Nullable items); +typedef void (^QPlayAutoResultParentItemBlock)(NSInteger errorCode,QPlayAutoListItem *parentItem); + +extern NSString const * QPlayAutoSDKVersion; + +@protocol QPlayAutoSDKDelegate + +//连接状态变化回调 +- (void)onQPlayAutoConnectStateChanged:(QPlayAutoConnectState)newState; +//播放状态状态变化回调 +- (void)onQPlayAutoPlayStateChanged:(QPlayAutoPlayState)playState song:(QPlayAutoListItem *)song position:(NSInteger)position; +//歌曲播放进度 +- (void)onQPlayAutoPlayProgressChanged:(QPlayAutoListItem *)song progress:(NSTimeInterval)progress duration:(NSTimeInterval)duration; +//歌曲收藏状态变化 +- (void)onSongFavoriteStateChange:(NSString*)songID isFavorite:(BOOL)isFavorite; +//播放状态事件变化 +- (void)onPlayModeChange:(QPlayAutoPlayMode)playMode; +//定时关闭事件 +- (void)onPlayPausedByTimeoff; +//登陆状态 +- (void)onLoginStateDidChanged:(BOOL)isLoginOK; +@end + + + +@interface QPlayAutoSDK : NSObject + +/// 注册App +/// @param name 名称 +/// @param brand 品牌 +/// @param deviceId ? +/// @param scheme 授权成功后Q音跳转合作方的scheme +/// @param appId Q音分配的Id +/// @param secretKey 合作方私钥 +/// @param bundleId 合作方包名 ++ (void)registerAppWithName:(NSString *)name brand:(NSString *)brand deviceId:(NSString *)deviceId scheme:(NSString *)scheme appId:(NSString *)appId secretKey:(NSString *)secretKey bundleId:(NSString *)bundleId; + +/// 播放状态 ++ (QPlayAutoPlayMode)currentPlayMode; +/// 当前歌曲 ++ (QPlayAutoListItem *_Nullable)currentSong; +/// 当前播放状态 ++ (QPlayAutoPlayState)currentPlayState; +/// 当前播放进度 ++ (NSTimeInterval)currentProgress; +/// QQ音乐是否登录 ++ (BOOL)isLoginOK; +/// 检查QQ音乐是否已安装 ++ (BOOL)isQQMusicInstalled; +/// 是否已连接 ++ (BOOL)isConnected; +/// 连接中 ++ (BOOL)isConnecting; +///QQ音乐设备信息 ++ (NSDictionary *)deviceInfo; + ++(NSString *)openId; ++(NSString *)openToken; + + +///是否可以处理回调url ++ (BOOL)canHandleOpenURL:(NSURL *)url; +///处理回调url ++ (BOOL)handleOpenURL:(NSURL *)url; + +/// 回调代理 ++ (void)setDelegate:(id) delegate; + + +/// 拉端连接QPlay +/// - Parameter forceLogin: 是否强制登录 ++ (void)connectAndForceLogin:(BOOL)forceLogin; +/// 停止 ++ (void)stop; + + +/// 二次热启动连接(需要联系Q音打开此功能才能使用) +/// - Parameters: +/// - timeout: 超时时间 +/// - completion: 回调 ++ (void)reconnectWithTimeout:(NSTimeInterval)timeout completion:(QPlayAutoRequestFinishBlock)completion; + +/// 跳转QQ音乐登录 +/// @param bundleId 合作方bundleId +/// @param callbackUrl 跳转合作方url ++ (BOOL)loginQQMusicWithBundleId:(NSString *)bundleId callbackUrl:(NSString *)callbackUrl; + + +/// 获取数据 +/// @param parent 父 +/// @param pageIndex 页码 +/// @param pageSize 页大小 +/// @param completion 回调 ++ (void)getDataItemsFromParent:(QPlayAutoListItem *)parent pageIndex:(NSUInteger)pageIndex pageSize:(NSUInteger)pageSize completion:(QPlayAutoResultParentItemBlock)completion; + + +/// 设置播放模式 +/// @param playMode 播放模式 +/// @param block 回调 ++ (NSInteger)setPlayMode:(QPlayAutoPlayMode)playMode callback:(QPlayAutoRequestFinishBlock _Nullable )block; + + +/// 设置播放整首还是高潮 +/// @param assencceMode 高潮还是整首 +/// @param block 回调 ++ (NSInteger)setAssenceMode:(QPlayAutoAssenceMode)assencceMode callback:(QPlayAutoRequestFinishBlock _Nullable )block; + +/// 收藏/取消收藏(歌曲id) +/// @param song 歌曲 +/// @param isFavorite 收藏/取消收藏 +/// @param completion 回调 ++ (void)setFavoriteStateWithSong:(QPlayAutoListItem *)song isFavorite:(BOOL)isFavorite completion:(void (^)(NSInteger errorCode))completion; + +/// 播放歌曲 +/// @param songList 歌曲列表 +/// @param playIndex 播放索引 +/// @param completion 回调 ++ (void)playAtIndex:(NSArray *)songList playIndex:(NSInteger)playIndex completion:(void (^)(NSInteger errorCode))completion; + + +/// 通过歌曲Mid列表播放歌曲 +/// @param songMidList 歌曲Mid列表 +/// @param playIndex 播放索引 +/// @param completion 回调 ++ (void)playSongMidAtIndex:(NSArray *)songMidList playIndex:(NSInteger)playIndex completion:(void (^)(NSInteger errorCode))completion; + + +///播放下一首 ++ (void)playerPlayNextWithCompletion:(void (^)(NSInteger errorCode))completion; + +///播放上一首 ++ (void)playerPlayPrevWithCompletion:(void (^)(NSInteger errorCode))completion; + +///播放暂停 ++ (void)playerPlayPause; + +///播放恢复 ++ (void)playerPlayResumeWithCompletion:(void (^)(NSInteger errorCode))completion; + +///Seek到指定位置 ++ (void)playerSeek:(NSInteger)position; + + +/// 搜索歌曲 +/// @param keyword 关键词 +/// @param type 搜索类型 +/// @param firstPage 是否第一页 +/// @param completion 回调 ++ (NSInteger)search:(NSString *)keyword + type:(QPlayAutoSearchType)type + firstPage:(BOOL)firstPage + completion:(QPlayAutoResultItemsBlock)completion; + +/// 相似歌曲 +/// @param song 歌曲 +/// @param completion 回调 ++ (void)requestSimilarWithSong:(QPlayAutoListItem *)song completion:(QPlayAutoResultItemsBlock)completion; + + +/// 查询歌词 +/// @param song 歌曲 +/// @param completion 回调 ++ (void)requestLyricWithSong:(QPlayAutoListItem *)song completion:(void (^)(NSInteger errorCode,QPlayAutoLyric *_Nullable lyric))completion; + + +/// 获取特定歌曲列表 +/// @param type 列表类型 +/// @param completion 回调 ++ (void)requestSonglistWithType:(QPlayAutoSongListType)type completion:(QPlayAutoResultItemsBlock)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/libQPlayAutoSDK.a b/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/libQPlayAutoSDK.a new file mode 100644 index 000000000..2ad7aa0a1 Binary files /dev/null and b/local_plugins/qq_music/ios/qq_music/ios-sdk/QPlayAutoSDK/libQPlayAutoSDK.a differ diff --git a/pubspec.yaml b/pubspec.yaml index c9e4d81b6..c6aaae6db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -142,5 +142,6 @@ flutter: - assets/js/ - assets/images/headphone_new4.png - assets/images/headphone_new3.png + - assets/images/qqmusic.png - .env - .mcp.json