From 529220abbfe77c3f8ff5f5d0d29ddd6f1fef1bcd Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 27 Mar 2025 10:04:35 +0800 Subject: [PATCH 01/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9ai=E5=9B=9E=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/translations/language/en_us.dart | 4 ++-- lib/core/translations/language/zh_cn.dart | 4 ++-- lib/data/providers/agent_provider.dart | 8 +++----- lib/data/services/deapsound_ai_service.dart | 6 +++--- lib/modules/chat/controllers/chat_controller.dart | 10 +++++----- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart index 35e5f41b5..d109f357d 100644 --- a/lib/core/translations/language/en_us.dart +++ b/lib/core/translations/language/en_us.dart @@ -99,8 +99,8 @@ const Map enUS = { "simultaneousTranslationInfo": "Instant translation for 80+ languages", "aiGirlfriend": "Parent-child bondingn", "aiGirlfriendInfo": "Intelligent company, chat at any time", - "healthConsultant": "Health Consultant", - "healthConsultantInfo": "Provides scientific health advice", + "healthConsultant": "Multi-function AI chat", + "healthConsultantInfo": "More AI chat features", "conferenceAssistant": "Business Meeting Assistant", "conferenceAssistantInfo": "Real-time minutes recording and multilingual translation", diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart index 7a0e0ad13..ba7674fd9 100644 --- a/lib/core/translations/language/zh_cn.dart +++ b/lib/core/translations/language/zh_cn.dart @@ -98,8 +98,8 @@ const Map zhCN = { "simultaneousTranslationInfo": "实时翻译80+种语言", "aiGirlfriend": "亲子陪伴", "aiGirlfriendInfo": "智能陪伴,随时聊天", - "healthConsultant": "健康顾问", - "healthConsultantInfo": "提供科学健康建议", + "healthConsultant": "多功能AI聊天", + "healthConsultantInfo": "提供更多AI聊天功能", "conferenceAssistant": "商务会议助手", "conferenceAssistantInfo": "实时记录会议纪要,多语言翻译", "studyAssistant": "语言学习助手", diff --git a/lib/data/providers/agent_provider.dart b/lib/data/providers/agent_provider.dart index fa3cc2fe0..25b9d94f0 100644 --- a/lib/data/providers/agent_provider.dart +++ b/lib/data/providers/agent_provider.dart @@ -130,15 +130,13 @@ class AgentProvider { '''), Agent( id: 'health_advisor', - name: '健康顾问 吴小江博士', + name: '多功能AI聊天 吴小江', avatarUrl: 'assets/images/agents/doctor.png', - description: - '我是吴博士,拥有丰富的健康管理经验。我可以为你提供科学的健康建议、生活方式指导和疾病预防知识,帮助你实现身心健康的平衡。', + description: '我是吴小江,我可以为你提供更多AI聊天功能。', tag: '健康', isOnline: true, voice: "zh_male_shaonianzixin_moon_bigtts", - welcomeMessage: - "您好,我是李医生,很高兴能为您提供健康咨询服务。健康是人生最宝贵的财富,我将用专业知识帮助您维护和改善健康状况。请问有什么健康问题想咨询吗?", + welcomeMessage: "您好,我是吴小江,很高兴能为您提供多功能AI聊天。我将用专业知识帮助您。请问有什么问题想咨询吗?", systemPrompt: ''' 你是一位名叫李医生的健康顾问,拥有丰富的医学知识和健康管理经验。 diff --git a/lib/data/services/deapsound_ai_service.dart b/lib/data/services/deapsound_ai_service.dart index f2d7c6e02..6b121af4f 100644 --- a/lib/data/services/deapsound_ai_service.dart +++ b/lib/data/services/deapsound_ai_service.dart @@ -20,7 +20,7 @@ class DeapsoundAIService implements AiService { required String systemPrompt, }) async* { final client = http.Client(); - final url = Uri.parse('https://www.deapsound.com/ai/chat'); + final url = Uri.parse('https://www.deapsound.com/ai/funcchat'); final headers = { 'Content-Type': 'application/json', @@ -51,8 +51,8 @@ class DeapsoundAIService implements AiService { if (data['event'] == 'workflow_finished') { break; } - if (data['event'] == 'text_chunk') { - String content = data['data']['text'] ?? ''; + if (data['content'] != null) { + String content = data['content'] ?? ''; if (content.isNotEmpty) { yield content; } diff --git a/lib/modules/chat/controllers/chat_controller.dart b/lib/modules/chat/controllers/chat_controller.dart index 7a25dcab7..4d0457c98 100644 --- a/lib/modules/chat/controllers/chat_controller.dart +++ b/lib/modules/chat/controllers/chat_controller.dart @@ -93,16 +93,16 @@ class ChatController extends GetxController { agent = foundAgent; switch (agent.id) { - case 'personal_assistant': - // 全能助手 - Get.put(DeapsoundAIService()); - _aiService = Get.find(); - break; case 'cyber_girlfriend': // 亲子陪伴 Get.put(KouziAIService()); _aiService = Get.find(); break; + case 'health_advisor': + // 多功能AI聊天 + Get.put(DeapsoundAIService()); + _aiService = Get.find(); + break; default: _aiService = Get.find(); } From 4a3a2881c2b8d1c164c7c53861835ed645c7b280 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 27 Mar 2025 11:06:10 +0800 Subject: [PATCH 02/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9iOS=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 9 ++++++--- ios/Runner/Info.plist | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index afed04887..cb604b243 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -523,7 +523,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CURRENT_PROJECT_VERSION = 23; DEVELOPMENT_TEAM = 42C82SZUVX; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -532,6 +532,7 @@ "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.yunqiinnovation.deepsound; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -723,7 +724,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CURRENT_PROJECT_VERSION = 23; DEVELOPMENT_TEAM = 42C82SZUVX; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -732,6 +733,7 @@ "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.yunqiinnovation.deepsound; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -748,7 +750,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CURRENT_PROJECT_VERSION = 23; DEVELOPMENT_TEAM = 42C82SZUVX; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -757,6 +759,7 @@ "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.yunqiinnovation.deepsound; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index aea2275af..05857d848 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + ITSAppUsesNonExemptEncryption + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion From c479900dd54372f3d839f88002270ee71dea6a64 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Thu, 27 Mar 2025 11:56:00 +0800 Subject: [PATCH 03/12] =?UTF-8?q?=E4=B8=80=E7=82=B9=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/login/controllers/login_controller.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart index f3057192e..3d92d3c85 100644 --- a/lib/modules/login/controllers/login_controller.dart +++ b/lib/modules/login/controllers/login_controller.dart @@ -21,8 +21,8 @@ class LoginController extends GetxController { final GetStorage _storage = GetStorage(); // 添加国家编码相关状态 - final _selectedCountryCode = '+86'.obs; - final _selectedCountryFlag = '🇨🇳'.obs; + final _selectedCountryCode = ''.obs; + final _selectedCountryFlag = '✉️'.obs; // 国家编码 Getter String get selectedCountryCode => _selectedCountryCode.value; String get selectedCountryFlag => _selectedCountryFlag.value; @@ -32,6 +32,8 @@ class LoginController extends GetxController { {'name': '中国', 'code': '+86', 'flag': '🇨🇳'}, // 保持第二位 // 按国家代码数字升序排列(去除+号后排序) + {'name': '美国', 'code': '+1', 'flag': '🇺🇸'}, + {'name': '加拿大', 'code': '+1', 'flag': '🇨🇦'}, {'name': '俄罗斯', 'code': '+7', 'flag': '🇷🇺'}, {'name': '英国', 'code': '+44', 'flag': '🇬🇧'}, {'name': '德国', 'code': '+49', 'flag': '🇩🇪'}, @@ -52,8 +54,6 @@ class LoginController extends GetxController { {'name': '菲律宾', 'code': '+63', 'flag': '🇵🇭'}, {'name': '印度尼西亚', 'code': '+62', 'flag': '🇮🇩'}, {'name': '阿联酋', 'code': '+971', 'flag': '🇦🇪'}, - {'name': '美国', 'code': '+1', 'flag': '🇺🇸'}, - {'name': '加拿大', 'code': '+1', 'flag': '🇨🇦'}, {'name': '墨西哥', 'code': '+52', 'flag': '🇲🇽'}, {'name': '巴西', 'code': '+55', 'flag': '🇧🇷'}, {'name': '阿根廷', 'code': '+54', 'flag': '🇦🇷'}, @@ -104,7 +104,7 @@ class LoginController extends GetxController { _selectedCountryCode.value = code; _selectedCountryFlag.value = flag; // 根据选择更新输入框类型 - if (code == '@') { + if (code == '') { contactController.clear(); // 清空当前输入 loginType = 0; // 设置为邮箱登录类型 } else { @@ -246,6 +246,7 @@ class LoginController extends GetxController { // 为了演示,我们只是模拟发送 if (isEmialContactController() || isPhoneContactController()) { _startCountdown(); + print(contactController.text); var response = await Api.getCode({ "addr": selectedCountryCode + contactController.text, "vtype": loginType, From 2d45dfe476dde12830ef3e2f6c15689c647b4251 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 27 Mar 2025 11:56:13 +0800 Subject: [PATCH 04/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BC=9A=E8=AE=AE?= =?UTF-8?q?=E5=8A=A9=E6=89=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting_details_controller.dart | 3 + .../controllers/meeting_edit_controller.dart | 58 +++++++++++++++---- .../meeting/views/meeting_edit_view.dart | 8 +-- 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_details_controller.dart b/lib/modules/meeting/controllers/meeting_details_controller.dart index ee4668196..d88b53f28 100644 --- a/lib/modules/meeting/controllers/meeting_details_controller.dart +++ b/lib/modules/meeting/controllers/meeting_details_controller.dart @@ -15,6 +15,8 @@ class MeetingDetailsController extends GetxController Rx meetingData = MeetingModel().obs; Rx dataDetails = MeetingDetailsModel().obs; + int seconds = 0; + RxBool isPlay = false.obs; @override @@ -36,6 +38,7 @@ class MeetingDetailsController extends GetxController final data = await SqfliteApi.getMeetingDetails(id); meetingData.value = MeetingModel.fromJson(data); dataDetails.value = MeetingDetailsModel.fromJson(data['details']); + seconds = meetingData.value.seconds; isLoading.value = false; } diff --git a/lib/modules/meeting/controllers/meeting_edit_controller.dart b/lib/modules/meeting/controllers/meeting_edit_controller.dart index 9dc2fe89a..d24308441 100644 --- a/lib/modules/meeting/controllers/meeting_edit_controller.dart +++ b/lib/modules/meeting/controllers/meeting_edit_controller.dart @@ -2,21 +2,23 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../data/services/db/sqflite_api.dart'; -import '../model/meeting_details_model.dart'; -import '../model/meeting_model.dart'; +import 'meeting_controller.dart'; +import 'meeting_details_controller.dart'; class MeetingEditController extends GetxController with GetSingleTickerProviderStateMixin { + final MeetingController meetingController = Get.find(); + final MeetingDetailsController meetingDetailsController = + Get.find(); + late AnimationController lottieController; late TextEditingController titleController; late TextEditingController contentController; - int id = 0; + int creationTime = 0; - RxBool isLoading = true.obs; - Rx meetingData = MeetingModel().obs; - Rx dataDetails = MeetingDetailsModel().obs; + int id = 0; RxBool isPlay = false.obs; @@ -38,16 +40,13 @@ class MeetingEditController extends GetxController } void _getDataDetails() async { - final data = await SqfliteApi.getMeetingDetails(id); - meetingData.value = MeetingModel.fromJson(data); - dataDetails.value = MeetingDetailsModel.fromJson(data['details']); titleController = TextEditingController( - text: meetingData.value.title, + text: meetingDetailsController.meetingData.value.title, ); contentController = TextEditingController( - text: dataDetails.value.content, + text: meetingDetailsController.dataDetails.value.content, ); - isLoading.value = false; + creationTime = meetingDetailsController.meetingData.value.creationtime; } void start() { @@ -59,4 +58,39 @@ class MeetingEditController extends GetxController isPlay.value = true; } } + + void confirm() async { + String content = contentController.text; + String firstContent = + content.length >= 20 ? content.substring(0, 20) : content; + String title = titleController.text.isNotEmpty + ? titleController.text + : meetingDetailsController.meetingData.value.title; + Map data = { + 'title': title, + 'firstContent': firstContent, + 'seconds': meetingDetailsController.seconds, + 'content': contentController.text, + }; + final result = await SqfliteApi.editMeeting(id, data); + if (result != null) { + int index = meetingController.dataList.indexWhere( + (item) => item['id'] == id, + ); + if (index != -1) { + final dataMap = + Map.from(meetingController.dataList[index]); + dataMap['title'] = title; + dataMap['content'] = firstContent; + dataMap['seconds'] = meetingDetailsController.seconds; + meetingController.dataList[index] = dataMap; + } + meetingDetailsController.meetingData.value.title = title; + meetingDetailsController.dataDetails.value.content = + contentController.text; + meetingDetailsController.meetingData.refresh(); + meetingDetailsController.dataDetails.refresh(); + Get.back(); + } + } } diff --git a/lib/modules/meeting/views/meeting_edit_view.dart b/lib/modules/meeting/views/meeting_edit_view.dart index 3e6f6852c..b85e95c7f 100644 --- a/lib/modules/meeting/views/meeting_edit_view.dart +++ b/lib/modules/meeting/views/meeting_edit_view.dart @@ -17,9 +17,7 @@ class MeetingEditView extends GetView { return Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], appBar: _buildAppBar(isDarkMode), - body: Obx(() => !controller.isLoading.value - ? _buildBody(isDarkMode) - : const SizedBox()), + body: _buildBody(isDarkMode), ); } @@ -38,7 +36,7 @@ class MeetingEditView extends GetView { ), actions: [ GestureDetector( - onTap: () {}, + onTap: controller.confirm, child: Padding( padding: EdgeInsets.symmetric( horizontal: 12.w, @@ -105,7 +103,7 @@ class MeetingEditView extends GetView { padding: EdgeInsets.symmetric(vertical: 5.w), child: Text( DateUtil.formatDateMs( - controller.meetingData.value.creationtime, + controller.creationTime, format: DateFormats.y_mo_d_h_m, ), style: TextStyle( From a23d79315449090801320c7505ed762a444fa4b1 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Thu, 27 Mar 2025 14:18:24 +0800 Subject: [PATCH 05/12] =?UTF-8?q?=E5=8A=A0=E5=85=A5google,fackbook?= =?UTF-8?q?=E7=99=BB=E9=99=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 44 +++---- ios/Runner.xcodeproj/project.pbxproj | 21 +++ .../xcshareddata/swiftpm/Package.resolved | 123 ++++++++++++++++++ ios/Runner/GoogleService-Info.plist | 36 +++++ ios/Runner/Info.plist | 26 ++++ ios/Runner/RunnerRelease.entitlements | 10 ++ 6 files changed, 238 insertions(+), 22 deletions(-) create mode 100644 ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 ios/Runner/GoogleService-Info.plist create mode 100644 ios/Runner/RunnerRelease.entitlements diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 33793d61b..cc790be8d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -264,18 +264,18 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite_darwin/darwin" SPEC CHECKSUMS: - app_settings: 58017cd26b604ae98c3e65acbdd8ba173703cc82 + app_settings: 5127ae0678de1dcc19f2293271c51d37c89428b2 AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 - audio_session: 19e9480dbdd4e5f6c4543826b2e8b0e4ab6145fe - audio_waveforms: cd736909ebc6b6a164eb74701d8c705ce3241e1c - device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d + audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0 + audio_waveforms: a6dde7fe7c0ea05f06ffbdb0f7c1b2b2ba6cedcf + device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 FBAEMKit: cb719c53575a3be86ea873279f30d6a2c4e15881 FBSDKCoreKit: ecdb980a24633ccb012700299ceb16d0235e14d2 FBSDKCoreKit_Basics: 045101c4a9ef10c845347424d73a29aae02c3e43 FBSDKLoginKit: 69eb59b2f839aba635616df6e422acd0ca88030a Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf - firebase_auth: 3d848b9b866b201e5c8e0c06d8b2cec272fd8825 - firebase_core: ac395f994af4e28f6a38b59e05a88ca57abeb874 + firebase_auth: 9ebbd83276bf977dea1c74dfc199acf9d2a2f42f + firebase_core: 8d552814f6c01ccde5d88939fced4ec26f2f5510 FirebaseAppCheckInterop: 9664c858489710f682766ef54e2b6741d3b62070 FirebaseAuth: ad59a1a7b161e75f74c39f70179d2482d40e2737 FirebaseAuthInterop: 01a804fb074424fd58b92dd50dd0272277199356 @@ -283,32 +283,32 @@ SPEC CHECKSUMS: FirebaseCoreExtension: 3d3f2017a00d06e09ab4ebe065391b0bb642565e FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_blue_plus_darwin: 3ea4ec9133b377febcc8a70b28cd2d2dc9242bd9 - flutter_facebook_auth: 48be46091bd1a5cc5a66055f1228a9d99a28bfaa - flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e - flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 - flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 - flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d - fluwx: daa284756ce53442b3d0417ceeda66e981906811 - google_sign_in_ios: 4111e87aa5e24a4404f00ea13479f35e571969cc + flutter_blue_plus_darwin: 09444a26fb6bdef523e55b68fc1c59af5a877ea6 + flutter_facebook_auth: 8a9ad5fbfc25b77e7a53ea8986866950b27edcae + flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1 + flutter_local_notifications: 395056b3175ba4f08480a7c5de30cd36d69827e4 + flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 + flutter_tts: b88dbc8655d3dc961bc4a796e4e16a4cc1795833 + fluwx: 6bf9c5a3a99ad31b0de137dd92370a0d10a60f4b + google_sign_in_ios: 19297361f2c51d7d8ac0201b866ef1fa5d1f94a8 GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 - just_audio: a42c63806f16995daf5b219ae1d679deb76e6a79 + image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a + just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d MicrosoftCognitiveServicesSpeech-iOS: 723eccb3e459bbb530df3d9bfe84a291074b09eb - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 - record_darwin: 3b1a8e7d5c0cbf45ad6165b4d83a6ca643d929c3 + record_darwin: fb1f375f1d9603714f55b8708a903bbb91ffdb0a SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 - share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f - speech_to_text: 627d3fd2194770b51abb324ba45c2d39398f24a8 - sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a + speech_to_text: 9dc43a5df3cbc2813f8c7cc9bd0fbf94268ed7ac + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 Try: 5ef669ae832617b3cee58cb2c6f99fb767a4ff96 WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index cb604b243..b46ef17e1 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3406320CAE3314A73EF78043 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BBE10AD7954527B6BB12CFB /* Pods_RunnerTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 591293932D9524D1004A65BB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 591293922D9524D1004A65BB /* GoogleService-Info.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -68,6 +69,8 @@ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 4A9190D0838960F143BDC158 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5912938C2D9520CE004A65BB /* RunnerRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerRelease.entitlements; sourceTree = ""; }; + 591293922D9524D1004A65BB /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 5BBE10AD7954527B6BB12CFB /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -161,6 +164,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 5912938C2D9520CE004A65BB /* RunnerRelease.entitlements */, 0A7A32E22D8DBA6D0047F746 /* VoiceInteractionService.swift */, 0A7A32E32D8DBA6D0047F746 /* VolcanoAIService.swift */, 0ADE296F2D8C6C450032BFCB /* AudioSessionManager.swift */, @@ -171,6 +175,7 @@ 0A5EDC032D8B544100215E26 /* BluetoothMediaButtonHelper.swift */, 0A5EDBFB2D89C4FD00215E26 /* ClassicBluetoothHelper.swift */, 97C146FA1CF9000F007C117D /* Main.storyboard */, + 591293922D9524D1004A65BB /* GoogleService-Info.plist */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, @@ -265,6 +270,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 5912938D2D95215F004A65BB /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -293,6 +301,7 @@ 0ADE29692D8C25660032BFCB /* silence.mp3 in Resources */, 0ADE296A2D8C25660032BFCB /* sample.mp3 in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + 591293932D9524D1004A65BB /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -750,6 +759,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements; CURRENT_PROJECT_VERSION = 23; DEVELOPMENT_TEAM = 42C82SZUVX; ENABLE_BITCODE = NO; @@ -802,6 +812,17 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 5912938D2D95215F004A65BB /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.10.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..7ee26c6b2 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,123 @@ +{ + "originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329", + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", + "version" : "1.2024072200.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "2e4a4daac9da51f79c131e76cb47e785dd7708c4", + "version" : "11.10.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "defe1970a9099d47765592ebd7ac5f5e9fdb57c6", + "version" : "11.10.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", + "version" : "8.0.2" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71", + "version" : "1.69.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35", + "version" : "4.4.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", + "version" : "1.29.0" + } + } + ], + "version" : 3 +} diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 000000000..c90ada3a3 --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,36 @@ + + + + + CLIENT_ID + 907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278 + ANDROID_CLIENT_ID + 907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com + API_KEY + AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808 + GCM_SENDER_ID + 907754492821 + PLIST_VERSION + 1 + BUNDLE_ID + com.yunqiinnovation.deepsound + PROJECT_ID + deepsound-e0e3d + STORAGE_BUCKET + deepsound-e0e3d.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:907754492821:ios:246794fc44b23174145c9c + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 05857d848..560e6c858 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,32 @@ +CFBundleURLTypes + + + CFBundleURLSchemes + + fb3829022037361855 + com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278 + + + +FacebookAppID +3829022037361855 +FacebookClientToken +193d0a2b1757c41cfa557ea8e8f21c33 +FacebookDisplayName +Deepsound + +LSApplicationQueriesSchemes + + fbapi + fb-messenger-api + fbauth2 + fbshareextension + googlechrome + googlechromes + ITSAppUsesNonExemptEncryption CADisableMinimumFrameDurationOnPhone diff --git a/ios/Runner/RunnerRelease.entitlements b/ios/Runner/RunnerRelease.entitlements new file mode 100644 index 000000000..62a4a38c3 --- /dev/null +++ b/ios/Runner/RunnerRelease.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.yunqiinnovation.deepsound + + + From ae57be2cafdbe60570f7c349ad674840c5ce9dab Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Thu, 27 Mar 2025 16:23:27 +0800 Subject: [PATCH 06/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8DGoogle=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=8A=A5=E9=94=99=EF=BC=8C=E8=BF=9B=E5=85=A5=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/swiftpm/Package.resolved | 123 ++++++++++++++++++ .../login/controllers/googleLogin.dart | 7 +- .../controllers/settings_controller.dart | 1 + .../splash/controllers/splash_controller.dart | 8 +- 4 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..7ee26c6b2 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,123 @@ +{ + "originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329", + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", + "version" : "1.2024072200.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "2e4a4daac9da51f79c131e76cb47e785dd7708c4", + "version" : "11.10.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "defe1970a9099d47765592ebd7ac5f5e9fdb57c6", + "version" : "11.10.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", + "version" : "8.0.2" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71", + "version" : "1.69.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35", + "version" : "4.4.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", + "version" : "1.29.0" + } + } + ], + "version" : 3 +} diff --git a/lib/modules/login/controllers/googleLogin.dart b/lib/modules/login/controllers/googleLogin.dart index 196699b15..548cbd792 100644 --- a/lib/modules/login/controllers/googleLogin.dart +++ b/lib/modules/login/controllers/googleLogin.dart @@ -33,6 +33,8 @@ class Google { return true; } else { UserCredential userCredential = await loginGoogle(); + token = await userCredential.user?.getIdToken(); + print('Future响应数据tokenResult: $token'); if (token != "") { return true; } else { @@ -61,10 +63,7 @@ class Google { // Obtain the auth details from the request final GoogleSignInAuthentication? googleAuth = await googleUser?.authentication; - accessToken = googleAuth?.accessToken; - token = googleAuth?.idToken; - print('loginGoogle:googleAuth?.idToken= ${token}'); - print('loginGoogle:googleAuth?.accessToken= ${accessToken}'); + // Create a new credential, final credential = GoogleAuthProvider.credential( accessToken: googleAuth?.accessToken, diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index a1f1890b1..8d5069816 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -225,6 +225,7 @@ class SettingsController extends GetxController { void signOut() { // 在此实现退出登录逻辑 _storage.write("logintoken", ""); + _storage.write("user_info", ""); Get.offAllNamed('/login'); } } diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index c52535c3d..a26016bb7 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -76,10 +76,12 @@ class SplashController extends GetxController { // 获取登录Token并跳转 Future _navigateToLogin() async { - final isLoggedIn = await getLoginToken(); + bool isLoggedIn = false; + final token = _storage.read('logintoken'); + if (token != "") { + isLoggedIn = await getLoginToken(); + } if (isLoggedIn) { - // Get.offAllNamed(Routes.login); // 跳转到登录页 - // Get.offAllNamed(Routes.home); // 跳转到主页 Get.offAllNamed(Routes.pairing); } else { Get.offAllNamed(Routes.login); // 跳转到登录页 From a5c063276dcd1cb48aa7000570e6dbfdda968220 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 27 Mar 2025 17:44:30 +0800 Subject: [PATCH 07/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9ai=E5=9B=9E=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/deapsound_ai_service.dart | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/data/services/deapsound_ai_service.dart b/lib/data/services/deapsound_ai_service.dart index 6b121af4f..5036923b9 100644 --- a/lib/data/services/deapsound_ai_service.dart +++ b/lib/data/services/deapsound_ai_service.dart @@ -22,14 +22,25 @@ class DeapsoundAIService implements AiService { final client = http.Client(); final url = Uri.parse('https://www.deapsound.com/ai/funcchat'); + List contextsList = []; + String msg = ''; + for (var i = 0; i < messages.length; i++) { + Map item = messages[i]; + if (i < messages.length - 1) { + if (item['role'] == 'user') { + contextsList.add(item['content']); + } + } else { + msg = item['content']; + } + } + final headers = { 'Content-Type': 'application/json', 'Authorization': apiKey, }; - final requestBody = { - "msg": messages.last['content'], - }; + final requestBody = {"contexts": contextsList, "msg": msg}; try { final request = http.Request('POST', url) From d6e0893cb99c74b235e96a12986fc5e722276152 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Thu, 27 Mar 2025 17:59:25 +0800 Subject: [PATCH 08/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dgoogle=EF=BC=8CFacebook?= =?UTF-8?q?=E5=B8=90=E5=8F=B7=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/controllers/settings_controller.dart | 9 ++++++++- lib/modules/splash/controllers/splash_controller.dart | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 8d5069816..9fe723ee2 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -8,6 +8,8 @@ import '../../../data/services/speech_factory.dart'; import '../models/language_model.dart'; //import '../../../data/models/user_model.dart'; import '../../../data/models/user_Info.dart'; +import '../../../modules/login/controllers/googleLogin.dart'; +import '../../../modules/login/controllers/facebookLogin.dart'; class SettingsController extends GetxController { // 语言设置 @@ -222,10 +224,15 @@ class SettingsController extends GetxController { targetLanguage.value = temp; } - void signOut() { + Future signOut() async { // 在此实现退出登录逻辑 _storage.write("logintoken", ""); _storage.write("user_info", ""); + final google = Google(); + final facebook = Facebook(); + + await google.signOutGoogle(); + await facebook.signOutFacebook(); Get.offAllNamed('/login'); } } diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index a26016bb7..b5dd9d823 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -78,7 +78,8 @@ class SplashController extends GetxController { Future _navigateToLogin() async { bool isLoggedIn = false; final token = _storage.read('logintoken'); - if (token != "") { + print('getLoginToken:进来了${token}'); + if (token != null && token.isNotEmpty) { isLoggedIn = await getLoginToken(); } if (isLoggedIn) { From 51cf8593f98953f052114fde1a60ffdf6544c62d Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 28 Mar 2025 13:44:19 +0800 Subject: [PATCH 09/12] =?UTF-8?q?facebook=E7=99=BB=E5=BD=95=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner/Info.plist | 4 + .../login/controllers/facebookLogin.dart | 106 ++++++++++++++---- 2 files changed, 88 insertions(+), 22 deletions(-) diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 560e6c858..b44af52ee 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -5,6 +5,8 @@ CFBundleURLTypes + CFBundleURLName + com.yunqiinnovation.deepsound CFBundleURLSchemes fb3829022037361855 @@ -38,6 +40,8 @@ DeepSound CFBundleExecutable $(EXECUTABLE_NAME) +NSUserTrackingUsageDescription + This application needs to access your tracking data in order to offer better personalized services and advertisements CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion diff --git a/lib/modules/login/controllers/facebookLogin.dart b/lib/modules/login/controllers/facebookLogin.dart index d73cc7f87..61e17a4a8 100644 --- a/lib/modules/login/controllers/facebookLogin.dart +++ b/lib/modules/login/controllers/facebookLogin.dart @@ -1,6 +1,13 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; import 'package:get/get.dart'; +import 'package:crypto/crypto.dart'; class Facebook { String token = ''; @@ -9,35 +16,90 @@ class Facebook { String email = ''; String pictureurl = ''; String id = ''; + String generateNonce([int length = 32]) { + const charset = + '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; + final random = Random.secure(); + return List.generate(length, (_) => charset[random.nextInt(charset.length)]) + .join(); + } + + /// Returns the sha256 hash of [input] in hex notation. + String sha256ofString(String input) { + final bytes = utf8.encode(input); + final digest = sha256.convert(bytes); + return digest.toString(); + } // FaceBook登录 // 修改后的 signInWithFacebook Future signInWithFacebook() async { try { - print("开始 Facebook 登录"); - final LoginResult result = await FacebookAuth.instance.login(); - print("Facebook 登录结果:$result"); - - if (result.status == LoginStatus.success) { - token = result.accessToken!.tokenString; - print("Facebook Token: $token"); - - final credential = FacebookAuthProvider.credential(token); - print("Facebook Token: $credential"); - final userData = await FacebookAuth.instance.getUserData(); - name = userData["name"].toString(); - - email = userData["email"].toString(); - pictureurl = userData["picture"]["data"]["url"].toString(); - id = userData["id"].toString(); - print("user login facebook ${userData..values}"); - print("user login facebook ${email}${pictureurl}${name}${id}"); - - return true; + final rawNonce = generateNonce(); + final nonce = sha256ofString(rawNonce); + final LoginResult loginResult = await FacebookAuth.instance + .login( + loginTracking: LoginTracking.enabled, + nonce: nonce, + ) + .catchError((onError) { + if (kDebugMode) { + //print(onError); + } + throw Exception(onError.message); + }); + if (loginResult.accessToken == null) { + throw Exception(loginResult.message); + } + // Create a credential from the access token + OAuthCredential facebookAuthCredential; + + print("tokenType${loginResult.accessToken!.type}"); + + if (Platform.isIOS) { + // Create a credential from the access token + switch (loginResult.accessToken!.type) { + case AccessTokenType.classic: + final classicToken = loginResult.accessToken as ClassicToken; + token = classicToken.tokenString; + print('Token 字符串: ${classicToken.tokenString}'); + print('classicToken值: ${classicToken}'); + facebookAuthCredential = FacebookAuthProvider.credential( + classicToken.authenticationToken!, + ); + break; + case AccessTokenType.limited: + final limitedToken = loginResult.accessToken as LimitedToken; + token = limitedToken.tokenString; + print('Token 字符串: ${limitedToken.tokenString}'); + print('LimitedToken值: ${limitedToken}'); + facebookAuthCredential = OAuthCredential( + providerId: 'facebook.com', + signInMethod: 'oauth', + idToken: limitedToken.tokenString, + rawNonce: rawNonce, + ); + break; + } } else { - print("登录失败,状态:${result.status},消息:${result.message}"); - return false; + facebookAuthCredential = FacebookAuthProvider.credential( + loginResult.accessToken!.tokenString, + ); + token = loginResult.accessToken!.tokenString; + } + print('facebookAuthCredential值: ${facebookAuthCredential}'); + if (facebookAuthCredential == null) { + throw Exception("Facebook Login Fail."); } + final userData = await FacebookAuth.instance.getUserData(); + name = userData["name"].toString(); + + email = userData["email"].toString(); + pictureurl = userData["picture"]["data"]["url"].toString(); + id = userData["id"].toString(); + print("user login facebook ${email}${pictureurl}${name}${id}"); + + return true; } catch (e) { print("Facebook 登录异常:$e"); return false; From a11d3682f8b55a20910329da6c9d130d4d30a589 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Fri, 28 Mar 2025 15:35:27 +0800 Subject: [PATCH 10/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BC=9A=E8=AE=AE?= =?UTF-8?q?=E5=8A=A9=E6=89=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/asr_service.dart | 54 ++-- .../speech_impl/xunfei_asr_service.dart | 270 ++++++++++++++++++ .../controllers/meeting_edit_controller.dart | 6 +- .../meeting_record_controller.dart | 50 +++- .../meeting/views/meeting_edit_view.dart | 4 +- 5 files changed, 348 insertions(+), 36 deletions(-) create mode 100644 lib/data/services/speech_impl/xunfei_asr_service.dart diff --git a/lib/data/services/asr_service.dart b/lib/data/services/asr_service.dart index 8e18e3433..02c25283e 100644 --- a/lib/data/services/asr_service.dart +++ b/lib/data/services/asr_service.dart @@ -4,28 +4,28 @@ import 'dart:async'; abstract class AsrService { /// 支持的语言 List get supportedLanguages; - + /// 初始化语音识别服务 Future initialize({ List? supportedLanguages, }); - + /// 执行一次性语音识别 - /// + /// /// 返回识别的最终结果 Future recognizeOnce(); - + /// 开始连续语音识别 - /// + /// /// 返回一个包含识别事件的流 Future> startContinuousRecognition(); - + /// 停止连续语音识别 Future stopContinuousRecognition(); - + /// 检查连续识别是否活跃 bool isContinuousRecognitionActive(); - + /// 释放资源 Future dispose(); } @@ -34,19 +34,19 @@ abstract class AsrService { enum RecognitionEventType { /// 最终识别结果 finalResult, - + /// 中间识别结果(实时反馈) intermediateResult, - + /// 会话开始 sessionStarted, - + /// 会话结束 sessionStopped, - + /// 识别取消 canceled, - + /// 识别错误 error, } @@ -55,23 +55,27 @@ enum RecognitionEventType { class RecognitionEvent { /// 事件类型 final RecognitionEventType type; - + /// 识别文本(仅在 finalResult 和 intermediateResult 类型中有效) final String text; - + /// 检测到的语言 final String detectedLanguage; - + + /// 角色 + final String role; + /// 错误信息(仅在 error 和 canceled 类型中有效) final String error; - + RecognitionEvent({ required this.type, this.text = '', this.detectedLanguage = '', + this.role = '', this.error = '', }); - + /// 创建最终结果事件的快捷构造函数 factory RecognitionEvent.finalResult({ required String text, @@ -83,7 +87,7 @@ class RecognitionEvent { detectedLanguage: detectedLanguage, ); } - + /// 创建错误事件的快捷构造函数 factory RecognitionEvent.error(String errorMessage) { return RecognitionEvent( @@ -91,15 +95,17 @@ class RecognitionEvent { error: errorMessage, ); } - + /// 检查是否为最终结果 bool get isFinalResult => type == RecognitionEventType.finalResult; - + /// 检查是否为错误 - bool get isError => type == RecognitionEventType.error || type == RecognitionEventType.canceled; - + bool get isError => + type == RecognitionEventType.error || + type == RecognitionEventType.canceled; + @override String toString() { return 'RecognitionEvent{type: $type, text: $text, detectedLanguage: $detectedLanguage, error: $error}'; } -} \ No newline at end of file +} diff --git a/lib/data/services/speech_impl/xunfei_asr_service.dart b/lib/data/services/speech_impl/xunfei_asr_service.dart new file mode 100644 index 000000000..94d4d72dc --- /dev/null +++ b/lib/data/services/speech_impl/xunfei_asr_service.dart @@ -0,0 +1,270 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:crypto/crypto.dart'; +import 'package:record/record.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; +import '../../../core/utils/logger.dart'; +import 'package:get/get.dart'; +import '../asr_service.dart'; + +/// 讯飞 语音识别服务 +/// +class XunfeiAsrService extends GetxService implements AsrService { + final String baseUrl = 'wss://rtasr.xfyun.cn/v1/ws'; + final int _chunkSize = 1280; // 每次发送的数据大小 + + final String appId = '43d87d22'; + final String apiKey = '83f063755e8d342670002f50a4471d42'; + + AudioRecorder? recorder; + Timer? _timer; + + WebSocketChannel? _channel; + bool isConnected = false; //是否链接 + + bool _isInitialized = false; + + final List _defaultSupportedLanguages = ['zh-CN']; + String _languageStr = 'cn'; + + // 连续识别相关 + bool _isContinuousRecognitionActive = false; + StreamController? _eventStreamController; + + @override + List get supportedLanguages => _defaultSupportedLanguages; + + Uint8List _buff = Uint8List(0); //音频缓存区 + BytesBuilder _originaBytes = BytesBuilder(); //原始音频 + bool isRecording = false; + + @override + Future initialize({ + List? supportedLanguages, + }) async { + try { + final List languages = + supportedLanguages ?? _defaultSupportedLanguages; + + String languagetoStr = languages.first.split('-')[1].toLowerCase(); + _languageStr = languagetoStr; + + _isInitialized = true; + Logger.info('Azure 语音识别服务初始化成功'); + return true; + } catch (e) { + Logger.error('Azure 语音识别服务初始化失败: $e'); + _isInitialized = false; + rethrow; + } + } + + @override + Future recognizeOnce() async { + if (!_isInitialized) { + await initialize(); + } + + try { + throw Exception('无效的识别结果格式'); + } catch (e) { + Logger.error('语音识别失败: $e'); + return RecognitionEvent.error(e.toString()); + } + } + + @override + Future> startContinuousRecognition() async { + if (!_isInitialized) { + await initialize(); + } + + try { + _eventStreamController = StreamController.broadcast(); + + await recorder?.dispose(); + recorder = AudioRecorder(); + Stream? stream = await recorder?.startStream( + const RecordConfig( + sampleRate: 16000, + encoder: AudioEncoder.pcm16bits, + numChannels: 1, + ), + ); + isRecording = true; + stream?.listen((data) { + if (isRecording) { + _buff = _appendToBuffer(data); + _originaBytes.add(data); + } + }); + await _tryConnecting(); + _timer = Timer.periodic(const Duration(milliseconds: 40), (timer) async { + //每40毫秒读取一次数据 + var frame = _getAudioData(); + if (frame.isNotEmpty) { + _channel?.sink.add(frame); + } + }); + + _isContinuousRecognitionActive = true; + return _eventStreamController!.stream; + } catch (e) { + Logger.error('开始连续语音识别失败: $e'); + rethrow; + } + } + + //写入音频数据到缓存区中 + Uint8List _appendToBuffer(Uint8List newData) { + var newBuffer = Uint8List(_buff.length + newData.length); + newBuffer.setAll(0, _buff); + newBuffer.setAll(_buff.length, newData); + return newBuffer; + } + + //读取缓存区中一帧数据 + Uint8List _getAudioData() { + if (_buff.length >= _chunkSize) { + // 从缓冲区中读取1280字节的数据 + var data = _buff.sublist(0, _chunkSize); + // 移除已读取的数据 + _buff = _buff.sublist(_chunkSize); + return data; + } else { + return _buff; + } + } + + //尝试连接 + Future _tryConnecting() async { + try { + final url = _geturl(); + _channel = WebSocketChannel.connect(Uri.parse(url)); + _channel?.stream.timeout(const Duration(seconds: 10)); //设置超时时间 + await _channel?.ready; + isConnected = true; + _channel?.stream.listen( + (message) { + _handle(message); + }, + onError: (error) { + isConnected = false; + Logger.error('连接失败: $error'); + }, + onDone: () { + isConnected = false; + Logger.error('WebSocket 连接已关闭!'); + }, + cancelOnError: true, + ); + } catch (e) { + Logger.error("初始化异常----------------$e"); + } + } + + //处理数据 + void _handle(data) { + try { + final result = jsonDecode(data); + if (result['code'] == "0" && result['data'] != "") { + final data = jsonDecode(result['data']); + String msg = ""; + String role = ''; + + List rtList = data['cn']['st']['rt']; + for (var rt in rtList) { + for (var ws in rt['ws']) { + for (var ws in ws['cw']) { + msg += ws['w']; + if (ws['rl'] != "0") { + role = ws['rl']; + } + } + } + } + Logger.info( + "识别到的内容 type:${data['cn']['st']['type']} msg:$msg rl:$role"); + if (msg.isNotEmpty) { + if (data['cn']['st']['type'] == "0") { + _eventStreamController?.add(RecognitionEvent( + type: RecognitionEventType.finalResult, + text: msg, + role: role, + )); + } else { + _eventStreamController?.add(RecognitionEvent( + type: RecognitionEventType.intermediateResult, + text: msg, + role: role, + )); + } + } + } + } catch (e) { + Logger.error("接受消息异常 $e"); + } + } + + ///获取链接地址 + String _geturl() { + // 生成时间戳 + String ts = (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); + // 计算 baseString + String baseString = md5.convert(utf8.encode(appId + ts)).toString(); + // 计算 signa + var hmacSha1 = Hmac(sha1, utf8.encode(apiKey)); + var signa = hmacSha1.convert(utf8.encode(baseString)); + String signaBase64 = base64Encode(signa.bytes); + String url = ""; + // 构建 WebSocket URL + url = + '$baseUrl?appid=$appId&ts=$ts&lang=$_languageStr&roleType=2&signa=${Uri.encodeComponent(signaBase64)}'; + return url; + } + + @override + Future stopContinuousRecognition() async { + try { + await recorder?.stop(); + isRecording = false; + _timer?.cancel(); + _buff = Uint8List(0); + _originaBytes = BytesBuilder(); + if (isConnected) { + _channel?.sink.add(utf8.encode('{"end": true}')); //结束 + } + + _isContinuousRecognitionActive = false; + return true; + } catch (e) { + Logger.error('停止连续语音识别失败: $e'); + rethrow; + } + } + + @override + bool isContinuousRecognitionActive() { + return _isContinuousRecognitionActive; + } + + @override + Future dispose() async { + try { + await stopContinuousRecognition(); + recorder?.dispose(); + _channel?.sink.close(); + + _eventStreamController?.close(); + _eventStreamController = null; + + _isInitialized = false; + + Logger.info('Azure 语音识别资源已释放'); + } catch (e) { + Logger.error('释放语音识别资源失败: $e'); + _isInitialized = false; + } + } +} diff --git a/lib/modules/meeting/controllers/meeting_edit_controller.dart b/lib/modules/meeting/controllers/meeting_edit_controller.dart index d24308441..87af7ec73 100644 --- a/lib/modules/meeting/controllers/meeting_edit_controller.dart +++ b/lib/modules/meeting/controllers/meeting_edit_controller.dart @@ -62,13 +62,13 @@ class MeetingEditController extends GetxController void confirm() async { String content = contentController.text; String firstContent = - content.length >= 20 ? content.substring(0, 20) : content; + content.length >= 30 ? content.substring(0, 30) : content; String title = titleController.text.isNotEmpty ? titleController.text : meetingDetailsController.meetingData.value.title; Map data = { 'title': title, - 'firstContent': firstContent, + 'firstContent': firstContent.replaceAll("\n", ""), 'seconds': meetingDetailsController.seconds, 'content': contentController.text, }; @@ -81,7 +81,7 @@ class MeetingEditController extends GetxController final dataMap = Map.from(meetingController.dataList[index]); dataMap['title'] = title; - dataMap['content'] = firstContent; + dataMap['content'] = firstContent.replaceAll("\n", ""); dataMap['seconds'] = meetingDetailsController.seconds; meetingController.dataList[index] = dataMap; } diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index 1e569bfd9..dfd364675 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import '../../../data/services/asr_service.dart'; import '../../../data/services/db/sqflite_api.dart'; +import '../../../data/services/speech_impl/xunfei_asr_service.dart'; import 'meeting_controller.dart'; class MeetingRecordController extends GetxController @@ -31,7 +32,8 @@ class MeetingRecordController extends GetxController void onInit() { super.onInit(); lottieController = AnimationController(vsync: this); - _asrService = Get.find(); + Get.put(XunfeiAsrService()); + _asrService = Get.find(); final Map args = Get.arguments ?? {}; title = args['title']; creationTime = DateTime.now().millisecondsSinceEpoch; @@ -41,7 +43,7 @@ class MeetingRecordController extends GetxController void onClose() { lottieController.dispose(); timer?.cancel(); - _asrService.stopContinuousRecognition(); + _asrService.dispose(); _recognitionSubscription?.cancel(); super.onClose(); } @@ -82,7 +84,6 @@ class MeetingRecordController extends GetxController //结束录音 Future _stopRecorder() async { await _asrService.stopContinuousRecognition(); - _recognitionSubscription?.cancel(); } // 处理语音识别事件 @@ -92,7 +93,7 @@ class MeetingRecordController extends GetxController intermediateContent.value = event.text; break; case RecognitionEventType.finalResult: - finalContent.value += event.text; + finalContent.value += _spliceText(event); intermediateContent.value = ''; break; default: @@ -101,14 +102,49 @@ class MeetingRecordController extends GetxController } } + String _spliceText(RecognitionEvent event) { + String eventText = event.text; + String text = ''; + if (finalContent.value.isNotEmpty) { + if (event.role != '') { + final punctuations = { + ',', + '。', + '!', + '?', + ';', + ':', + ',', + '.', + ':', + ';', + '!', + '?' + }; + final firstChar = eventText[0]; + if (punctuations.contains(firstChar)) { + text += '$firstChar\n'; + eventText = eventText.substring(1); + } else { + text += '\n'; + } + text += '说话人${event.role}:'; + } + } else { + text += '说话人${event.role}:'; + } + text += eventText; + return text; + } + // 完成 void complete() async { - String firstContent = finalContent.value.length >= 20 - ? finalContent.value.substring(0, 20) + String firstContent = finalContent.value.length >= 30 + ? finalContent.value.substring(0, 30) : finalContent.value; Map data = { 'title': title, - 'firstContent': firstContent, + 'firstContent': firstContent.replaceAll("\n", ""), 'seconds': seconds.value, 'creationTime': creationTime, 'audio': [], diff --git a/lib/modules/meeting/views/meeting_edit_view.dart b/lib/modules/meeting/views/meeting_edit_view.dart index b85e95c7f..73de47a76 100644 --- a/lib/modules/meeting/views/meeting_edit_view.dart +++ b/lib/modules/meeting/views/meeting_edit_view.dart @@ -237,14 +237,14 @@ class MeetingEditView extends GetView { controller: controller.contentController, style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, - fontSize: 16.sp, + fontSize: 14.sp, ), maxLines: 50, decoration: InputDecoration( hintText: '编辑记录内容', hintStyle: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black54, - fontSize: 16.sp, + fontSize: 14.sp, ), fillColor: Colors.transparent, border: InputBorder.none, From 18d354166c95b06b4bbeebdaae49bd8afe0039fb Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Fri, 28 Mar 2025 18:15:54 +0800 Subject: [PATCH 11/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BC=9A=E8=AE=AE?= =?UTF-8?q?=E5=8A=A9=E6=89=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/asr_service.dart | 5 ++ .../speech_impl/xunfei_asr_service.dart | 2 + .../meeting_details_controller.dart | 18 +++- .../controllers/meeting_edit_controller.dart | 20 ++++- .../meeting_record_controller.dart | 84 ++++++++++++++++++- .../meeting/views/meeting_details_view.dart | 51 +++++++---- .../meeting/views/meeting_edit_view.dart | 54 ++++++++---- 7 files changed, 198 insertions(+), 36 deletions(-) diff --git a/lib/data/services/asr_service.dart b/lib/data/services/asr_service.dart index 02c25283e..fee73821c 100644 --- a/lib/data/services/asr_service.dart +++ b/lib/data/services/asr_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; /// 语音识别服务接口 abstract class AsrService { @@ -65,6 +66,9 @@ class RecognitionEvent { /// 角色 final String role; + /// 原始音频 + final Uint8List? audio; + /// 错误信息(仅在 error 和 canceled 类型中有效) final String error; @@ -73,6 +77,7 @@ class RecognitionEvent { this.text = '', this.detectedLanguage = '', this.role = '', + this.audio, this.error = '', }); diff --git a/lib/data/services/speech_impl/xunfei_asr_service.dart b/lib/data/services/speech_impl/xunfei_asr_service.dart index 94d4d72dc..1201f7c66 100644 --- a/lib/data/services/speech_impl/xunfei_asr_service.dart +++ b/lib/data/services/speech_impl/xunfei_asr_service.dart @@ -192,7 +192,9 @@ class XunfeiAsrService extends GetxService implements AsrService { type: RecognitionEventType.finalResult, text: msg, role: role, + audio: _originaBytes.toBytes(), )); + _originaBytes.clear(); } else { _eventStreamController?.add(RecognitionEvent( type: RecognitionEventType.intermediateResult, diff --git a/lib/modules/meeting/controllers/meeting_details_controller.dart b/lib/modules/meeting/controllers/meeting_details_controller.dart index d88b53f28..37c3b3259 100644 --- a/lib/modules/meeting/controllers/meeting_details_controller.dart +++ b/lib/modules/meeting/controllers/meeting_details_controller.dart @@ -1,13 +1,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:just_audio/just_audio.dart'; import '../../../data/services/db/sqflite_api.dart'; +import '../../../data/services/speech_impl/volcano_tts_api_service.dart'; import '../model/meeting_details_model.dart'; import '../model/meeting_model.dart'; class MeetingDetailsController extends GetxController with GetSingleTickerProviderStateMixin { late AnimationController lottieController; + final AudioPlayer audioPlayer = AudioPlayer(); int id = 0; @@ -31,6 +34,7 @@ class MeetingDetailsController extends GetxController @override void onClose() { lottieController.dispose(); + audioPlayer.dispose(); super.onClose(); } @@ -38,16 +42,26 @@ class MeetingDetailsController extends GetxController final data = await SqfliteApi.getMeetingDetails(id); meetingData.value = MeetingModel.fromJson(data); dataDetails.value = MeetingDetailsModel.fromJson(data['details']); - seconds = meetingData.value.seconds; + audioPlayer.setAudioSource(BytesAudioSource(dataDetails.value.audio)); + audioPlayer.playerStateStream.listen((state) async { + if (state.processingState == ProcessingState.completed) { + await audioPlayer.seek(Duration.zero); + audioPlayer.pause(); + lottieController.stop(); + isPlay.value = false; + } + }); isLoading.value = false; } - void start() { + void start() async { if (isPlay.value) { lottieController.stop(); + audioPlayer.pause(); isPlay.value = false; } else { lottieController.repeat(); + audioPlayer.play(); isPlay.value = true; } } diff --git a/lib/modules/meeting/controllers/meeting_edit_controller.dart b/lib/modules/meeting/controllers/meeting_edit_controller.dart index 87af7ec73..5ae01a749 100644 --- a/lib/modules/meeting/controllers/meeting_edit_controller.dart +++ b/lib/modules/meeting/controllers/meeting_edit_controller.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:just_audio/just_audio.dart'; import '../../../data/services/db/sqflite_api.dart'; +import '../../../data/services/speech_impl/volcano_tts_api_service.dart'; import 'meeting_controller.dart'; import 'meeting_details_controller.dart'; @@ -16,6 +18,8 @@ class MeetingEditController extends GetxController late TextEditingController titleController; late TextEditingController contentController; + final AudioPlayer audioPlayer = AudioPlayer(); + int creationTime = 0; int id = 0; @@ -36,6 +40,7 @@ class MeetingEditController extends GetxController lottieController.dispose(); titleController.dispose(); contentController.dispose(); + audioPlayer.dispose(); super.onClose(); } @@ -47,14 +52,27 @@ class MeetingEditController extends GetxController text: meetingDetailsController.dataDetails.value.content, ); creationTime = meetingDetailsController.meetingData.value.creationtime; + audioPlayer.setAudioSource( + BytesAudioSource(meetingDetailsController.dataDetails.value.audio)); + audioPlayer.playerStateStream.listen((state) async { + if (state.processingState == ProcessingState.completed) { + await audioPlayer.seek(Duration.zero); + audioPlayer.pause(); + lottieController.stop(); + isPlay.value = false; + } + }); } - void start() { + void start() async { if (isPlay.value) { lottieController.stop(); + audioPlayer.pause(); isPlay.value = false; } else { lottieController.repeat(); + await audioPlayer.seek(Duration.zero); + audioPlayer.play(); isPlay.value = true; } } diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index dfd364675..17c1849fa 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -28,6 +29,8 @@ class MeetingRecordController extends GetxController StreamSubscription? _recognitionSubscription; + BytesBuilder _originaBytes = BytesBuilder(); //原始音频 + @override void onInit() { super.onInit(); @@ -45,6 +48,7 @@ class MeetingRecordController extends GetxController timer?.cancel(); _asrService.dispose(); _recognitionSubscription?.cancel(); + _originaBytes = BytesBuilder(); super.onClose(); } @@ -95,6 +99,7 @@ class MeetingRecordController extends GetxController case RecognitionEventType.finalResult: finalContent.value += _spliceText(event); intermediateContent.value = ''; + _originaBytes.add(event.audio!); break; default: // 不做任何处理 @@ -142,12 +147,20 @@ class MeetingRecordController extends GetxController String firstContent = finalContent.value.length >= 30 ? finalContent.value.substring(0, 30) : finalContent.value; + Uint8List audio = _originaBytes.toBytes(); + Uint8List wavHeader = generateWavHeader( + sampleRate: 16000, + numberOfChannels: 1, + bitsPerSample: 16, + dataLength: audio.length, + ); + Uint8List wavData = Uint8List.fromList([...wavHeader, ...audio]); Map data = { 'title': title, 'firstContent': firstContent.replaceAll("\n", ""), 'seconds': seconds.value, 'creationTime': creationTime, - 'audio': [], + 'audio': wavData, 'content': finalContent.value, }; final result = await SqfliteApi.insertMeeting(data); @@ -162,4 +175,73 @@ class MeetingRecordController extends GetxController Get.back(); } } + + Uint8List generateWavHeader({ + required int sampleRate, + required int numberOfChannels, + required int bitsPerSample, + required int dataLength, + }) { + final byteRate = sampleRate * numberOfChannels * (bitsPerSample ~/ 8); + final blockAlign = numberOfChannels * (bitsPerSample ~/ 8); + final riffSize = 36 + dataLength; + final header = Uint8List(44); + var pos = 0; + // RIFF 块 + header.setRange(pos, pos + 4, [0x52, 0x49, 0x46, 0x46]); // 'RIFF' + pos += 4; + // 写入 RIFF 大小(小端) + header.setRange(pos, pos + 4, [ + riffSize & 0xff, + (riffSize >> 8) & 0xff, + (riffSize >> 16) & 0xff, + (riffSize >> 24) & 0xff, + ]); + pos += 4; + // WAVE 标识 + header.setRange(pos, pos + 4, [0x57, 0x41, 0x56, 0x45]); // 'WAVE' + pos += 4; + // fmt 子块 + header.setRange(pos, pos + 4, [0x66, 0x6d, 0x74, 0x20]); // 'fmt ' + pos += 4; + header.setRange(pos, pos + 4, [16, 0, 0, 0]); // 块大小 + pos += 4; + header.setRange(pos, pos + 2, [1, 0]); // PCM 格式 + pos += 2; + header.setRange(pos, pos + 2, [numberOfChannels, 0]); // 声道数 + pos += 2; + // 采样率(小端) + header.setRange(pos, pos + 4, [ + sampleRate & 0xff, + (sampleRate >> 8) & 0xff, + (sampleRate >> 16) & 0xff, + (sampleRate >> 24) & 0xff, + ]); + pos += 4; + // 字节率(小端) + header.setRange(pos, pos + 4, [ + byteRate & 0xff, + (byteRate >> 8) & 0xff, + (byteRate >> 16) & 0xff, + (byteRate >> 24) & 0xff, + ]); + pos += 4; + // 块对齐 + header.setRange(pos, pos + 2, [blockAlign, 0]); + pos += 2; + // 位深度 + header.setRange(pos, pos + 2, [bitsPerSample, 0]); + pos += 2; + // data 子块 + header.setRange(pos, pos + 4, [0x64, 0x61, 0x74, 0x61]); // 'data' + pos += 4; + // 数据大小(小端) + header.setRange(pos, pos + 4, [ + dataLength & 0xff, + (dataLength >> 8) & 0xff, + (dataLength >> 16) & 0xff, + (dataLength >> 24) & 0xff, + ]); + return header; + } } diff --git a/lib/modules/meeting/views/meeting_details_view.dart b/lib/modules/meeting/views/meeting_details_view.dart index 7423791ef..712af6131 100644 --- a/lib/modules/meeting/views/meeting_details_view.dart +++ b/lib/modules/meeting/views/meeting_details_view.dart @@ -139,12 +139,22 @@ class MeetingDetailsView extends GetView { Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Text( - '00:00:00', - style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[500], - fontSize: 12.sp, - ), + StreamBuilder( + stream: controller.audioPlayer.positionStream, + builder: (context, snapshot) { + final position = snapshot.data ?? Duration.zero; + return Text( + DateUtil.formatDateMs( + position.inMilliseconds, + format: 'HH:mm:ss', + isUtc: true, + ), + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ); + }, ), GestureDetector( onTap: controller.start, @@ -164,12 +174,23 @@ class MeetingDetailsView extends GetView { ), ), ), - Text( - '00:00:00', - style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[500], - fontSize: 12.sp, - ), + StreamBuilder( + stream: controller.audioPlayer.durationStream, + builder: (context, snapshot) { + final duration = snapshot.data ?? Duration.zero; + controller.seconds = duration.inSeconds; + return Text( + DateUtil.formatDateMs( + duration.inMilliseconds, + format: 'HH:mm:ss', + isUtc: true, + ), + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ); + }, ), ], ), @@ -202,10 +223,8 @@ class MeetingDetailsView extends GetView { alignment: Alignment.topLeft, child: SingleChildScrollView( reverse: true, - child: Obx( - () => Text( - controller.dataDetails.value.content, - ), + child: Text( + controller.dataDetails.value.content, ), ), ), diff --git a/lib/modules/meeting/views/meeting_edit_view.dart b/lib/modules/meeting/views/meeting_edit_view.dart index 73de47a76..7c65afd10 100644 --- a/lib/modules/meeting/views/meeting_edit_view.dart +++ b/lib/modules/meeting/views/meeting_edit_view.dart @@ -174,12 +174,22 @@ class MeetingEditView extends GetView { Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Text( - '00:00:00', - style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[500], - fontSize: 12.sp, - ), + StreamBuilder( + stream: controller.audioPlayer.positionStream, + builder: (context, snapshot) { + final position = snapshot.data ?? Duration.zero; + return Text( + DateUtil.formatDateMs( + position.inMilliseconds, + format: 'HH:mm:ss', + isUtc: true, + ), + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ); + }, ), GestureDetector( onTap: controller.start, @@ -192,19 +202,31 @@ class MeetingEditView extends GetView { 12.w, ), ), - child: Icon( - controller.isPlay.value ? Icons.pause : Icons.play_arrow, - color: Colors.white, - size: 25.w, + child: Obx( + () => Icon( + controller.isPlay.value ? Icons.pause : Icons.play_arrow, + color: Colors.white, + size: 25.w, + ), ), ), ), - Text( - '00:00:00', - style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[500], - fontSize: 12.sp, - ), + StreamBuilder( + stream: controller.audioPlayer.durationStream, + builder: (context, snapshot) { + final duration = snapshot.data ?? Duration.zero; + return Text( + DateUtil.formatDateMs( + duration.inMilliseconds, + format: 'HH:mm:ss', + isUtc: true, + ), + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ); + }, ), ], ), From bd062c2acdc83e8665b92eae7a262b13b3533f52 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Sat, 29 Mar 2025 17:49:05 +0800 Subject: [PATCH 12/12] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/translations/language/en_us.dart | 8 + lib/core/translations/language/zh_cn.dart | 8 + lib/modules/home/bindings/home_binding.dart | 2 +- .../home/controllers/home_controller.dart | 2 +- .../controllers/pairing_controller.dart | 2 +- .../controllers/profile_controller.dart | 142 ++++++++++++++---- lib/modules/profile/views/profile_view.dart | 10 +- .../controllers/settings_controller.dart | 92 +++++++++--- lib/modules/settings/views/settings_view.dart | 59 +++++++- .../timbre/controllers/timbre_controller.dart | 8 +- lib/modules/timbre/views/timbre_view.dart | 6 +- 11 files changed, 270 insertions(+), 69 deletions(-) diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart index d109f357d..8d3cbab5b 100644 --- a/lib/core/translations/language/en_us.dart +++ b/lib/core/translations/language/en_us.dart @@ -43,6 +43,7 @@ const Map enUS = { "and": " and ", "userAgreement": "User Agreement", "privacyPolicy": "Privacy Policy", + 'selectCountry':'Select Country/Region', // 首页 "deviceStatus": "Device Status", @@ -85,6 +86,8 @@ const Map enUS = { "fine": "Great", "excellent": "Excellent", "unknown": "Unknown", + 'bluetoothTurnedOff': 'Bluetooth is turned off, please turn it on', + 'bluetoothOff':'bluetooth is off', // 发现页 "searchAi": "Search AI features...", @@ -148,6 +151,11 @@ const Map enUS = { "flutterVoiceService": "Local voice service", "flutterVoiceServiceInfo": "Device-local voice service with no internet required", + 'timbreService': 'Timbre Service', + 'timbreServiceInfo': 'Select voice synthesis service', + 'defaultTimbre': 'Default Timbre', + 'microsoftCloudTimbreService': 'Microsoft Cloud Timbre Service', + 'voiceClone': 'Voice Clone', // 聊天页 "clearChatHistory": "Clear Chat History", diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart index ba7674fd9..e1e33780b 100644 --- a/lib/core/translations/language/zh_cn.dart +++ b/lib/core/translations/language/zh_cn.dart @@ -84,6 +84,8 @@ const Map zhCN = { "fine": "很好", "excellent": "极佳", "unknown": "未知", + 'bluetoothTurnedOff': '蓝牙已关闭,请打开蓝牙', + 'bluetoothOff': '蓝牙已关闭', // 发现页 "searchAi": "搜索AI功能...", @@ -143,6 +145,12 @@ const Map zhCN = { "azureVoiceServiceInfo": "微软云端语音服务,支持高质量语音合成和识别", "flutterVoiceService": "Flutter 本地语音服务", "flutterVoiceServiceInfo": "设备本地语音服务,无需联网即可使用", + 'timbreService': '音色服务', + 'timbreServiceInfo': '选择音色语音合成服务', + 'defaultTimbre': '默认音色', + 'microsoftCloudTimbreService': '微软云端音色服务', + 'voiceClone': '声音复刻', + 'selectCountry': '选择国家/地区', // 聊天页 "clearChatHistory": "清空聊天记录", diff --git a/lib/modules/home/bindings/home_binding.dart b/lib/modules/home/bindings/home_binding.dart index 9a67dfcc4..835a946fd 100644 --- a/lib/modules/home/bindings/home_binding.dart +++ b/lib/modules/home/bindings/home_binding.dart @@ -1,4 +1,4 @@ -import 'package:deep_voice/modules/explore/controllers/explore_controller.dart'; +import '../../explore/controllers/explore_controller.dart'; import 'package:get/get.dart'; import '../controllers/home_controller.dart'; diff --git a/lib/modules/home/controllers/home_controller.dart b/lib/modules/home/controllers/home_controller.dart index 1c1de79d5..cfed9c72f 100644 --- a/lib/modules/home/controllers/home_controller.dart +++ b/lib/modules/home/controllers/home_controller.dart @@ -278,7 +278,7 @@ class HomeController extends GetxController { String getConnectionStatusText() { // 如果蓝牙关闭,优先显示蓝牙状态 if (_bluetoothService.bluetoothState.value == 'off') { - return '蓝牙已关闭'; + return 'bluetoothOff'.tr; // 蓝牙已关闭 } switch (_connectionStatus.value) { diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index b1f07dfdb..2501e26bd 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -82,7 +82,7 @@ class PairingController extends GetxController { /// 更新状态描述 void _updateStatusDescription() { if (_bluetoothService.bluetoothState.value == 'off') { - statusDescription.value = '蓝牙已关闭,请打开蓝牙'; + statusDescription.value = 'bluetoothTurnedOff'.tr;// 蓝牙已关闭, 请打开蓝牙 return; } diff --git a/lib/modules/profile/controllers/profile_controller.dart b/lib/modules/profile/controllers/profile_controller.dart index 28a45fac6..dc2863aa9 100644 --- a/lib/modules/profile/controllers/profile_controller.dart +++ b/lib/modules/profile/controllers/profile_controller.dart @@ -13,6 +13,7 @@ import 'package:path_provider/path_provider.dart'; import '../../../data/services/network/api.dart'; import 'package:dio/dio.dart'; + class ProfileController extends GetxController { static ProfileController get to => Get.find(); @@ -115,6 +116,53 @@ class ProfileController extends GetxController { // void toggleEditing() { // _isEditing.value = !_isEditing.value; + // } + + // 验证base64图片数据 + // Future _verifyBase64Image(String base64String) async { + // try { + // // 解码base64数据 + // List imageBytes = base64Decode(base64String); + + // // 获取应用文档目录 + // final appDir = await getApplicationDocumentsDirectory(); + // final verifyPath = '${appDir.path}/verify_base64_${DateTime.now().millisecondsSinceEpoch}.jpg'; + + // // 将解码后的数据写入文件 + // final file = File(verifyPath); + // await file.writeAsBytes(imageBytes); + + // // 记录验证文件的路径 + // Logger().d('Base64验证图片已保存到: $verifyPath'); + + // // 显示图片预览对话框 + // Get.dialog( + // Dialog( + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Padding( + // padding: const EdgeInsets.all(16.0), + // child: Text('Base64 图片验证', style: TextStyle(fontWeight: FontWeight.bold)), + // ), + // Image.file(File(verifyPath)), + // Padding( + // padding: const EdgeInsets.all(16.0), + // child: ElevatedButton( + // onPressed: () => Get.back(), + // child: Text('关闭'), + // ), + // ), + // ], + // ), + // ), + // ); + + // Get.snackbar('验证', '已将Base64转换回图片,请查看预览'); + // } catch (e) { + // Logger().e('验证Base64图片失败: $e'); + // Get.snackbar('错误', 'Base64验证失败: $e'); + // } // } // 更新用户信息到后端 @@ -123,26 +171,29 @@ class ProfileController extends GetxController { // 准备数据 Map data = { "image": "", // 默认空字符串 - "language": currentLanguage.value, - "name": userInfo.value.name, + "language": currentLanguage.value,//当前用户语言 + "name": userInfo.value.name,//当前用户名 }; - // // 如果有头像,转换为base64 - // if (localAvatar.value.isNotEmpty) { - // try{ - // File imageFile = File(localAvatar.value); - // if(await imageFile.exists()){ - // List imageBytes = await imageFile.readAsBytes(); - // String base64Image = base64Encode(imageBytes); - // data["image"] = base64Image; - // }else{ - // Logger().e('头像文件不存在'); - // } - // }catch(e){ - // Logger().e('读取头像文件失败: $e'); - // } - - // } + // 如果有头像,转换为base64 + if (localAvatar.value.isNotEmpty) { + //如果本地头像路径不为空,即代表有头像 + try { + File imageFile = File(localAvatar.value); + if (await imageFile.exists()) { + List imageBytes = await imageFile.readAsBytes(); + String base64Image = base64Encode(imageBytes); + data["image"] = base64Image; + + // // 验证base64数据 - 将base64转回图片并保存到本地 + // await _verifyBase64Image(base64Image); + } else { + Logger().e('头像文件不存在'); + } + } catch (e) { + Logger().e('读取头像文件失败: $e'); + } + } final dio = Dio(BaseOptions( baseUrl: 'https://www.deapsound.com:443', @@ -160,6 +211,8 @@ class ProfileController extends GetxController { '/api/home/user_setting', data: data, ); + + // 调用API //final response = await Api.updateUserSetting(data); @@ -167,6 +220,13 @@ class ProfileController extends GetxController { if (response.data['code'] == 0) { Logger().d('更新成功: ${response.data}'); Get.snackbar('成功', '同步到服务器成功'); + userInfo.update((u) { + if (u != null) { + u.name = response.data['data']['name'] ?? ''; + u.avatar = response.data['data']['avatar'] ?? ''; + } + }); + Logger().d('更新后的用户信息: ${userInfo.value.toJson()}'); } else { throw '更新失败:${response.data['msg'] ?? '未知错误'}'; } @@ -184,18 +244,35 @@ class ProfileController extends GetxController { final userJson = _storage.read(userKey); if (userJson != null) { userInfo.value = User.instance; + Logger().d('从本地加载用户信息: $userJson'); // 如果有远程头像地址,下载并保存到本地 if (userInfo.value.avatar.isNotEmpty) { + Logger().d('远程头像地址: ${userInfo.value.avatar}'); _downloadAndSaveAvatar(userInfo.value.avatar); } } } // 下载并保存远程头像 - Future _downloadAndSaveAvatar(String url) async { + Future _downloadAndSaveAvatar(String url) async { try { - final response = await GetConnect().get(url); - if (response.status.isOk && response.body != null) { + Logger().d('开始下载头像: $url'); + + final dio = Dio(BaseOptions( + headers: { + 'accept': '*/*', + 'Authorization': _storage.read("logintoken"), + }, + )); + + final response = await dio.get( + url, + options: Options(responseType: ResponseType.bytes), + ); + + Logger().d('下载状态: ${response.statusCode}'); + + if (response.statusCode == 200) { // 保存到应用文档目录 final appDir = await getApplicationDocumentsDirectory(); final fileName = 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg'; @@ -203,15 +280,13 @@ class ProfileController extends GetxController { // 写入文件 final file = File(localPath); - final List bytes = response.body.runtimeType == String - ? base64Decode(response.body.toString()) - : response.body; - await file.writeAsBytes(bytes); + await file.writeAsBytes(response.data); // 更新本地头像路径 localAvatar.value = localPath; await _storage.write('local_avatar', localPath); Logger().d('头像已下载到本地: $localPath'); + //Get.snackbar('头像', '头像已下载到本地'); } } catch (e) { Logger().e('下载头像失败: $e'); @@ -225,12 +300,12 @@ class ProfileController extends GetxController { // 更新用户头像 Future updateUserAvatar() async { final XFile? image = await _picker.pickImage( - source: ImageSource.gallery, + source: ImageSource.gallery,// 从相册选择 maxWidth: 800, maxHeight: 800, imageQuality: 85, ); - + if (image != null) { // 检查文件大小 final file = File(image.path); @@ -268,7 +343,7 @@ class ProfileController extends GetxController { localAvatar.value = image.path; await _storage.write('local_avatar', image.path); // await saveUserInfo(); //保存到本地 - await _syncWithBackend(); //同步到后端 + //await _syncWithBackend(); //同步到后端 // 通知 SettingsController 更新 // if (Get.isRegistered()) { @@ -278,7 +353,7 @@ class ProfileController extends GetxController { } // 更新用户名 - Future updateUserName(String name) async { + Future updateUserNameAndAvatar(String name) async { if (name.trim().isEmpty) { Logger().e('无效名称: $name'); return; @@ -298,11 +373,14 @@ class ProfileController extends GetxController { //同步更新 SettingsController if (Get.isRegistered()) { final settingsController = Get.find(); - settingsController.loadUserInfo(); + settingsController.userInfo.value.name = userInfo.value.name; + settingsController.userInfo.value.avatar = '${userInfo.value.avatar}?time=${DateTime.now().millisecondsSinceEpoch}'; + //settingsController.loadUserInfo(); + settingsController.userInfo.refresh(); } } catch (e) { - Logger().e('更新名称错误: $e'); - Get.snackbar('错误', '更新用户名失败'); + Logger().e('更新用户名和头像错误: $e'); + Get.snackbar('错误', '更新用户名和头像失败'); } } } diff --git a/lib/modules/profile/views/profile_view.dart b/lib/modules/profile/views/profile_view.dart index a74e8ae21..bb2771b08 100644 --- a/lib/modules/profile/views/profile_view.dart +++ b/lib/modules/profile/views/profile_view.dart @@ -148,6 +148,14 @@ class EditProfileView extends GetView { centerTitle: true, elevation: 0, backgroundColor: isDarkMode ? Colors.black : Colors.white, + leading: IconButton( + icon: Icon( + Icons.arrow_back, + size: 24.sp, + color: isDarkMode? Colors.white : Colors.black, + ), + onPressed: () => Get.back(), + ), title: Text( 'editProfile'.tr, style: TextStyle( @@ -231,7 +239,7 @@ class EditProfileView extends GetView { child: ElevatedButton( onPressed: () { // 保存用户名 - controller.updateUserName(nameController.text); + controller.updateUserNameAndAvatar(nameController.text); // 返回上一级页面 Get.back(); }, diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 9fe723ee2..ce400e224 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -1,15 +1,17 @@ import 'package:firebase_auth/firebase_auth.dart' as firebase; import 'package:get/get.dart'; import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; +import 'package:dio/dio.dart'; import 'package:get_storage/get_storage.dart'; import '../../../core/themes/theme_service.dart'; import '../../../data/services/speech_factory.dart'; import '../models/language_model.dart'; -//import '../../../data/models/user_model.dart'; +import 'package:logger/logger.dart'; import '../../../data/models/user_Info.dart'; import '../../../modules/login/controllers/googleLogin.dart'; import '../../../modules/login/controllers/facebookLogin.dart'; +import 'package:path_provider/path_provider.dart'; +import 'dart:io'; class SettingsController extends GetxController { // 语言设置 @@ -61,12 +63,12 @@ class SettingsController extends GetxController { }, ]; - RxString timbreName = '默认音色'.obs; + RxString timbreName = 'defaultTimbre'.obs; //用户信息 static const String userKey = 'user_info'; //final _storage=GetStorage(); - final ImagePicker _picker = ImagePicker(); + //final ImagePicker _picker = ImagePicker(); final Rx userInfo = User.instance.obs; //使用单例实例 @override @@ -93,8 +95,20 @@ class SettingsController extends GetxController { void loadUserInfo() { final userJson = _storage.read(userKey); + Logger().d('加载用户信息: $userJson'); + if (userJson != null) { userInfo.value = User.instance; + Logger().d('用户信息已加载: ${userInfo.value.toJson()}'); + + if (userInfo.value.avatar.isNotEmpty) { + Logger().d('开始下载头像'); + _downloadAndSaveAvatar(userInfo.value.avatar); + } else { + Logger().d('用户没有头像'); + } + } else { + Logger().d('没有找到用户信息'); } } @@ -102,24 +116,64 @@ class SettingsController extends GetxController { await _storage.write(userKey, userInfo.value.toJson()); } - Future updateUserAvatar() async { - final XFile? image = await _picker.pickImage( - source: ImageSource.gallery, - maxWidth: 800, - maxHeight: 800, - imageQuality: 85, - ); - - if (image != null) { - userInfo.update((user) { - if (user != null) { - user.avatar = image.path; - } - }); - await saveUserInfo(); + // 下载并保存远程头像 + Future _downloadAndSaveAvatar(String url) async { + try { + Logger().d('开始下载头像: $url'); + + final dio = Dio(BaseOptions( + headers: { + 'accept': '*/*', + 'Authorization': _storage.read("logintoken"), + }, + )); + + final response = await dio.get( + url, + options: Options(responseType: ResponseType.bytes), + ); + + Logger().d('下载状态: ${response.statusCode}'); + + if (response.statusCode == 200) { + // 保存到应用文档目录 + final appDir = await getApplicationDocumentsDirectory(); + final fileName = 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg'; + final localPath = '${appDir.path}/$fileName'; + + // 写入文件 + final file = File(localPath); + await file.writeAsBytes(response.data); + + // 更新本地头像路径 + //localAvatar.value = localPath; + await _storage.write('local_avatar', localPath); + Logger().d('头像已下载到本地: $localPath'); + //Get.snackbar('头像', '头像已下载到本地'); + } + } catch (e) { + Logger().e('下载头像失败: $e'); } } + // Future updateUserAvatar() async { + // final XFile? image = await _picker.pickImage( + // source: ImageSource.gallery, + // maxWidth: 800, + // maxHeight: 800, + // imageQuality: 85, + // ); + + // if (image != null) { + // userInfo.update((user) { + // if (user != null) { + // user.avatar = image.path; + // } + // }); + // await saveUserInfo(); + // } + // } + Future updateUserName(String name) async { if (name.trim().isNotEmpty) { userInfo.update((user) { diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 01c8e0243..564cf4443 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -110,13 +110,55 @@ class SettingsView extends GetView { // 裁剪圆形头像 borderRadius: BorderRadius.circular(30.r), child: controller.userInfo.value.avatar.isEmpty - ? Image.network( - 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80', - fit: BoxFit.cover, // 图片填充方式 + ? + // Image.network( + // 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80', + // fit: BoxFit.cover, // 图片填充方式 + // ) + Container( + decoration: BoxDecoration( + color: Colors.blue[600], + borderRadius: BorderRadius.circular(30.r), + ), + child: Center( + child: Text( + controller.userInfo.value.name.isNotEmpty + ? controller.userInfo.value.name[0] + .toUpperCase() + : '?', + style: TextStyle( + color: Colors.white, + fontSize: 24.sp, + fontWeight: FontWeight.bold, + ), + ), + ), ) : Image.network( controller.userInfo.value.avatar, - fit: BoxFit.cover, // 图片填充方式 + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + // 当图片加载失败时显示用户名首字母 + return Container( + decoration: BoxDecoration( + color: Colors.blue[600], + borderRadius: BorderRadius.circular(30.r), + ), + child: Center( + child: Text( + controller.userInfo.value.name.isNotEmpty + ? controller.userInfo.value.name[0] + .toUpperCase() + : '?', + style: TextStyle( + color: Colors.white, + fontSize: 24.sp, + fontWeight: FontWeight.bold, + ), + ), + ), + ); + }, )), ), )), @@ -328,6 +370,9 @@ class SettingsView extends GetView { ), onTap: () { // 打开备份设置页面 + print('---------用户头像-${controller.userInfo.value.avatar}'); + print('---------用户名-${controller.userInfo.value.name}'); + //print('user${User.instance.value}'); }, isDarkMode: isDarkMode, ), @@ -346,15 +391,15 @@ class SettingsView extends GetView { : Colors.grey[200]), Obx( () => _buildNavigationSetting( - title: '音色服务', - subtitle: '选择音色语音合成服务', + title: 'timbreService'.tr, // 音色服务 + subtitle: 'timbreServiceInfo'.tr, // 选择音色服务 icon: Icons.photo_camera_front_outlined, iconBgColor: isDarkMode ? Colors.cyan[900]!.withOpacity(0.3) : Colors.cyan[100]!, iconColor: isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, - value: controller.timbreName.value, + value: controller.timbreName.value.tr, valueStyle: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.grey[500], diff --git a/lib/modules/timbre/controllers/timbre_controller.dart b/lib/modules/timbre/controllers/timbre_controller.dart index 4514d2aa2..62d2cec06 100644 --- a/lib/modules/timbre/controllers/timbre_controller.dart +++ b/lib/modules/timbre/controllers/timbre_controller.dart @@ -51,15 +51,15 @@ class TimbreController extends GetxController { } else { dataList.add({ 'type': '1', - 'name': '默认音色', - 'description': '微软云端音色服务', + 'name': 'defaultTimbre', + 'description': 'microsoftCloudTimbreService', 'isUsable': true }); _storage.write('timbreList', [ { 'type': '1', - 'name': '默认音色', - 'description': '微软云端音色服务', + 'name': 'defaultTimbre', + 'description': 'microsoftCloudTimbreService', 'isUsable': true } ]); diff --git a/lib/modules/timbre/views/timbre_view.dart b/lib/modules/timbre/views/timbre_view.dart index dd82c8d55..9073b1dcd 100644 --- a/lib/modules/timbre/views/timbre_view.dart +++ b/lib/modules/timbre/views/timbre_view.dart @@ -137,7 +137,7 @@ class TimbreView extends GetView { const Icon(Icons.keyboard_voice, color: Colors.white), 5.horizontalSpace, Text( - '声音复刻', + 'voiceClone'.tr, style: TextStyle(color: Colors.white, fontSize: 16.sp), ), ], @@ -180,7 +180,7 @@ class TimbreView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - data['name'], + '${data['name']}'.tr, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, @@ -190,7 +190,7 @@ class TimbreView extends GetView { ), SizedBox(height: 4.h), Text( - data['description'], + '${data['description']}'.tr, style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.black54,