diff --git a/lib/core/bindings/initial_binding.dart b/lib/core/bindings/initial_binding.dart index 7354a5dc7..00c74738a 100644 --- a/lib/core/bindings/initial_binding.dart +++ b/lib/core/bindings/initial_binding.dart @@ -21,12 +21,15 @@ import '../../data/services/ble_manager.dart'; import '../../data/services/account_service.dart'; import '../../data/services/version_update_service.dart'; import '../../data/services/ota_check_service.dart'; +import '../../core/utils/app_lifecycle_manager.dart'; /// 初始绑定,用于管理全局依赖 class InitialBinding extends Bindings { @override void dependencies() { Logger.warning('InitialBinding dependencies'); + // 应用生命周期管理器 + Get.put(AppLifecycleManager(), permanent: true); // 语言管理器(需要最先初始化) Get.lazyPut(() => LanguageManager(), fenix: true); diff --git a/lib/core/translations/app_translations.dart b/lib/core/translations/app_translations.dart index feee5eb94..cf89b513a 100644 --- a/lib/core/translations/app_translations.dart +++ b/lib/core/translations/app_translations.dart @@ -88,6 +88,7 @@ class AppTranslations extends Translations { // 默认使用简体中文 return fallbackLocale; } + static final supportedLocales = [ const Locale('zh', 'CN'), // 简体中文 const Locale('zh', 'HK'), // 香港繁体中文 @@ -165,6 +166,14 @@ class AppTranslations extends Translations { final GetStorage storage = GetStorage(); String? language = storage.read('language'); + // print('lxm---系统语言发生变化: --------------------------'); + // print('lxm---Get.deviceLocale: ${Get.deviceLocale}'); + // print('lxm---Get.locale: ${Get.locale}'); + + // print('lxm---语言代码: ${Get.deviceLocale?.languageCode}'); + // print('lxm---国家代码: ${Get.deviceLocale?.countryCode}'); + // print('lxm---脚本代码: ${Get.deviceLocale?.scriptCode}'); + // 如果用户手动设置了语言且不是跟随系统 if (language != null && language != 'system') { List languageList = language.split('_'); diff --git a/lib/core/utils/app_lifecycle_manager.dart b/lib/core/utils/app_lifecycle_manager.dart index f1ea2b0b8..6c4e178c5 100644 --- a/lib/core/utils/app_lifecycle_manager.dart +++ b/lib/core/utils/app_lifecycle_manager.dart @@ -1,23 +1,28 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; import 'logger.dart'; +import '../translations/app_translations.dart'; +//import '../../modules/settings/controllers/settings_controller.dart'; /// 应用生命周期管理器 /// /// 监控应用的前台/后台状态变化,并在状态变化时执行相应操作 class AppLifecycleManager extends GetxService with WidgetsBindingObserver { // 当前应用生命周期状态 - final Rx _appLifecycleState = AppLifecycleState.resumed.obs; + final Rx _appLifecycleState = + AppLifecycleState.resumed.obs; AppLifecycleState get appLifecycleState => _appLifecycleState.value; - + // 状态变化流 final _lifecycleStateStream = AppLifecycleState.resumed.obs; - Stream get lifecycleStateStream => _lifecycleStateStream.stream; - + Stream get lifecycleStateStream => + _lifecycleStateStream.stream; + // 是否在前台 final RxBool _isInForeground = true.obs; bool get isInForeground => _isInForeground.value; - + @override void onInit() { super.onInit(); @@ -25,15 +30,15 @@ class AppLifecycleManager extends GetxService with WidgetsBindingObserver { WidgetsBinding.instance.addObserver(this); Logger.info('AppLifecycleManager 已初始化'); } - + @override void didChangeAppLifecycleState(AppLifecycleState state) { final previousState = _appLifecycleState.value; _appLifecycleState.value = state; _lifecycleStateStream.value = state; - + // Logger.info('应用生命周期状态变化: $previousState -> $state'); - + // 根据状态变化更新前台/后台标志 switch (state) { case AppLifecycleState.resumed: @@ -50,12 +55,12 @@ class AppLifecycleManager extends GetxService with WidgetsBindingObserver { break; } } - + /// 处理应用进入前台 void _handleAppResumed() { // Logger.info('应用进入前台'); // 这里不需要主动恢复服务,因为我们希望用户手动控制录音开始 - // 如果正在录音,停止录音 + // 如果正在录音,停止录音 // if (asrService.isRecording) { // Logger.info('应用进入后台,停止语音识别'); @@ -64,11 +69,11 @@ class AppLifecycleManager extends GetxService with WidgetsBindingObserver { // ttsService.disconnect(); // } } - + /// 处理应用进入后台 void _handleAppPaused(AppLifecycleState state) { // Logger.info('应用进入后台: $state'); - + // 获取 VolcanoAsrApiService 实例 // 如果正在录音,停止录音 @@ -80,7 +85,7 @@ class AppLifecycleManager extends GetxService with WidgetsBindingObserver { // } } - + @override void onClose() { // 移除应用生命周期观察者 @@ -88,4 +93,55 @@ class AppLifecycleManager extends GetxService with WidgetsBindingObserver { // Logger.info('AppLifecycleManager 已关闭'); super.onClose(); } -} \ No newline at end of file + + /// 处理系统语言变化 + @override + void didChangeLocales(List? locales) { + super.didChangeLocales(locales); + // print('lxm---系统语言发生变化: ${locales?.first}'); + // print('lxm---Get.deviceLocale: ${Get.deviceLocale}'); + // print('lxm---Get.locale: ${Get.locale}'); + // print( + // 'lxm---AppTranslations.deviceLocale(): ${AppTranslations.deviceLocale()}'); + // print('lxm---语言代码: ${locales?.first.languageCode}'); + // print('lxm---国家代码: ${locales?.first.countryCode}'); + // print('lxm---脚本代码: ${locales?.first.scriptCode}'); + + Logger.info('系统语言发生变化: ${locales?.first}'); + + // 检查用户是否手动设置了语言 + final storage = GetStorage(); + String? userLanguage = storage.read('language'); + + if (userLanguage != null && userLanguage != 'system') { + // 用户手动设置了语言,保持用户选择,不跟随系统 + Logger.info('用户已手动设置语言为: $userLanguage,忽略系统语言变化'); + + // 强制恢复用户设置的语言 + List languageList = userLanguage.split('_'); + Locale userLocale = Locale(languageList[0], languageList[1]); + + // 延迟执行,确保系统语言变化处理完成 + Future.delayed(const Duration(milliseconds: 100), () { + Get.updateLocale(userLocale); + Logger.info('已恢复用户设置的语言: ${userLocale.toString()}'); + }); + } else { + // 用户选择跟随系统,更新应用语言 + Logger.info('用户选择跟随系统语言,更新应用语言'); + + Locale? newLocale = AppTranslations.deviceLocale(); + if (newLocale != null) { + Get.updateLocale(newLocale); + + // 同步更新设置控制器中的语言状态 + try { + //final settingsController = Get.find(); + // 这里可能需要根据新的locale更新settingsController的language状态 + } catch (e) { + Logger.warning('未找到SettingsController实例: $e'); + } + } + } + } +} diff --git a/lib/data/services/qq_music.dart b/lib/data/services/qq_music.dart index b1d7d40dd..e5f90bcce 100644 --- a/lib/data/services/qq_music.dart +++ b/lib/data/services/qq_music.dart @@ -37,7 +37,7 @@ class QqMusicService extends GetxService { if (result['success'] == true) { print('QQ音乐自动登录成功: ${result['openId']}'); Future.delayed(Duration(milliseconds: 1000), () { - syncCurrentPlayInfo(isFrest: true); + syncCurrentPlayInfo(); }); } else { print('QQ音乐自动登录失败: ${result['error']}'); @@ -422,7 +422,7 @@ class QqMusicService extends GetxService { /// 同步当前播放信息 - 统一接口 Future> syncCurrentPlayInfo( - {bool isFrest = false}) async { + {bool isOpenPlay = false}) async { try { _stateMonitorTimer?.cancel(); //先关闭计时器 final result = await _qqMusic.syncCurrentPlayInfo(); @@ -473,6 +473,12 @@ class QqMusicService extends GetxService { } else { stopPlaybackStateMonitor(); currentSong.value = null; + + if (isOpenPlay) { + Future.delayed(Duration(seconds: 2), () { + syncCurrentPlayInfo(); + }); + } } // 通知所有监听器 _notifyListeners(); diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 124cc4f57..fd7dc00e4 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -780,16 +780,20 @@ class AgentController extends GetxController { ); } } - if (metaResult.containsKey("qqmusic_searchandplay_songs")) { - final qqmusic_search_songs = - metaResult['qqmusic_searchandplay_songs'] - as Map; - final data = qqmusic_search_songs['data']; - if (data != null && data is List) { - Future.delayed(Duration(seconds: 1), () { - _qqmusicManager.syncCurrentPlayInfo(); - }); - } + final musicKey = metaResult.keys.firstWhere( + (key) => key.contains("music"), + orElse: () => "", + ); + if (musicKey.isNotEmpty) { + // final qqmusic_search_songs = + // metaResult['qqmusic_searchandplay_songs'] + // as Map; + // final data = qqmusic_search_songs['data']; + // if (data != null && data is List) { + Future.delayed(Duration(seconds: 2), () { + _qqmusicManager.syncCurrentPlayInfo(isOpenPlay: true); + }); + // } } else if (metaResult.containsKey("maps_route_navigation")) { //导航 final start = metaResult["maps_route_navigation"] diff --git a/local_plugins/qq_music/ios/qq_music/Sources/qq_music/QPlayAutoManager.swift b/local_plugins/qq_music/ios/qq_music/Sources/qq_music/QPlayAutoManager.swift index eaee48f19..8782d68c0 100644 --- a/local_plugins/qq_music/ios/qq_music/Sources/qq_music/QPlayAutoManager.swift +++ b/local_plugins/qq_music/ios/qq_music/Sources/qq_music/QPlayAutoManager.swift @@ -492,8 +492,13 @@ import QPlayAutoSDK print("QPlayAutoManager: 播放歌曲失败 - 登录失败") } } - + private var pendingPlayCompletion: ((Bool, String?) -> Void)? + private var playVerificationTimer: Timer? @objc public func playSongs(mids: [String], index: Int = 0, completion: @escaping (Bool, String?) -> Void) { + playSongsWithRetry(mids: mids, index: index, retryCount: 0, maxRetries: 3, completion: completion) + } + + private func playSongsWithRetry(mids: [String], index: Int, retryCount: Int, maxRetries: Int, completion: @escaping (Bool, String?) -> Void) { guard !mids.isEmpty else { completion(false, "歌曲列表为空") return @@ -504,16 +509,47 @@ import QPlayAutoSDK return } - // 修改:使用 ensureQQMusicInstalledAndLoginAndExecute 替代 ensureLoginAndExecute ensureQQMusicInstalledAndLoginAndExecute { QPlayAutoSDK.playSongMid(atIndex: mids, play: index) { errorCode in if errorCode == 0 { - completion(true, nil) - print("QQ音乐播放列表启动成功,共\(mids.count)首歌曲") + // 不立即返回成功,而是等待播放状态确认 + self.pendingPlayCompletion = completion + + // 设置超时检查 + self.playVerificationTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { _ in + if let pendingCompletion = self.pendingPlayCompletion { + self.pendingPlayCompletion = nil + // 检查当前播放状态 + let currentState = self.currentPlayState + if currentState == .playing { + pendingCompletion(true, nil) + print("QQ音乐播放列表验证成功,共\(mids.count)首歌曲") + } else { + // 播放验证失败,尝试重试 + if retryCount < maxRetries { + print("QQ音乐播放验证失败,第\(retryCount + 1)次重试...") + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.playSongsWithRetry(mids: mids, index: index, retryCount: retryCount + 1, maxRetries: maxRetries, completion: completion) + } + } else { + pendingCompletion(false, "播放验证超时,已重试\(maxRetries)次,当前状态: \(currentState.rawValue)") + } + } + } + } } else { - let errorMessage = "QQ音乐播放列表启动失败,错误码: \(errorCode)" - completion(false, errorMessage) - print(errorMessage) + // SDK调用失败,尝试重试 + if retryCount < maxRetries { + let errorMessage = "QQ音乐播放列表启动失败,错误码: \(errorCode),第\(retryCount + 1)次重试..." + print(errorMessage) + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.playSongsWithRetry(mids: mids, index: index, retryCount: retryCount + 1, maxRetries: maxRetries, completion: completion) + } + } else { + let errorMessage = "QQ音乐播放列表启动失败,已重试\(maxRetries)次,错误码: \(errorCode)" + completion(false, errorMessage) + print(errorMessage) + } } } } onQQMusicNotInstalled: { @@ -522,7 +558,6 @@ import QPlayAutoSDK completion(false, "登录失败,无法播放歌曲列表") } } - @objc public func next() { ensureQQMusicInstalledAndLoginAndExecute { QPlayAutoSDK.playerPlayNext { errorCode in @@ -803,6 +838,19 @@ extension QPlayAutoManager: QPlayAutoSDKDelegate { for weakDelegate in delegates { weakDelegate.delegate?.onPlayStateChanged?(playState, song: song, position: position) } + // 检查是否有待验证的播放请求 + if let pendingCompletion = pendingPlayCompletion { + if playState == .playing { + pendingPlayCompletion = nil + playVerificationTimer?.invalidate() + pendingCompletion(true, nil) + print("QQ音乐播放状态确认:真正开始播放") + } else { + pendingPlayCompletion = nil + playVerificationTimer?.invalidate() + pendingCompletion(false, "播放失败,歌曲可能不可用") + } + } } public func onQPlayAutoPlayProgressChanged(_ song: QPlayAutoListItem, progress: TimeInterval, duration: TimeInterval) {