import 'dart:async'; import 'dart:developer' as developer; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import '../../data/services/voice_recognition_service.dart'; class SpeechDemoPage extends StatefulWidget { const SpeechDemoPage({Key? key}) : super(key: key); @override State createState() => _SpeechDemoPageState(); } class _SpeechDemoPageState extends State { final VoiceRecognitionService _recognitionService = Get.find(); final RxBool _permissionsGranted = false.obs; final RxBool _isInitializing = false.obs; final RxBool _isRecognizing = false.obs; final RxString _errorMessage = ''.obs; final RxString _recognizedText = ''.obs; final RxString _status = ''.obs; // 添加原生权限通道 - 只用于处理Android 11+的MANAGE_EXTERNAL_STORAGE权限 static const MethodChannel _permissionChannel = MethodChannel('com.example.deep_voice/permissions'); @override void initState() { super.initState(); _setupPermissionListener(); _checkPermissions(); } @override void dispose() { _recognitionService.stopRecognition(); super.dispose(); } // 设置权限监听器 - 只监听MANAGE_EXTERNAL_STORAGE权限结果 void _setupPermissionListener() { _permissionChannel.setMethodCallHandler((call) async { if (call.method == 'manageExternalStorageResult') { developer.log('Received manageExternalStorageResult from native: ${call.arguments}'); // 更新权限状态 await _checkPermissions(); } return null; }); } // 检查所有必要的权限 Future _checkPermissions() async { developer.log('Checking permissions'); try { // 检查基本权限 final micStatus = await Permission.microphone.status; final storageStatus = await Permission.storage.status; developer.log('=== 详细权限状态 ==='); developer.log('麦克风权限: $micStatus'); developer.log('存储权限: $storageStatus'); // 对于Android 11+,检查MANAGE_EXTERNAL_STORAGE权限 bool manageStorageGranted = true; if (Platform.isAndroid) { if (await _isAndroid11OrHigher()) { // 使用原生方法检查MANAGE_EXTERNAL_STORAGE权限 try { manageStorageGranted = await _permissionChannel.invokeMethod('isExternalStorageManager') ?? false; } catch (e) { developer.log('Error checking MANAGE_EXTERNAL_STORAGE: $e'); manageStorageGranted = await Permission.manageExternalStorage.isGranted; } developer.log('管理所有文件权限: $manageStorageGranted'); } } // 记录其他权限状态 developer.log('读取外部存储权限: ${await Permission.audio.status}'); developer.log('写入外部存储权限: ${await Permission.photos.status}'); developer.log('====================='); // 更新权限状态 final bool basicPermissionsGranted = micStatus.isGranted && storageStatus.isGranted; // 修改逻辑:如果是Android 11+且已授予MANAGE_EXTERNAL_STORAGE权限,则忽略普通存储权限状态 bool effectiveStoragePermission = storageStatus.isGranted; if (Platform.isAndroid && await _isAndroid11OrHigher() && manageStorageGranted) { developer.log('已授予MANAGE_EXTERNAL_STORAGE权限,忽略普通存储权限状态'); effectiveStoragePermission = true; } _permissionsGranted.value = micStatus.isGranted && (effectiveStoragePermission || manageStorageGranted); developer.log('所有权限已授予: ${_permissionsGranted.value}'); // 如果权限已授予,初始化引擎 if (_permissionsGranted.value && !_isInitializing.value) { _initializeEngine(); } else if (!_permissionsGranted.value) { _errorMessage.value = '需要麦克风和存储权限才能使用语音识别功能'; } } catch (e) { developer.log('检查权限时出错: $e'); _errorMessage.value = '检查权限时出错: $e'; } } // 检查是否为Android 11或更高版本 Future _isAndroid11OrHigher() async { if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; return androidInfo.version.sdkInt >= 30; // Android 11 is API 30 } return false; } // 记录详细的权限状态 Future _logPermissionDetails() async { developer.log('=== 详细权限状态 ==='); developer.log('麦克风权限: ${await Permission.microphone.status}'); developer.log('存储权限: ${await Permission.storage.status}'); if (Platform.isAndroid) { if (await _isAndroid11OrHigher()) { bool manageStorageGranted = false; try { manageStorageGranted = await _permissionChannel.invokeMethod('isExternalStorageManager') ?? false; } catch (e) { developer.log('Error checking MANAGE_EXTERNAL_STORAGE: $e'); manageStorageGranted = await Permission.manageExternalStorage.isGranted; } developer.log('管理所有文件权限: $manageStorageGranted'); } } developer.log('读取外部存储权限: ${await Permission.audio.status}'); developer.log('写入外部存储权限: ${await Permission.photos.status}'); developer.log('====================='); } // 请求所有必要的权限 Future _requestPermissions() async { developer.log('请求权限'); try { // 对于Android 11+,优先请求MANAGE_EXTERNAL_STORAGE权限 if (Platform.isAndroid && await _isAndroid11OrHigher()) { bool isManager = false; try { isManager = await _permissionChannel.invokeMethod('isExternalStorageManager') ?? false; } catch (e) { developer.log('检查MANAGE_EXTERNAL_STORAGE时出错: $e'); isManager = await Permission.manageExternalStorage.isGranted; } if (!isManager) { developer.log('请求MANAGE_EXTERNAL_STORAGE权限'); // 使用原生方法请求MANAGE_EXTERNAL_STORAGE权限 await _permissionChannel.invokeMethod('requestManageExternalStorage'); // 等待一段时间,让用户有机会授予权限 await Future.delayed(const Duration(seconds: 1)); // 再次检查权限状态 try { isManager = await _permissionChannel.invokeMethod('isExternalStorageManager') ?? false; developer.log('MANAGE_EXTERNAL_STORAGE权限状态: $isManager'); } catch (e) { developer.log('检查MANAGE_EXTERNAL_STORAGE时出错: $e'); } } } // 请求基本权限 Map statuses = await [ Permission.microphone, Permission.storage, ].request(); // 记录结果 statuses.forEach((permission, status) { developer.log('权限 $permission: $status'); }); // 检查权限状态 await _checkPermissions(); } catch (e) { developer.log('请求权限时出错: $e'); _errorMessage.value = '请求权限时出错: $e'; } } // 打开应用设置 Future _openAppSettings() async { developer.log('打开应用设置'); try { // 使用permission_handler打开设置 final opened = await openAppSettings(); if (!opened) { // 如果permission_handler失败,使用原生方法 await _permissionChannel.invokeMethod('openAppSettings'); } } catch (e) { developer.log('打开应用设置时出错: $e'); // 使用原生方法作为备选 try { await _permissionChannel.invokeMethod('openAppSettings'); } catch (e2) { developer.log('通过原生通道打开应用设置时出错: $e2'); } } } // 初始化语音引擎 Future _initializeEngine() async { if (_isInitializing.value) return; _isInitializing.value = true; _errorMessage.value = ''; _status.value = '正在初始化语音引擎...'; try { // 再次检查权限状态 await _checkPermissions(); if (!_permissionsGranted.value) { developer.log('权限未授予,无法初始化引擎'); _errorMessage.value = '权限未授予,无法初始化语音引擎'; _isInitializing.value = false; return; } // 记录权限详情 await _logPermissionDetails(); // 测试存储权限 String? availableDir = await _getAvailableStorageDirectory(); if (availableDir != null) { developer.log('找到可用的存储目录: $availableDir'); } else { developer.log('警告: 未找到可用的存储目录,可能会影响语音引擎初始化'); } // 初始化语音引擎 developer.log('初始化语音识别引擎'); await _recognitionService.initialize(); developer.log('语音识别引擎初始化成功'); _status.value = '语音引擎初始化成功,可以开始识别'; _isInitializing.value = false; } catch (e, stackTrace) { developer.log('初始化语音识别引擎时出错: $e'); developer.log('堆栈跟踪: $stackTrace'); // 解析错误代码 String errorDetail = '未知错误'; if (e is PlatformException) { developer.log('PlatformException 代码: ${e.code}, 消息: ${e.message}'); if (e.code == 'INIT_ERROR') { if (e.message?.contains('-7') == true) { errorDetail = '无法创建日志目录 (错误码: -7),可能是存储权限问题'; } else { errorDetail = '初始化错误: ${e.message}'; } } } _errorMessage.value = '初始化语音引擎失败: $errorDetail'; _status.value = '初始化失败'; _isInitializing.value = false; // 显示错误提示和重试选项 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('初始化失败: $errorDetail'), action: SnackBarAction( label: '重试', onPressed: _initializeEngine, ), duration: const Duration(seconds: 10), ), ); } } Future _startRecognition() async { developer.log('开始识别'); _errorMessage.value = ''; _isRecognizing.value = true; _status.value = '正在识别...'; try { await _recognitionService.startRecognition(); // 设置监听器接收识别结果 _recognitionService.recognizedText.listen((text) { _recognizedText.value = text; }); } catch (e) { developer.log('开始识别时出错: $e'); _errorMessage.value = '启动识别失败: $e'; _isRecognizing.value = false; _status.value = '识别启动失败'; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('启动识别失败: $e'), duration: const Duration(seconds: 3), ), ); } } Future _stopRecognition() async { developer.log('停止识别'); try { await _recognitionService.stopRecognition(); _isRecognizing.value = false; _status.value = '识别已停止'; // 保存最终识别结果 _recognizedText.value = _recognitionService.recognizedText.value; } catch (e) { developer.log('停止识别时出错: $e'); _errorMessage.value = '停止识别失败: $e'; _isRecognizing.value = false; _status.value = '识别停止失败'; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('停止识别失败: $e'), duration: const Duration(seconds: 3), ), ); } } // 获取可用的存储目录 Future _getAvailableStorageDirectory() async { try { List directoriesToTry = []; // 应用专用外部存储目录 if (Platform.isAndroid) { directoriesToTry.add(Directory('/storage/emulated/0/Android/data/com.example.deep_voice/files')); } // 应用内部存储目录 try { final appDir = await getApplicationDocumentsDirectory(); directoriesToTry.add(appDir); } catch (e) { developer.log('获取应用内部存储目录失败: $e'); } // 应用缓存目录 try { final cacheDir = await getTemporaryDirectory(); directoriesToTry.add(cacheDir); } catch (e) { developer.log('获取应用缓存目录失败: $e'); } // 尝试每个目录 for (var directory in directoriesToTry) { try { developer.log('测试目录可用性: ${directory.path}'); if (!await directory.exists()) { await directory.create(recursive: true); } final testFile = File('${directory.path}/test_write.tmp'); await testFile.writeAsString('Test write at ${DateTime.now()}'); await testFile.readAsString(); // 验证可读 await testFile.delete(); developer.log('找到可用目录: ${directory.path}'); return directory.path; } catch (e) { developer.log('目录 ${directory.path} 不可用: $e'); } } developer.log('未找到可用的存储目录'); return null; } catch (e) { developer.log('获取可用存储目录时出错: $e'); return null; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('语音识别演示'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 权限请求部分 Obx(() => !_permissionsGranted.value ? Card( color: Colors.amber.shade100, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '需要麦克风和存储权限', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 8), const Text( '注意: 在Android 11及以上版本,您需要在设置中手动授予"管理所有文件"权限', style: TextStyle( fontSize: 14, ), ), const SizedBox(height: 16), Wrap( spacing: 8.0, runSpacing: 8.0, alignment: WrapAlignment.center, children: [ ElevatedButton( onPressed: _requestPermissions, child: const Text('授予权限'), ), ElevatedButton( onPressed: _openAppSettings, child: const Text('打开设置'), ), ElevatedButton( onPressed: _logPermissionDetails, child: const Text('检查权限'), ), ElevatedButton( onPressed: _initializeEngine, child: const Text('强制初始化'), ), ], ), ], ), ), ) : const SizedBox.shrink()), // 状态显示 Obx(() => _status.value.isNotEmpty ? Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( '状态: ${_status.value}', style: const TextStyle( fontWeight: FontWeight.bold, ), ), ) : const SizedBox.shrink()), // 错误消息 Obx(() => _errorMessage.value.isNotEmpty ? Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( '错误: ${_errorMessage.value}', style: const TextStyle( color: Colors.red, fontWeight: FontWeight.bold, ), ), ) : const SizedBox.shrink()), // 识别结果显示区域 Expanded( child: Card( child: Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: Obx(() => Text( _recognizedText.value.isEmpty ? '识别结果将显示在这里...' : _recognizedText.value, style: const TextStyle( fontSize: 18.0, ), )), ), ), ), ), // 控制按钮 Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Obx(() => ElevatedButton( onPressed: _permissionsGranted.value && !_isRecognizing.value && !_isInitializing.value ? _startRecognition : null, child: const Text('开始识别'), )), Obx(() => ElevatedButton( onPressed: _isRecognizing.value ? _stopRecognition : null, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, ), child: const Text('停止识别'), )), ], ), ), ], ), ), ); } }