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/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/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) {
diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart
index c17147ef4..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, // 欢迎页面
@@ -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
@@ -275,170 +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();
- _initDeviceInfo();
- _initAppInfo(); // 新增:初始化APP信息
- _checkGuestLoginConfig(); // 检查游客登录配置
- },
- 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/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/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(),
diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart
index 87e5f4f1c..ef031b871 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,8 @@ 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 {
final GetStorage _storage = GetStorage();
@@ -21,6 +24,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 +35,47 @@ 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() {
+ PrivacyPolicyDialog.show(
+ onAccept: () {
+ _onPrivacyAccepted();
+ _hasAcceptedPrivacy.value = true;
+ },
+ onReject: _onPrivacyRejected,
+ canDismiss: false, // 禁止通过返回键关闭弹窗
+ );
+ }
+
// 初始化地图SDK
// void _initMapSDK() {
// try {
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
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