From ae4b04ba8ea6f5cb16e7e2f56759ed735462a9f6 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Mon, 28 Jul 2025 15:18:33 +0800 Subject: [PATCH 1/6] =?UTF-8?q?refactor(privacy):=20=E5=B0=86=E9=9A=90?= =?UTF-8?q?=E7=A7=81=E6=94=BF=E7=AD=96=E5=BC=B9=E7=AA=97=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E7=A7=BB=E8=87=B3splash=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构隐私政策处理流程,将相关逻辑从登录控制器移至splash控制器 移除重复的初始化代码,简化登录控制器逻辑 --- .../login/controllers/login_controller.dart | 17 +- .../splash/controllers/splash_controller.dart | 197 +++++++++++++++++- 2 files changed, 197 insertions(+), 17 deletions(-) diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart index c17147ef4..fb27108f2 100644 --- a/lib/modules/login/controllers/login_controller.dart +++ b/lib/modules/login/controllers/login_controller.dart @@ -229,17 +229,9 @@ class LoginController extends GetxController { _hasAcceptedPrivacy.value = _storage.read('hasAcceptedPrivacy') ?? false; // 从本地存储中读取是否已接受隐私政策 - //使用addPostFrameCallback确保在构建完成后显示对话框 - WidgetsBinding.instance.addPostFrameCallback((_) { - //如果用户未接受隐私政策,显示弹窗 - if (!_hasAcceptedPrivacy.value) { - _showPrivacyDialog(); - } else { - _initDeviceInfo(); - _initAppInfo(); // 新增:初始化APP信息 - _checkGuestLoginConfig(); // 检查游客登录配置 - } - }); + _initDeviceInfo(); + _initAppInfo(); // 新增:初始化APP信息 + _checkGuestLoginConfig(); // 检查游客登录配置 } @override @@ -415,9 +407,6 @@ class LoginController extends GetxController { // 用户同意,关闭弹窗并继续 _hasAcceptedPrivacy.value = true; Get.back(); - _initDeviceInfo(); - _initAppInfo(); // 新增:初始化APP信息 - _checkGuestLoginConfig(); // 检查游客登录配置 }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0066CC), diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index 5a3f35a13..a333d3ac2 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import '../../../data/models/appconfig.dart'; import '../../../data/models/appconfig_model.dart'; import 'package:flutter/material.dart'; @@ -10,6 +11,7 @@ import '../../../core/themes/theme_service.dart'; import '../../../data/services/network/api.dart'; import '../../../data/services/version_update_service.dart'; import '../../../modules/common/widgets/version_update_dialog.dart'; +import '../../common/webview_page.dart'; class SplashController extends GetxController { final GetStorage _storage = GetStorage(); @@ -21,6 +23,8 @@ class SplashController extends GetxController { final _isResourceLoaded = false.obs; bool get isResourceLoaded => _isResourceLoaded.value; + final _hasAcceptedPrivacy = false.obs; //是否同意隐私政策 + // 版本号 //final String version = 'V1.0.0'; @@ -30,13 +34,200 @@ class SplashController extends GetxController { @override void onInit() { super.onInit(); + + // 首先检查隐私政策状态 + final hasAcceptedPrivacy = _storage.read('hasAcceptedPrivacy') ?? false; WidgetsBinding.instance.addPostFrameCallback((_) { - _startLoadingAnimation(); - _loadAppResources(); - // _initMapSDK(); + if (!hasAcceptedPrivacy) { + // 优先显示隐私政策弹窗,暂停其他初始化 + _showPrivacyDialog(); + } else { + // 已同意隐私政策,正常启动流程 + _startNormalFlow(); + } }); } + void _startNormalFlow() { + _startLoadingAnimation(); + _loadAppResources(); + } + + void _onPrivacyAccepted() { + _storage.write('hasAcceptedPrivacy', true); + Get.back(); // 关闭弹窗 + _startNormalFlow(); // 开始正常流程 + } + + void _onPrivacyRejected() { + exit(0); // 退出应用 + } + + // 显示隐私政策弹窗 + void _showPrivacyDialog() { + // 获取当前主题模式 + final bool isDarkMode = Get.theme.brightness == Brightness.dark; + + Get.dialog( + WillPopScope( + onWillPop: () async => false, // 禁止通过返回键关闭弹窗 + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + backgroundColor: + isDarkMode ? Colors.grey[800] : Colors.white, // 暗黑模式使用深灰色背景 + child: Container( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'loginDeclaration'.tr, //APP 声明 + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: + isDarkMode ? Colors.white : Colors.black, // 暗黑模式使用白色文字 + ), + ), + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: 'welcomeToThisApp'.tr, //欢迎使用本APP,请充分阅读 + style: TextStyle( + fontSize: 14, + color: isDarkMode + ? Colors.white70 + : Colors.black87, // 暗黑模式使用浅白色 + ), + ), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () { + String url = + 'https://www.voitrans.net/doc/ysxy.html'; // 隐私政策链接 + if (Get.locale?.languageCode != 'zh' || + Get.locale?.countryCode != 'CN') { + url = + 'https://www.voitrans.net/doc/ysxy_en.html'; // 隐私政策英文链接 + } + Get.to(() => WebViewPage( + url: url, + title: 'privacyPolicy'.tr, + )); + }, + child: Text( + 'privacyPolicy'.tr, //隐私政策 + style: const TextStyle( + fontSize: 14, + color: Color(0xFF3B82F6), + ), + ), + ), + ), + TextSpan( + text: 'and'.tr, //和 + style: TextStyle( + fontSize: 14, + color: isDarkMode ? Colors.white70 : Colors.black87, + ), + ), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () { + String url = + 'https://www.voitrans.net/doc/yhxy.html'; // 用户协议链接 + if (Get.locale?.languageCode != 'zh' || + Get.locale?.countryCode != 'CN') { + url = + 'https://www.voitrans.net/doc/yhxy_en.html'; // 用户协议英文链接 + } + Get.to(() => WebViewPage( + url: url, + title: 'userAgreement'.tr, //用户协议 + )); + }, + child: Text( + 'userAgreement'.tr, //用户协议 + style: const TextStyle( + fontSize: 14, + color: Color(0xFF3B82F6), + ), + ), + ), + ), + TextSpan( + text: 'privacyPolicyText' + .tr, //',点击“同意并进入”表示您已充分阅读并同意上述协议。点击“不同意”将退出应用。', + style: TextStyle( + fontSize: 14, + color: isDarkMode ? Colors.white70 : Colors.black87, + ), + ), + ], + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: () { + // 用户不同意,退出应用 + _onPrivacyRejected(); + }, + style: TextButton.styleFrom( + foregroundColor: isDarkMode + ? Colors.grey[400] + : Colors.black54, // 暗黑模式使用浅灰色 + ), + child: Text( + 'disagree'.tr, //不同意 + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.black54, + ), + ), + //child: const Text('不同意'), + ), + ElevatedButton( + onPressed: () { + _onPrivacyAccepted(); + // 用户同意,关闭弹窗并继续 + _hasAcceptedPrivacy.value = true; + //Get.back(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF0066CC), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: isDarkMode ? 4 : 2, // 暗黑模式下增加阴影效果提高辨识度 + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 10), // 统一的内边距 + ), + child: Text('agreeAndEnter'.tr), + ), + ], + ), + ], + ), + ), + ), + ), + barrierDismissible: false, // 禁止点击外部关闭弹窗 + ); + } + // 初始化地图SDK // void _initMapSDK() { // try { From 8923d0407c9eb41efd6730ac30cd764a081d96b7 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Mon, 28 Jul 2025 20:40:15 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(privacy):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=9A=90=E7=A7=81=E6=94=BF=E7=AD=96=E5=BC=B9=E7=AA=97=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=B9=B6=E9=87=8D=E6=9E=84=E7=9B=B8=E5=85=B3=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将隐私政策弹窗逻辑提取为独立组件,优化代码复用 在登录和闪屏页面使用新组件替代重复实现 支持暗黑模式适配和国际化 --- .../common/widgets/privacy_policy_dialog.dart | 200 ++++++++++++++++++ .../login/controllers/login_controller.dart | 171 +-------------- .../splash/controllers/splash_controller.dart | 168 +-------------- 3 files changed, 217 insertions(+), 322 deletions(-) create mode 100644 lib/modules/common/widgets/privacy_policy_dialog.dart diff --git a/lib/modules/common/widgets/privacy_policy_dialog.dart b/lib/modules/common/widgets/privacy_policy_dialog.dart new file mode 100644 index 000000000..b6863e2f3 --- /dev/null +++ b/lib/modules/common/widgets/privacy_policy_dialog.dart @@ -0,0 +1,200 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import '../webview_page.dart'; + +/// 隐私政策弹窗组件 +class PrivacyPolicyDialog extends StatelessWidget { + final VoidCallback? onAccept; // 同意回调 + final VoidCallback? onReject; // 拒绝回调 + final bool canDismiss; // 是否可以通过返回键关闭 + + const PrivacyPolicyDialog({ + super.key, + this.onAccept, + this.onReject, + this.canDismiss = false, + }); + + /// 显示隐私政策弹窗 + static Future show({ + VoidCallback? onAccept, + VoidCallback? onReject, + bool canDismiss = false, + }) { + return Get.dialog( + PrivacyPolicyDialog( + onAccept: onAccept, + onReject: onReject, + canDismiss: canDismiss, + ), + barrierDismissible: canDismiss, + ); + } + + @override + Widget build(BuildContext context) { + final bool isDarkMode = Get.theme.brightness == Brightness.dark; + + return PopScope( + canPop: canDismiss, // 根据参数决定是否可以通过返回键关闭 + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.r), + ), + backgroundColor: + isDarkMode ? Colors.grey[800] : Colors.white, // 暗黑模式使用深灰色背景 + child: Container( + padding: EdgeInsets.all(20.w), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'loginDeclaration'.tr, // APP 声明 + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.bold, + color: isDarkMode ? Colors.white : Colors.black, // 暗黑模式使用白色文字 + ), + ), + SizedBox(height: 16.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: 'welcomeToThisApp'.tr, // 欢迎使用本APP,请充分阅读 + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode + ? Colors.white70 + : Colors.black87, // 暗黑模式使用浅白色 + ), + ), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () { + String url = + 'https://www.voitrans.net/doc/ysxy.html'; // 隐私政策链接 + if (Get.locale?.languageCode != 'zh' || + Get.locale?.countryCode != 'CN') { + url = + 'https://www.voitrans.net/doc/ysxy_en.html'; // 隐私政策英文链接 + } + Get.to(() => WebViewPage( + url: url, + title: 'privacyPolicy'.tr, + )); + }, + child: Text( + 'privacyPolicy'.tr, // 隐私政策 + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF3B82F6), + ), + ), + ), + ), + TextSpan( + text: 'and'.tr, // 和 + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.white70 : Colors.black87, + ), + ), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () { + String url = + 'https://www.voitrans.net/doc/yhxy.html'; // 用户协议链接 + if (Get.locale?.languageCode != 'zh' || + Get.locale?.countryCode != 'CN') { + url = + 'https://www.voitrans.net/doc/yhxy_en.html'; // 用户协议英文链接 + } + Get.to(() => WebViewPage( + url: url, + title: 'userAgreement'.tr, // 用户协议 + )); + }, + child: Text( + 'userAgreement'.tr, // 用户协议 + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF3B82F6), + ), + ), + ), + ), + TextSpan( + text: 'privacyPolicyText' + .tr, // ',点击"同意并进入"表示您已充分阅读并同意上述协议。点击"不同意"将退出应用。', + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.white70 : Colors.black87, + ), + ), + ], + ), + textAlign: TextAlign.center, + ), + ), + SizedBox(height: 20.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: () { + if (onReject != null) { + onReject!(); + } else { + // 默认行为:退出应用 + exit(0); + } + }, + style: TextButton.styleFrom( + foregroundColor: isDarkMode + ? Colors.grey[400] + : Colors.black54, // 暗黑模式使用浅灰色 + ), + child: Text( + 'disagree'.tr, // 不同意 + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.black54, + ), + ), + ), + ElevatedButton( + onPressed: () { + if (onAccept != null) { + onAccept!(); + } else { + // 默认行为:关闭弹窗 + Get.back(); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF0066CC), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + ), + elevation: isDarkMode ? 4 : 2, // 暗黑模式下增加阴影效果提高辨识度 + padding: EdgeInsets.symmetric( + horizontal: 16.w, vertical: 10.h), // 统一的内边距 + ), + child: Text('agreeAndEnter'.tr), // 同意并进入 + ), + ], + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart index fb27108f2..3cd59f5fd 100644 --- a/lib/modules/login/controllers/login_controller.dart +++ b/lib/modules/login/controllers/login_controller.dart @@ -12,12 +12,12 @@ import 'weixinLogin.dart'; import 'appleLogin.dart'; import '../../../data/services/network/api.dart'; import '../../../../data/models/user_Info.dart'; -import '../../common/webview_page.dart'; import 'package:get_storage/get_storage.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'dart:io'; import 'package:package_info_plus/package_info_plus.dart'; import '../../../data/services/version_update_service.dart'; +import '../../../modules/common/widgets/privacy_policy_dialog.dart'; enum LoginViewState { welcome, // 欢迎页面 @@ -267,167 +267,14 @@ class LoginController extends GetxController { // 显示隐私政策弹窗 void _showPrivacyDialog() { - // 获取当前主题模式 - final bool isDarkMode = Get.theme.brightness == Brightness.dark; - - Get.dialog( - WillPopScope( - onWillPop: () async => false, // 禁止通过返回键关闭弹窗 - child: Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: - isDarkMode ? Colors.grey[800] : Colors.white, // 暗黑模式使用深灰色背景 - child: Container( - padding: const EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'loginDeclaration'.tr, //APP 声明 - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: - isDarkMode ? Colors.white : Colors.black, // 暗黑模式使用白色文字 - ), - ), - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: 'welcomeToThisApp'.tr, //欢迎使用本APP,请充分阅读 - style: TextStyle( - fontSize: 14, - color: isDarkMode - ? Colors.white70 - : Colors.black87, // 暗黑模式使用浅白色 - ), - ), - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () { - String url = - 'https://www.voitrans.net/doc/ysxy.html'; // 隐私政策链接 - if (Get.locale?.languageCode != 'zh' || - Get.locale?.countryCode != 'CN') { - url = - 'https://www.voitrans.net/doc/ysxy_en.html'; // 隐私政策英文链接 - } - Get.to(() => WebViewPage( - url: url, - title: 'privacyPolicy'.tr, - )); - }, - child: Text( - 'privacyPolicy'.tr, //隐私政策 - style: const TextStyle( - fontSize: 14, - color: Color(0xFF3B82F6), - ), - ), - ), - ), - TextSpan( - text: 'and'.tr, //和 - style: TextStyle( - fontSize: 14, - color: isDarkMode ? Colors.white70 : Colors.black87, - ), - ), - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () { - String url = - 'https://www.voitrans.net/doc/yhxy.html'; // 用户协议链接 - if (Get.locale?.languageCode != 'zh' || - Get.locale?.countryCode != 'CN') { - url = - 'https://www.voitrans.net/doc/yhxy_en.html'; // 用户协议英文链接 - } - Get.to(() => WebViewPage( - url: url, - title: 'userAgreement'.tr, //用户协议 - )); - }, - child: Text( - 'userAgreement'.tr, //用户协议 - style: const TextStyle( - fontSize: 14, - color: Color(0xFF3B82F6), - ), - ), - ), - ), - TextSpan( - text: 'privacyPolicyText' - .tr, //',点击“同意并进入”表示您已充分阅读并同意上述协议。点击“不同意”将退出应用。', - style: TextStyle( - fontSize: 14, - color: isDarkMode ? Colors.white70 : Colors.black87, - ), - ), - ], - ), - textAlign: TextAlign.center, - ), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - TextButton( - onPressed: () { - // 用户不同意,退出应用 - exit(0); - }, - style: TextButton.styleFrom( - foregroundColor: isDarkMode - ? Colors.grey[400] - : Colors.black54, // 暗黑模式使用浅灰色 - ), - child: Text( - 'disagree'.tr, //不同意 - style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.black54, - ), - ), - //child: const Text('不同意'), - ), - ElevatedButton( - onPressed: () { - _storage.write( - 'hasAcceptedPrivacy', true); // 存储用户已接受隐私政策 - // 用户同意,关闭弹窗并继续 - _hasAcceptedPrivacy.value = true; - Get.back(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF0066CC), - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: isDarkMode ? 4 : 2, // 暗黑模式下增加阴影效果提高辨识度 - padding: const EdgeInsets.symmetric( - horizontal: 16, vertical: 10), // 统一的内边距 - ), - child: Text('agreeAndEnter'.tr), - ), - ], - ), - ], - ), - ), - ), - ), - barrierDismissible: false, // 禁止点击外部关闭弹窗 + PrivacyPolicyDialog.show( + onAccept: () { + _storage.write('hasAcceptedPrivacy', true); // 存储用户已接受隐私政策 + _hasAcceptedPrivacy.value = true; + Get.back(); + }, + onReject: () => exit(0), + canDismiss: false, // 禁止通过返回键关闭弹窗 ); } diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index a333d3ac2..b12b22746 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -11,6 +11,7 @@ import '../../../core/themes/theme_service.dart'; import '../../../data/services/network/api.dart'; import '../../../data/services/version_update_service.dart'; import '../../../modules/common/widgets/version_update_dialog.dart'; +import '../../../modules/common/widgets/privacy_policy_dialog.dart'; import '../../common/webview_page.dart'; class SplashController extends GetxController { @@ -65,166 +66,13 @@ class SplashController extends GetxController { // 显示隐私政策弹窗 void _showPrivacyDialog() { - // 获取当前主题模式 - final bool isDarkMode = Get.theme.brightness == Brightness.dark; - - Get.dialog( - WillPopScope( - onWillPop: () async => false, // 禁止通过返回键关闭弹窗 - child: Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: - isDarkMode ? Colors.grey[800] : Colors.white, // 暗黑模式使用深灰色背景 - child: Container( - padding: const EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'loginDeclaration'.tr, //APP 声明 - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: - isDarkMode ? Colors.white : Colors.black, // 暗黑模式使用白色文字 - ), - ), - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: 'welcomeToThisApp'.tr, //欢迎使用本APP,请充分阅读 - style: TextStyle( - fontSize: 14, - color: isDarkMode - ? Colors.white70 - : Colors.black87, // 暗黑模式使用浅白色 - ), - ), - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () { - String url = - 'https://www.voitrans.net/doc/ysxy.html'; // 隐私政策链接 - if (Get.locale?.languageCode != 'zh' || - Get.locale?.countryCode != 'CN') { - url = - 'https://www.voitrans.net/doc/ysxy_en.html'; // 隐私政策英文链接 - } - Get.to(() => WebViewPage( - url: url, - title: 'privacyPolicy'.tr, - )); - }, - child: Text( - 'privacyPolicy'.tr, //隐私政策 - style: const TextStyle( - fontSize: 14, - color: Color(0xFF3B82F6), - ), - ), - ), - ), - TextSpan( - text: 'and'.tr, //和 - style: TextStyle( - fontSize: 14, - color: isDarkMode ? Colors.white70 : Colors.black87, - ), - ), - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () { - String url = - 'https://www.voitrans.net/doc/yhxy.html'; // 用户协议链接 - if (Get.locale?.languageCode != 'zh' || - Get.locale?.countryCode != 'CN') { - url = - 'https://www.voitrans.net/doc/yhxy_en.html'; // 用户协议英文链接 - } - Get.to(() => WebViewPage( - url: url, - title: 'userAgreement'.tr, //用户协议 - )); - }, - child: Text( - 'userAgreement'.tr, //用户协议 - style: const TextStyle( - fontSize: 14, - color: Color(0xFF3B82F6), - ), - ), - ), - ), - TextSpan( - text: 'privacyPolicyText' - .tr, //',点击“同意并进入”表示您已充分阅读并同意上述协议。点击“不同意”将退出应用。', - style: TextStyle( - fontSize: 14, - color: isDarkMode ? Colors.white70 : Colors.black87, - ), - ), - ], - ), - textAlign: TextAlign.center, - ), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - TextButton( - onPressed: () { - // 用户不同意,退出应用 - _onPrivacyRejected(); - }, - style: TextButton.styleFrom( - foregroundColor: isDarkMode - ? Colors.grey[400] - : Colors.black54, // 暗黑模式使用浅灰色 - ), - child: Text( - 'disagree'.tr, //不同意 - style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.black54, - ), - ), - //child: const Text('不同意'), - ), - ElevatedButton( - onPressed: () { - _onPrivacyAccepted(); - // 用户同意,关闭弹窗并继续 - _hasAcceptedPrivacy.value = true; - //Get.back(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF0066CC), - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: isDarkMode ? 4 : 2, // 暗黑模式下增加阴影效果提高辨识度 - padding: const EdgeInsets.symmetric( - horizontal: 16, vertical: 10), // 统一的内边距 - ), - child: Text('agreeAndEnter'.tr), - ), - ], - ), - ], - ), - ), - ), - ), - barrierDismissible: false, // 禁止点击外部关闭弹窗 + PrivacyPolicyDialog.show( + onAccept: () { + _onPrivacyAccepted(); + _hasAcceptedPrivacy.value = true; + }, + onReject: _onPrivacyRejected, + canDismiss: false, // 禁止通过返回键关闭弹窗 ); } From 388fa342b01ef2ca55689039db6c7853a053eeb8 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Tue, 29 Jul 2025 15:46:09 +0800 Subject: [PATCH 3/6] =?UTF-8?q?fix(android):=20=E7=A7=BB=E9=99=A4MANAGE=5F?= =?UTF-8?q?EXTERNAL=5FSTORAGE=E6=9D=83=E9=99=90=E5=A3=B0=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit style(login): 调整社交登录按钮间距,仅在iOS的Apple按钮前添加间距 --- lib/modules/login/views/login_view.dart | 3 +-- local_plugins/jl_opus/android/src/main/AndroidManifest.xml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/modules/login/views/login_view.dart b/lib/modules/login/views/login_view.dart index c38fae252..014740d9c 100644 --- a/lib/modules/login/views/login_view.dart +++ b/lib/modules/login/views/login_view.dart @@ -486,15 +486,14 @@ class LoginView extends GetView { if (controller.isChinaMainland.value) ...[ _buildSocialButton( FontAwesomeIcons.weixin, Colors.green, isDarkMode), - SizedBox(width: 24.w) ] else ...[ _buildSocialButton( FontAwesomeIcons.google, Colors.red, isDarkMode), - SizedBox(width: 24.w), ], // Apple登录按钮(仅在iOS上显示) if (GetPlatform.isIOS) ...[ + SizedBox(width: 24.w), _buildSocialButton( FontAwesomeIcons.apple, Colors.black, isDarkMode), ], diff --git a/local_plugins/jl_opus/android/src/main/AndroidManifest.xml b/local_plugins/jl_opus/android/src/main/AndroidManifest.xml index ee906a1ee..331a4778c 100644 --- a/local_plugins/jl_opus/android/src/main/AndroidManifest.xml +++ b/local_plugins/jl_opus/android/src/main/AndroidManifest.xml @@ -5,5 +5,5 @@ - + \ No newline at end of file From 50b9f477dee361e4676ded202ad27dd1ea144687 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Tue, 29 Jul 2025 19:21:53 +0800 Subject: [PATCH 4/6] =?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/modules/meeting/views/meeting_mine_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/meeting/views/meeting_mine_view.dart b/lib/modules/meeting/views/meeting_mine_view.dart index c2aa57307..9a6c8363c 100644 --- a/lib/modules/meeting/views/meeting_mine_view.dart +++ b/lib/modules/meeting/views/meeting_mine_view.dart @@ -46,7 +46,7 @@ class _MeetingMineViewState extends State ), child: SafeArea( child: ListView( - padding: EdgeInsets.symmetric(horizontal: 12.w), + padding: EdgeInsets.fromLTRB(12.w, 0, 12.w, 90.w), children: [ _head(), _statistics(), From b28f50e864ca2507b5e7dd7cd20347594beab872 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Tue, 29 Jul 2025 20:33:57 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix(=E9=9A=90=E7=A7=81):=20=E7=A6=81?= =?UTF-8?q?=E7=94=A8Facebook=E7=99=BB=E5=BD=95=E5=92=8C=E5=B9=BF=E5=91=8A?= =?UTF-8?q?=E7=9A=84=E7=94=A8=E6=88=B7=E8=B7=9F=E8=B8=AA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在iOS的Info.plist中明确禁用SKAdNetwork和广告跟踪端点 修改Facebook登录跟踪设置为limited模式 更新Podfile配置以禁用IDFA收集和自动日志事件 移除NSUserTrackingUsageDescription权限描述 --- ios/Podfile | 22 +++++++++++++++++-- ios/Runner/Info.plist | 6 +++++ .../login/controllers/facebookLogin.dart | 2 +- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ios/Podfile b/ios/Podfile index ad48460a9..9591f8465 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -41,7 +41,7 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - + # Ensure all pods use the same iOS deployment target target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0' @@ -56,7 +56,7 @@ post_install do |installer| ## dart: PermissionGroup.calendar 'PERMISSION_EVENTS=1', - + ## dart: PermissionGroup.calendarFullAccess 'PERMISSION_EVENTS_FULL_ACCESS=1', @@ -66,6 +66,24 @@ post_install do |installer| ## dart: PermissionGroup.bluetooth 'PERMISSION_BLUETOOTH=1', ] + + # 禁用用户跟踪相关功能 + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'FBSDK_DISABLE_IDFA=1' + + # 移除可能的用户跟踪权限描述 + if target.name == 'Runner' + # 确保不包含NSUserTrackingUsageDescription + config.build_settings.delete('NSUserTrackingUsageDescription') + end + end + + # 针对Facebook SDK禁用广告ID收集 + if target.name.start_with?('FBSDKCoreKit') || target.name.start_with?('FacebookSDK') + target.build_configurations.each do |config| + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)'] + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'FBSDK_DISABLE_IDFA=1' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'FBSDK_DISABLE_AUTO_LOG_APP_EVENTS=1' + end end end end diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index aff31ef06..3d78b7eda 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -57,6 +57,12 @@ FacebookClientToken 193d0a2b1757c41cfa557ea8e8f21c33 + + SKAdNetworkItems + + + NSAdvertisingAttributionReportEndpoint + LSApplicationQueriesSchemes weixin diff --git a/lib/modules/login/controllers/facebookLogin.dart b/lib/modules/login/controllers/facebookLogin.dart index 59e701faf..2ec25147f 100644 --- a/lib/modules/login/controllers/facebookLogin.dart +++ b/lib/modules/login/controllers/facebookLogin.dart @@ -56,7 +56,7 @@ class Facebook { final nonce = sha256ofString(rawNonce); final LoginResult loginResult = await FacebookAuth.instance .login( - loginTracking: LoginTracking.enabled, + loginTracking: LoginTracking.limited, // 改为limited,禁用用户跟踪 nonce: nonce, ) .catchError((onError) { From de4fc6f44d7d8de8d6309e15c5d39f0b568012be Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Tue, 29 Jul 2025 20:53:39 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E4=B8=8D=E8=83=BD=E5=9B=9E=E9=9F=B3=E6=B6=88?= =?UTF-8?q?=E9=99=A4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../azure_speech/AzureAsrHelper.kt | 98 ++++++++++++++----- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt index 35fa385de..02d909d4a 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt @@ -13,6 +13,10 @@ import com.microsoft.cognitiveservices.speech.* import com.microsoft.cognitiveservices.speech.util.EventHandler import android.media.AudioManager +import android.media.AudioAttributes +import android.media.AudioFocusRequest +import android.os.Handler +import android.os.Looper import com.microsoft.cognitiveservices.speech.audio.PullAudioInputStreamCallback import com.microsoft.cognitiveservices.speech.audio.AudioStreamFormat import java.util.concurrent.BlockingQueue @@ -764,31 +768,23 @@ class AzureAsrHelper(private val context: Context) { private fun runMicrophoneCapture() { try { + // 首先设置通话音频模式 + setupCommunicationAudioMode() + val supportedSampleRates = intArrayOf(16000, 8000, 11025, 22050, 44100) - // 查找设备支持的最佳采样率 val sampleRate = supportedSampleRates.firstOrNull { rate -> - val bufferSize = AudioRecord.getMinBufferSize( - rate, - channelConfig, - audioFormat - ) - bufferSize > 0 // 返回正值表示支持 - } ?: 16000 // 默认回退值 + val bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat) + bufferSize > 0 + } ?: 16000 + val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) - // 计算最小缓冲区大小(避免出现音频欠载) - val minBufferSize = AudioRecord.getMinBufferSize( - sampleRate, - channelConfig, - audioFormat - ) -// 创建录音对象 + // 使用VOICE_COMMUNICATION音频源(专为VoIP优化) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - Log.d("AzureASR", "android.os.Build.VERSION.SDK_INT") val format = AudioFormat.Builder() .setSampleRate(sampleRate) .setEncoding(audioFormat) - .setChannelMask(channelConfig) + .setChannelMask(AudioFormat.CHANNEL_IN_MONO) .build() audioRecord = AudioRecord.Builder() @@ -797,29 +793,27 @@ class AzureAsrHelper(private val context: Context) { .setBufferSizeInBytes(minBufferSize * 2) .build() } else { - // 创建 AudioRecord 对象 audioRecord = AudioRecord( - MediaRecorder.AudioSource.VOICE_RECOGNITION, // 语音识别专用音源(自带降噪/回声消除) + MediaRecorder.AudioSource.VOICE_COMMUNICATION, sampleRate, channelConfig, audioFormat, - minBufferSize * 2 // 使用两倍缓冲区防止溢出 + minBufferSize * 2 ) - } - // 检查初始化状态 + if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) { throw IllegalStateException("AudioRecord初始化失败") } - - // 4. 开始录音 audioRecord?.startRecording() - Log.d("TAG", "录音开始,采样率: $sampleRate Hz") - + + Log.d("TAG", "通话模式录音开始,采样率: $sampleRate Hz") } catch (e: Exception) { Log.e("TAG", "音频捕获异常: ${e.message}") + // 出错时恢复音频模式 + restoreCommunicationAudioMode() } } @@ -981,5 +975,55 @@ class AzureAsrHelper(private val context: Context) { fun onError(error: String) } + /** + * 配置通话音频模式以优化回声消除 + */ + private fun setupCommunicationAudioMode() { + try { + audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + + // 保存原始音频模式 + originalAudioMode = audioManager?.mode ?: AudioManager.MODE_NORMAL + + // 设置通话模式 - 这是关键! + audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION + + // 启用扬声器(如果需要外放) + audioManager?.isSpeakerphoneOn = true + + // 请求音频焦点 + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) + .setAudioAttributes( + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build() + ) + .build() + + audioManager?.requestAudioFocus(focusRequest) + } + + Log.d("AudioMode", "通话音频模式已设置: MODE_IN_COMMUNICATION") + + } catch (e: Exception) { + Log.e("AudioMode", "设置通话音频模式失败: ${e.message}") + } + } -} \ No newline at end of file + /** + * 恢复原始音频模式 + */ + private fun restoreCommunicationAudioMode() { + try { + audioManager?.mode = originalAudioMode + audioManager?.isSpeakerphoneOn = false + // 释放音频焦点 + Log.d("AudioMode", "音频模式已恢复") + } catch (e: Exception) { + Log.e("AudioMode", "恢复音频模式失败: ${e.message}") + } + } +} + \ No newline at end of file