|
|
|
@ -25,23 +25,35 @@ import '../../../data/models/message_model.dart'; |
|
|
|
|
|
|
|
// 语音输入模式枚举 |
|
|
|
enum VoiceInputMode { |
|
|
|
text, // 文字输入模式 |
|
|
|
pushToTalk, // 按住说话模式 |
|
|
|
phoneCall, // 打电话模式 |
|
|
|
text, // 文字输入模式 |
|
|
|
pushToTalk, // 按住说话模式 |
|
|
|
phoneCall, // 打电话模式 |
|
|
|
} |
|
|
|
|
|
|
|
class AgentController extends GetxController { |
|
|
|
static AgentController get to => Get.find(); //单例访问模式 |
|
|
|
|
|
|
|
// 聊天消息列表 |
|
|
|
final messages = <Message>[].obs; |
|
|
|
|
|
|
|
// UI控制器 |
|
|
|
final TextEditingController textController = TextEditingController(); |
|
|
|
final ScrollController scrollController = ScrollController(); |
|
|
|
//音乐播放管理 |
|
|
|
final _musiceManager = Get.find<MusicManager>(); |
|
|
|
|
|
|
|
// 导航管理 |
|
|
|
final _navigationManager = Get.find<NavigationManager>(); |
|
|
|
RxBool isPlaying = false.obs; |
|
|
|
RxInt currentId = 0.obs; |
|
|
|
|
|
|
|
// 播放进度相关状态 |
|
|
|
RxInt currentPosition = 0.obs; // 当前播放位置(毫秒) |
|
|
|
RxInt duration = 0.obs; // 总时长(毫秒) |
|
|
|
RxDouble progress = 0.0.obs; // 播放进度(0.0-1.0) |
|
|
|
|
|
|
|
// 当前播放列表管理 |
|
|
|
final currentPlaylist = <Map<String, dynamic>>[].obs; // 当前播放列表 |
|
|
|
final currentPlaylistIndex = 0.obs; // 当前播放歌曲在列表中的索引 |
|
|
|
|
|
|
|
// 各种状态标志 |
|
|
|
final isInitialized = false.obs; |
|
|
|
@ -98,6 +110,8 @@ class AgentController extends GetxController { |
|
|
|
// BLE事件订阅 |
|
|
|
StreamSubscription<dynamic>? _bleEventSubscription; |
|
|
|
|
|
|
|
// 进度更新定时器 |
|
|
|
Timer? _progressTimer; |
|
|
|
// 用于跟踪AI回复状态 |
|
|
|
bool _isNewAssistantResponse = true; |
|
|
|
String _currentResponseId = ''; |
|
|
|
@ -110,17 +124,54 @@ 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(); |
|
|
|
_loadTtsPreference(); //加载TTS偏好设置 |
|
|
|
_loadChatHistory(); //加载聊天历史记录 |
|
|
|
_subscribeToEvents(); //订阅代理服务事件 |
|
|
|
_subscribeToBleEvents(); //订阅BLE事件 |
|
|
|
_initMusicStatus(); //初始化音乐状态 |
|
|
|
_setupMediaSessionCallbacks(); //设置媒体会话回调 |
|
|
|
_setupTextInputListener(); //设置文本输入监听器 |
|
|
|
} |
|
|
|
|
|
|
|
// 初始化音乐播放状态 |
|
|
|
Future<void> _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, |
|
|
|
onStop1: onMediaSessionSeekTo1, |
|
|
|
); |
|
|
|
Logger.d('AgentController', '媒体会话回调设置完成'); |
|
|
|
} catch (e) { |
|
|
|
Logger.e('AgentController', '媒体会话回调设置失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
void onClose() { |
|
|
|
@ -994,7 +1045,8 @@ class AgentController extends GetxController { |
|
|
|
_pendingPlusOptionsExpansion = false; |
|
|
|
// 添加小延迟确保UI稳定 |
|
|
|
Future.delayed(const Duration(milliseconds: 50), () { |
|
|
|
if (!isKeyboardVisible.value) { // 再次确认键盘状态 |
|
|
|
if (!isKeyboardVisible.value) { |
|
|
|
// 再次确认键盘状态 |
|
|
|
isPlusOptionsExpanded.value = true; |
|
|
|
} |
|
|
|
}); |
|
|
|
@ -1194,7 +1246,8 @@ class AgentController extends GetxController { |
|
|
|
} |
|
|
|
|
|
|
|
// 检查文字模式与语音模式的一致性 |
|
|
|
if (isTextInputMode.value && (isPushToTalkMode.value || isPhoneCallMode.value)) { |
|
|
|
if (isTextInputMode.value && |
|
|
|
(isPushToTalkMode.value || isPhoneCallMode.value)) { |
|
|
|
Logger.w(TAG, '状态冲突:文字模式与语音模式同时启用,优先保持语音模式'); |
|
|
|
isTextInputMode.value = false; |
|
|
|
} |
|
|
|
@ -1268,12 +1321,299 @@ class AgentController extends GetxController { |
|
|
|
await stopVoiceInput(); |
|
|
|
} |
|
|
|
|
|
|
|
// 获取音乐播放状态 |
|
|
|
Future<void> 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}'); |
|
|
|
print("音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}"); |
|
|
|
} catch (e) { |
|
|
|
Logger.e('AgentController', '获取音乐状态失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 播放音乐 |
|
|
|
Future<void> musicPlay(Map<String, dynamic> 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<String, dynamic>; |
|
|
|
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 = <String, String>{ |
|
|
|
'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(musicData); |
|
|
|
await _updateMediaSessionPlaybackState(true); |
|
|
|
|
|
|
|
// 启动进度定时器 |
|
|
|
_startProgressTimer(); |
|
|
|
|
|
|
|
Logger.d(TAG, '音乐播放启动成功'); |
|
|
|
} catch (e) { |
|
|
|
Logger.e(TAG, '播放音乐失败: $e'); |
|
|
|
// 发生错误时重置状态 |
|
|
|
isPlaying.value = false; |
|
|
|
currentId.value = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 暂停音乐 |
|
|
|
Future<void> musicPause() async { |
|
|
|
try { |
|
|
|
Logger.d('AgentController', '暂停音乐播放'); |
|
|
|
await _musiceManager.pause(); |
|
|
|
isPlaying.value = false; |
|
|
|
_stopProgressTimer(); |
|
|
|
|
|
|
|
// 更新媒体会话播放状态 |
|
|
|
await _updateMediaSessionPlaybackState(false); |
|
|
|
} catch (e) { |
|
|
|
Logger.e('AgentController', '暂停音乐失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 恢复音乐播放 |
|
|
|
Future<void> 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<void> 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<void> updateMusicProgress1(String currentIdInternal, int newPosition, |
|
|
|
int newDuration, double newProgress) async { |
|
|
|
try { |
|
|
|
if (int.parse(currentIdInternal) != 0) { |
|
|
|
isPlaying.value = true; |
|
|
|
// return; // 如果没有在播放,直接返回 |
|
|
|
} |
|
|
|
|
|
|
|
// // var progressInfo = await _musiceManager.getProgress(); |
|
|
|
// final newPosition = progressInfo['currentPosition'] ?? 0; |
|
|
|
// final newDuration = progressInfo['duration'] ?? 0; |
|
|
|
// final newProgress = progressInfo['progress'] ?? 0.0; |
|
|
|
print( |
|
|
|
"updateMusicProgress1:currentIdInternal:$currentIdInternal newPosition:$newPosition newDuration:$newDuration newProgress:$newProgress"); |
|
|
|
bool hasUpdate = false; |
|
|
|
if (currentId.value != int.parse(currentIdInternal)) { |
|
|
|
currentId.value = int.parse(currentIdInternal); |
|
|
|
hasUpdate = true; |
|
|
|
} |
|
|
|
// 只有当值发生变化时才更新,避免不必要的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<void> _updateMediaSessionProgress() async { |
|
|
|
try { |
|
|
|
// 这里将调用music_service插件的方法来更新系统媒体会话进度 |
|
|
|
// 具体实现将在music_service插件中添加 |
|
|
|
await _musiceManager.updateMediaSessionProgress( |
|
|
|
currentPosition.value, |
|
|
|
duration.value, |
|
|
|
progress.value, |
|
|
|
); |
|
|
|
} catch (e) { |
|
|
|
// 忽略错误,避免影响正常播放 |
|
|
|
Logger.d('AgentController', '更新媒体会话进度失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 更新系统媒体会话的元数据 |
|
|
|
Future<void> _updateMediaSessionMetadata(Map<String, dynamic> song) async { |
|
|
|
try { |
|
|
|
await _musiceManager.updateMediaSessionMetadata(song); |
|
|
|
} catch (e) { |
|
|
|
Logger.d('AgentController', '更新媒体会话元数据失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 更新系统媒体会话的播放状态 |
|
|
|
Future<void> _updateMediaSessionPlaybackState(bool isPlaying) async { |
|
|
|
try { |
|
|
|
await _musiceManager.updateMediaSessionPlaybackState(isPlaying); |
|
|
|
} catch (e) { |
|
|
|
Logger.d('AgentController', '更新媒体会话播放状态失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 跳转到指定位置 |
|
|
|
Future<void> seekToPosition(int positionMs) async { |
|
|
|
try { |
|
|
|
await _musiceManager.seekTo(positionMs); |
|
|
|
// 立即更新进度状态 |
|
|
|
// await updateMusicProgress(); |
|
|
|
} catch (e) { |
|
|
|
Logger.e('AgentController', '跳转播放位置失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 跳转到指定位置 |
|
|
|
Future<void> seekToPosition1(String currentIdInternal, int newPosition, |
|
|
|
int newDuration, double newProgress) async { |
|
|
|
try { |
|
|
|
//await _musiceManager.seekTo(positionMs); |
|
|
|
// 立即更新进度状态 |
|
|
|
await updateMusicProgress1( |
|
|
|
currentIdInternal, newPosition, newDuration, newProgress); |
|
|
|
} catch (e) { |
|
|
|
Logger.e('AgentController', '跳转播放位置失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 修改自动停止处理,遵循TTS设置 |
|
|
|
void _handleAutoStop(int index) { |
|
|
|
@ -1313,7 +1653,188 @@ class AgentController extends GetxController { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
/// 播放音乐列表 |
|
|
|
Future<void> playPlaylist(List<Map<String, dynamic>> 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<void> 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<void> 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<Map<String, dynamic>> 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<void> onMediaSessionPlay() async { |
|
|
|
Logger.d(TAG, '收到媒体会话播放回调'); |
|
|
|
if (!isPlaying.value) { |
|
|
|
await musicResume(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理来自系统通知栏的暂停操作 |
|
|
|
Future<void> onMediaSessionPause() async { |
|
|
|
Logger.d(TAG, '收到媒体会话暂停回调'); |
|
|
|
if (isPlaying.value) { |
|
|
|
await musicPause(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理来自系统通知栏的下一曲操作 |
|
|
|
Future<void> onMediaSessionNext() async { |
|
|
|
Logger.d(TAG, '收到媒体会话下一曲回调'); |
|
|
|
await playNext(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理来自系统通知栏的上一曲操作 |
|
|
|
Future<void> onMediaSessionPrevious() async { |
|
|
|
Logger.d(TAG, '收到媒体会话上一曲回调'); |
|
|
|
await playPrevious(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理来自系统通知栏的跳转操作 |
|
|
|
Future<void> onMediaSessionSeekTo(int positionMs) async { |
|
|
|
Logger.d(TAG, '收到媒体会话跳转回调: ${positionMs}ms'); |
|
|
|
await seekToPosition(positionMs); |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理来自系统通知栏的停止操作 |
|
|
|
Future<void> onMediaSessionStop() async { |
|
|
|
Logger.d(TAG, '收到媒体会话停止回调'); |
|
|
|
await musicPause(); |
|
|
|
// 重置播放状态 |
|
|
|
currentPosition.value = 0; |
|
|
|
progress.value = 0.0; |
|
|
|
_stopProgressTimer(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理来自系统通知栏的跳转操作 |
|
|
|
Future<void> onMediaSessionSeekTo1(String currentIdInternal, int newPosition, |
|
|
|
int newDuration, double newProgress) async { |
|
|
|
print( |
|
|
|
"onMediaSessionSeekTo1 currentIdInternal :$currentIdInternal ,newPosition:$newPosition newDuration:$newDuration newProgress:$newProgress"); |
|
|
|
await seekToPosition1( |
|
|
|
currentIdInternal, newPosition, newDuration, newProgress); |
|
|
|
} |
|
|
|
} |
|
|
|
|