|
|
@ -715,54 +715,54 @@ class AgentController extends GetxController { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 检查麦克风权限 |
|
|
// 检查麦克风权限,返回true表示已授权,false表示未授权 |
|
|
Future<bool> _checkMicrophonePermission() async { |
|
|
Future<bool> _checkMicrophonePermission() async { |
|
|
try { |
|
|
try { |
|
|
// iOS平台直接返回true,不处理权限问题 |
|
|
// 1.先获取当前麦克风权限状态 |
|
|
if (Platform.isIOS) { |
|
|
final micStatus = await Permission.microphone.status; |
|
|
|
|
|
|
|
|
|
|
|
// 2.如果麦克风权限已授予,返回true |
|
|
|
|
|
if (micStatus.isGranted) { |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 创建待请求权限列表 |
|
|
// 3.创建待请求权限列表(这里只请求麦克风权限) |
|
|
List<Permission> permissionsToRequest = []; |
|
|
List<Permission> permissionsToRequest = []; |
|
|
|
|
|
|
|
|
// Android 需要考虑SDK版本不同的权限要求 |
|
|
|
|
|
permissionsToRequest.add(Permission.microphone); |
|
|
permissionsToRequest.add(Permission.microphone); |
|
|
|
|
|
|
|
|
// 批量请求所有需要的权限 |
|
|
// 4.弹出权限说明对话框(首次拒绝/还没授权时展示) |
|
|
|
|
|
if (micStatus.isPermanentlyDenied) { |
|
|
|
|
|
_showPermissionDialog(true); |
|
|
|
|
|
} else { |
|
|
|
|
|
_showPermissionDialog(false); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 5.请求权限,并获取返回的权限状态集合 |
|
|
final statuses = await permissionsToRequest.request(); |
|
|
final statuses = await permissionsToRequest.request(); |
|
|
|
|
|
|
|
|
// 检查是否所有权限都已授予 |
|
|
// 6.用于记录被拒绝的权限(便于后续判断) |
|
|
bool allGranted = true; |
|
|
|
|
|
Permission? deniedPermission; |
|
|
Permission? deniedPermission; |
|
|
|
|
|
|
|
|
|
|
|
// 7.遍历权限状态集合,检查是否有任何权限被拒绝 |
|
|
statuses.forEach((permission, status) { |
|
|
statuses.forEach((permission, status) { |
|
|
if (!status.isGranted) { |
|
|
if (!status.isGranted) { |
|
|
allGranted = false; |
|
|
|
|
|
deniedPermission = permission; |
|
|
deniedPermission = permission; |
|
|
Logger.e(TAG, '权限 ${permission.toString()} 未被授予: $status'); |
|
|
Logger.e(TAG, '权限 ${permission.toString()} 未被授予: $status'); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// 单独检查麦克风权限状态,因为这是主要需求 |
|
|
// // 8. 如果是“永久拒绝”(用户勾选了“不再询问”) |
|
|
final micStatus = await Permission.microphone.status; |
|
|
// if (micStatus.isPermanentlyDenied) { |
|
|
|
|
|
// _showPermissionDialog(true); |
|
|
|
|
|
// return false; |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
// 如果所有权限都已授予,或至少麦克风权限已授予,返回true |
|
|
// // 9. 普通的拒绝情况,再次弹出说明对话框 |
|
|
if (allGranted || micStatus.isGranted) { |
|
|
// if (deniedPermission != null) { |
|
|
return true; |
|
|
// _showPermissionDialog(false); |
|
|
} |
|
|
// } |
|
|
|
|
|
|
|
|
// 处理特定权限被永久拒绝的情况 |
|
|
|
|
|
if (micStatus.isPermanentlyDenied) { |
|
|
|
|
|
_showPermissionDialog(true); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 普通拒绝情况 |
|
|
|
|
|
if (deniedPermission != null) { |
|
|
|
|
|
_showPermissionDialog(false); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 10. 如果权限未被授予,返回 false |
|
|
return false; |
|
|
return false; |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.e(TAG, '检查麦克风权限失败: $e'); |
|
|
Logger.e(TAG, '检查麦克风权限失败: $e'); |
|
|
@ -770,6 +770,87 @@ class AgentController extends GetxController { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 显示权限提示对话框 |
|
|
|
|
|
void _showPermissionDialog(bool isPermanent) { |
|
|
|
|
|
Get.generalDialog( |
|
|
|
|
|
barrierDismissible: false, |
|
|
|
|
|
barrierLabel: '权限提示', |
|
|
|
|
|
transitionBuilder: (context, anim1, anim2, child) { |
|
|
|
|
|
return SlideTransition( |
|
|
|
|
|
position: Tween<Offset>( |
|
|
|
|
|
begin: Offset(0, -1), // 从上方滑入 |
|
|
|
|
|
end: Offset(0, 0), |
|
|
|
|
|
).animate(CurvedAnimation(parent: anim1, curve: Curves.easeOut)), |
|
|
|
|
|
child: child, |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
|
|
|
pageBuilder: (context, anim1, anim2) { |
|
|
|
|
|
return SafeArea( |
|
|
|
|
|
child: Align( |
|
|
|
|
|
alignment: Alignment.topCenter, // 关键:设置为顶部对齐 |
|
|
|
|
|
child: Padding( |
|
|
|
|
|
padding: const EdgeInsets.only(top: 80, left: 24, right: 24), |
|
|
|
|
|
child: Material( |
|
|
|
|
|
borderRadius: BorderRadius.circular(12), |
|
|
|
|
|
color: Colors.white, |
|
|
|
|
|
child: Padding( |
|
|
|
|
|
padding: const EdgeInsets.all(16), |
|
|
|
|
|
child: ConstrainedBox( |
|
|
|
|
|
constraints: BoxConstraints(maxWidth: 400), |
|
|
|
|
|
child: Column( |
|
|
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
|
|
|
children: [ |
|
|
|
|
|
const Text('需要麦克风权限', |
|
|
|
|
|
style: TextStyle( |
|
|
|
|
|
fontSize: 18, fontWeight: FontWeight.bold)), |
|
|
|
|
|
const SizedBox(height: 12), |
|
|
|
|
|
Text(isPermanent |
|
|
|
|
|
? '语音功能需要麦克风权限。请前往设置页面手动开启权限。' |
|
|
|
|
|
: '语音功能需要麦克风权限。请允许使用麦克风权限以继续使用语音功能。'), |
|
|
|
|
|
const SizedBox(height: 16), |
|
|
|
|
|
const Text('权限使用说明:', |
|
|
|
|
|
style: TextStyle(fontWeight: FontWeight.bold)), |
|
|
|
|
|
const SizedBox(height: 8), |
|
|
|
|
|
const Text('• 麦克风权限:用于录制您的语音,实现语音翻译、语音助手对话等核心功能'), |
|
|
|
|
|
const SizedBox(height: 8), |
|
|
|
|
|
const Text('• 我们仅在您主动使用语音功能时访问麦克风'), |
|
|
|
|
|
const SizedBox(height: 8), |
|
|
|
|
|
const Text('• 您可以随时在系统设置中关闭权限'), |
|
|
|
|
|
const SizedBox(height: 20), |
|
|
|
|
|
Row( |
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.end, |
|
|
|
|
|
children: [ |
|
|
|
|
|
TextButton( |
|
|
|
|
|
onPressed: () => Get.back(), |
|
|
|
|
|
child: const Text('取消'), |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 8), |
|
|
|
|
|
TextButton( |
|
|
|
|
|
onPressed: () { |
|
|
|
|
|
Get.back(); |
|
|
|
|
|
if (isPermanent) { |
|
|
|
|
|
AppSettings.openAppSettings(); |
|
|
|
|
|
} else { |
|
|
|
|
|
Permission.microphone.request(); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
child: Text(isPermanent ? '前往设置' : '授权'), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 获取Android SDK版本 |
|
|
// 获取Android SDK版本 |
|
|
Future<int> _getAndroidSdkVersion() async { |
|
|
Future<int> _getAndroidSdkVersion() async { |
|
|
if (Platform.isAndroid) { |
|
|
if (Platform.isAndroid) { |
|
|
@ -784,35 +865,6 @@ class AgentController extends GetxController { |
|
|
return 0; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 显示权限提示对话框 |
|
|
|
|
|
void _showPermissionDialog(bool isPermanent) { |
|
|
|
|
|
Get.dialog( |
|
|
|
|
|
AlertDialog( |
|
|
|
|
|
title: const Text('需要麦克风权限'), |
|
|
|
|
|
content: Text(isPermanent |
|
|
|
|
|
? '语音功能需要麦克风权限。请前往设置页面手动开启权限。' |
|
|
|
|
|
: '语音功能需要麦克风权限。请允许使用麦克风权限以继续使用语音功能。'), |
|
|
|
|
|
actions: [ |
|
|
|
|
|
TextButton( |
|
|
|
|
|
onPressed: () => Get.back(), |
|
|
|
|
|
child: const Text('取消'), |
|
|
|
|
|
), |
|
|
|
|
|
TextButton( |
|
|
|
|
|
onPressed: () { |
|
|
|
|
|
Get.back(); |
|
|
|
|
|
if (isPermanent) { |
|
|
|
|
|
AppSettings.openAppSettings(); |
|
|
|
|
|
} else { |
|
|
|
|
|
Permission.microphone.request(); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
child: Text(isPermanent ? '前往设置' : '授权'), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 开始语音输入 |
|
|
// 开始语音输入 |
|
|
Future<void> startVoiceInput() async { |
|
|
Future<void> startVoiceInput() async { |
|
|
if (isListening.value) return; |
|
|
if (isListening.value) return; |
|
|
|