From b56d4698b991e43c167dc2410c5ce0f6a9a31a67 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Sat, 16 Aug 2025 12:54:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=B9=B6=E6=B7=BB=E5=8A=A0=E6=96=B0=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将权限管理功能从设置模块分离为独立模块,新增蓝牙、通知、存储和麦克风权限支持。优化权限分组显示逻辑,根据平台显示不同权限组,并完善权限状态管理和多语言支持。 - 新增权限分组模型和独立控制器 - 添加蓝牙、通知等新权限支持 - 优化权限请求流程和状态显示 - 完善中英文多语言翻译 - 移除旧版权限视图并更新路由配置 --- lib/core/translations/language/en_us.dart | 14 + lib/core/translations/language/zh_cn.dart | 11 + lib/data/services/music_manager.dart | 2 +- lib/data/services/novice_guide_service.dart | 2 +- .../agent/controllers/agent_controller.dart | 28 +- .../bindings/permissions_binding.dart | 9 + .../controllers/permissions_controller.dart | 222 ++++++++++ .../permissions/models/permission_group.dart | 67 +++ .../permissions/views/permissions_view.dart | 276 ++++++++++++ .../controllers/settings_controller.dart | 97 ----- .../settings/views/permissions_view.dart | 402 ------------------ lib/routes/app_pages.dart | 5 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 14 files changed, 622 insertions(+), 517 deletions(-) create mode 100644 lib/modules/permissions/bindings/permissions_binding.dart create mode 100644 lib/modules/permissions/controllers/permissions_controller.dart create mode 100644 lib/modules/permissions/models/permission_group.dart create mode 100644 lib/modules/permissions/views/permissions_view.dart delete mode 100644 lib/modules/settings/views/permissions_view.dart diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart index 45071ebdd..a0ef8f942 100644 --- a/lib/core/translations/language/en_us.dart +++ b/lib/core/translations/language/en_us.dart @@ -487,11 +487,18 @@ const Map enUS = { // 权限管理页面 'getPermissionStatusFailed': 'Failed to get permission status', 'communicationPermissions': 'Communication Permissions', + 'bluetoothPermissions': 'Bluetooth Permissions', + 'notificationPermissions': 'Notification Permissions', + 'storagePermissions': 'Storage Permissions', + 'microphonePermissions': 'Microphone Permissions', 'alarmPermissions': 'Alarm Permissions', 'calendarPermissions': 'Calendar Permissions', 'locationPermissions': 'Location Permissions', 'phone': 'Phone', 'sms': 'SMS', + 'contacts': 'Contacts', + //'bluetooth': 'Bluetooth', + 'notification': 'Notification', 'alarm': 'Alarm', 'calendarWrite': 'Calendar Write', 'calendarFullAccess': 'Calendar Full Access', @@ -521,6 +528,13 @@ const Map enUS = { 'contactsPermissionDesc': 'Used for accessing contact information', 'contactsPermissionDetailDesc': 'Phone call and SMS functionality requires contacts permission to select contacts', + 'bluetoothPermissionDesc': + 'Used for connecting and managing Bluetooth devices', + 'bluetoothPermissionDetailDesc': + 'Connecting to Bluetooth headphones and other devices requires Bluetooth permission', + 'notificationPermissionDesc': 'Used for sending notification messages', + 'notificationPermissionDetailDesc': + 'App needs notification permission to send you important alerts and messages', 'calendarWritePermissionDetailDesc': 'Creating calendar events requires calendar write permission', 'calendarFullAccessPermissionDetailDesc': diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart index 986e8cd14..3006dbb9a 100644 --- a/lib/core/translations/language/zh_cn.dart +++ b/lib/core/translations/language/zh_cn.dart @@ -455,11 +455,18 @@ const Map zhCN = { // 权限管理页面 'getPermissionStatusFailed': '获取权限状态失败', 'communicationPermissions': '通讯权限', + 'bluetoothPermissions': '蓝牙权限', + 'notificationPermissions': '通知权限', + 'storagePermissions': '存储权限', + 'microphonePermissions': '麦克风权限', 'alarmPermissions': '闹钟权限', 'calendarPermissions': '日历权限', 'locationPermissions': '位置权限', 'phone': '电话', 'sms': '短信', + 'contacts': '联系人', + //'bluetooth': '蓝牙', + 'notification': '通知', 'alarm': '闹钟', 'calendarWrite': '日历写入', 'calendarFullAccess': '日历完整访问', @@ -486,6 +493,10 @@ const Map zhCN = { 'locationPermissionDetailDesc': '获取位置信息需要位置权限', 'contactsPermissionDesc': '用于访问联系人信息', 'contactsPermissionDetailDesc': '拨打电话和发送短信功能需要联系人权限以选择联系人', + 'bluetoothPermissionDesc': '用于连接和管理蓝牙设备', + 'bluetoothPermissionDetailDesc': '连接蓝牙耳机等设备需要蓝牙权限', + 'notificationPermissionDesc': '用于发送通知消息', + 'notificationPermissionDetailDesc': '应用需要通知权限以向您发送重要提醒和消息', 'appNeedsPermissionForFullFunction': '应用需要此权限以提供完整功能', 'permanentDenialText': '您已永久拒绝{permission}权限,请前往应用设置开启', 'goToSettings': '去设置', diff --git a/lib/data/services/music_manager.dart b/lib/data/services/music_manager.dart index e761e1a37..4fb1b7343 100644 --- a/lib/data/services/music_manager.dart +++ b/lib/data/services/music_manager.dart @@ -22,7 +22,7 @@ class MusicManager extends GetxService { // Logger.error('无法获取 BuildContext'); // return; // } - requestNotificationPermission(); + //requestNotificationPermission(); Logger.info('初始化权限请求'); } diff --git a/lib/data/services/novice_guide_service.dart b/lib/data/services/novice_guide_service.dart index c58b1a312..be154d551 100644 --- a/lib/data/services/novice_guide_service.dart +++ b/lib/data/services/novice_guide_service.dart @@ -148,7 +148,7 @@ class NoviceGuideService extends GetxService { void startAIGuide() async { if (!isAIGuide) { - await Future.delayed(const Duration(milliseconds: 200)); + await Future.delayed(const Duration(milliseconds: 300)); isAIGuide = true; _storage.write('ai_guide', true); Intro.of(Get.context!).start(group: 'ai'); diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 009bad72d..e786100ac 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -1060,20 +1060,20 @@ class AgentController extends GetxController { Future startVoiceInput() async { if (isListening.value) return; Logger.i(TAG, '开始语音输入'); // 开始语音输入 - final hasPermission = await PermissionUtil.instance.requestPermission( - // context: Get.context!, - permissionType: Permission.microphone, - permissionName: 'microphonePermission'.tr, // 麦克风 - explanationText: 'voiceTranslationPermissionText'.tr, // 语音识别与翻译功能需要访问麦克风。 - permanentDenialText: - 'permissionDeniedText'.tr, // 您已永久拒绝麦克风权限,请前往系统设置开启权限。 - settingsButtonText: 'goToSettings'.tr, // 去设置 - cancelButtonText: 'cancel'.tr, // 取消 - ); - if (!hasPermission) { - isTextInputMode.value = true; // 如果没有权限,切回文本模式 - return; - } + // final hasPermission = await PermissionUtil.instance.requestPermission( + // // context: Get.context!, + // permissionType: Permission.microphone, + // permissionName: 'microphonePermission'.tr, // 麦克风 + // explanationText: 'voiceTranslationPermissionText'.tr, // 语音识别与翻译功能需要访问麦克风。 + // permanentDenialText: + // 'permissionDeniedText'.tr, // 您已永久拒绝麦克风权限,请前往系统设置开启权限。 + // settingsButtonText: 'goToSettings'.tr, // 去设置 + // cancelButtonText: 'cancel'.tr, // 取消 + // ); + // if (!hasPermission) { + // isTextInputMode.value = true; // 如果没有权限,切回文本模式 + // return; + // } try { await AgentService.recognizeCallback(); diff --git a/lib/modules/permissions/bindings/permissions_binding.dart b/lib/modules/permissions/bindings/permissions_binding.dart new file mode 100644 index 000000000..2b0547984 --- /dev/null +++ b/lib/modules/permissions/bindings/permissions_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import '../controllers/permissions_controller.dart'; + +class PermissionsBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => PermissionsController()); + } +} \ No newline at end of file diff --git a/lib/modules/permissions/controllers/permissions_controller.dart b/lib/modules/permissions/controllers/permissions_controller.dart new file mode 100644 index 000000000..fc483dfbe --- /dev/null +++ b/lib/modules/permissions/controllers/permissions_controller.dart @@ -0,0 +1,222 @@ +import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:flutter/material.dart'; +import 'dart:io' show Platform; +import '../../../core/utils/permission_util.dart'; + +class PermissionsController extends GetxController { + // 权限状态映射 + final RxMap permissionStatuses = + {}.obs; + + // 是否正在请求权限 + final RxBool isRequestingPermission = false.obs; + + @override + void onInit() { + super.onInit(); + checkAllPermissions(); + } + + /// 检查所有权限状态 + Future> checkAllPermissions() async { + final Map statuses = {}; + + // 根据平台获取需要检查的权限列表 + final permissions = _getPlatformPermissions(); + + for (final permission in permissions) { + try { + statuses[permission] = await permission.status; + } catch (e) { + // 某些权限在特定平台可能不支持,设置为拒绝状态 + statuses[permission] = PermissionStatus.denied; + } + } + + permissionStatuses.value = statuses; + return statuses; + } + + /// 获取平台特定的权限列表 + List _getPlatformPermissions() { + if (Platform.isIOS) { + return [ + Permission.calendarFullAccess, + Permission.location, + Permission.contacts, + Permission.bluetooth, + Permission.notification, + Permission.storage, + Permission.microphone, + ]; + } else if (Platform.isAndroid) { + return [ + Permission.calendarFullAccess, + Permission.location, + Permission.phone, + Permission.sms, + Permission.contacts, + Permission.bluetooth, + Permission.notification, + Permission.scheduleExactAlarm, + Permission.storage, + Permission.microphone, + ]; + } + return []; + } + + /// 请求单个权限 + Future requestPermission(Permission permission) async { + if (isRequestingPermission.value) return false; + + isRequestingPermission.value = true; + + try { + final permissionName = getPermissionName(permission); + final explanationText = _getDetailedDescription(permission); + + final hasPermission = await PermissionUtil.instance.requestPermission( + permissionType: permission, + permissionName: permissionName, + explanationText: explanationText, + permanentDenialText: 'permanentDenialText'.tr.replaceAll( + '{permission}', permissionName), + settingsButtonText: 'goToSettings'.tr, + cancelButtonText: 'cancel'.tr, + ); + + if (hasPermission) { + _showSuccessMessage(); + } + + // 刷新权限状态 + await checkAllPermissions(); + + return hasPermission; + } catch (e) { + _showErrorMessage(); + return false; + } finally { + isRequestingPermission.value = false; + } + } + + /// 打开系统设置 + void openSettings() { + openAppSettings(); + } + + /// 获取权限的用户友好名称 + String getPermissionName(Permission permission) { + switch (permission) { + case Permission.phone: + return 'phone'.tr; + case Permission.sms: + return 'sms'.tr; + case Permission.contacts: + return 'contacts'.tr; + case Permission.bluetooth: + return 'bluetooth'.tr; + case Permission.notification: + return 'notification'.tr; + case Permission.calendarWriteOnly: + return 'calendarWrite'.tr; + case Permission.calendarFullAccess: + return 'calendarFullAccess'.tr; + case Permission.location: + return 'locationInfo'.tr; + case Permission.scheduleExactAlarm: + return 'alarm'.tr; + case Permission.storage: + return 'storagePermission'.tr; + case Permission.microphone: + return 'microphonePermission'.tr; + default: + return permission.toString(); + } + } + + /// 获取权限说明文本 + String _getDetailedDescription(Permission permission) { + switch (permission) { + case Permission.phone: + return 'phonePermissionDetailDesc'.tr; + case Permission.sms: + return 'smsPermissionDetailDesc'.tr; + case Permission.contacts: + return 'contactsPermissionDetailDesc'.tr; + case Permission.bluetooth: + return 'bluetoothPermissionDetailDesc'.tr; + case Permission.notification: + return 'notificationPermissionDetailDesc'.tr; + case Permission.calendarWriteOnly: + return 'calendarWritePermissionDetailDesc'.tr; + case Permission.calendarFullAccess: + return 'calendarFullAccessPermissionDetailDesc'.tr; + case Permission.location: + return 'locationPermissionDetailDesc'.tr; + case Permission.scheduleExactAlarm: + return 'alarmPermissionDetailDesc'.tr; + case Permission.storage: + return 'storagePermissionExplanation'.tr; + case Permission.microphone: + return 'microphonePermissionExplanation'.tr; + default: + return 'appNeedsPermissionForFullFunction'.tr; + } + } + + /// 获取权限描述 + String getPermissionDescription(Permission permission) { + switch (permission) { + case Permission.phone: + return 'phonePermissionDesc'.tr; + case Permission.sms: + return 'smsPermissionDesc'.tr; + case Permission.contacts: + return 'contactsPermissionDesc'.tr; + case Permission.bluetooth: + return 'bluetoothPermissionDesc'.tr; + case Permission.notification: + return 'notificationPermissionDesc'.tr; + case Permission.calendarWriteOnly: + return 'calendarWritePermissionDesc'.tr; + case Permission.calendarFullAccess: + return 'calendarFullAccessPermissionDesc'.tr; + case Permission.location: + return 'locationPermissionDesc'.tr; + case Permission.scheduleExactAlarm: + return 'alarmPermissionDesc'.tr; + case Permission.storage: + return 'storagePermissionExplanation'.tr; + case Permission.microphone: + return 'microphonePermissionExplanation'.tr; + default: + return 'appFunctionNeeds'.tr; + } + } + + /// 显示成功消息 + void _showSuccessMessage() { + Get.snackbar( + 'success'.tr, + 'permissionGranted'.tr, + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.green, + colorText: Colors.white, + ); + } + + /// 显示错误消息 + void _showErrorMessage() { + Get.snackbar( + 'error'.tr, + 'permissionRequestError'.tr, + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red, + colorText: Colors.white, + ); + } +} \ No newline at end of file diff --git a/lib/modules/permissions/models/permission_group.dart b/lib/modules/permissions/models/permission_group.dart new file mode 100644 index 000000000..f5c6fb324 --- /dev/null +++ b/lib/modules/permissions/models/permission_group.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class PermissionGroup { + final String titleKey; + final IconData icon; + final List permissions; + + const PermissionGroup({ + required this.titleKey, + required this.icon, + required this.permissions, + }); + + static List getPlatformGroups() { + return [ + // 通讯权限组 - 仅Android(包含联系人权限) + const PermissionGroup( + titleKey: 'communicationPermissions', + icon: Icons.phone_android, + permissions: [Permission.phone, Permission.sms, Permission.contacts], + ), + // 蓝牙权限组 + const PermissionGroup( + titleKey: 'bluetoothPermissions', + icon: Icons.bluetooth, + permissions: [Permission.bluetooth], + ), + // 通知权限组 + const PermissionGroup( + titleKey: 'notificationPermissions', + icon: Icons.notifications, + permissions: [Permission.notification], + ), + // 闹钟权限组 - 仅Android + const PermissionGroup( + titleKey: 'alarmPermissions', + icon: Icons.alarm, + permissions: [Permission.scheduleExactAlarm], + ), + // 日历权限组 + const PermissionGroup( + titleKey: 'calendarPermissions', + icon: Icons.calendar_today, + permissions: [Permission.calendarFullAccess], + ), + // 位置权限组 + const PermissionGroup( + titleKey: 'locationPermissions', + icon: Icons.location_on, + permissions: [Permission.location], + ), + // 存储权限组 + const PermissionGroup( + titleKey: 'storagePermissions', + icon: Icons.storage, + permissions: [Permission.storage], + ), + // 麦克风权限组 + const PermissionGroup( + titleKey: 'microphonePermissions', + icon: Icons.mic, + permissions: [Permission.microphone], + ), + ]; + } +} \ No newline at end of file diff --git a/lib/modules/permissions/views/permissions_view.dart b/lib/modules/permissions/views/permissions_view.dart new file mode 100644 index 000000000..23eb299b8 --- /dev/null +++ b/lib/modules/permissions/views/permissions_view.dart @@ -0,0 +1,276 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'dart:ui'; +import 'dart:io' show Platform; + +import '../controllers/permissions_controller.dart'; +import '../models/permission_group.dart'; + +class PermissionsView extends GetView { + const PermissionsView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return Scaffold( + backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], + appBar: AppBar( + backgroundColor: isDarkMode ? Colors.black : Colors.white, + elevation: 0.5, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 20.w, + color: isDarkMode ? Colors.white : Colors.black87, + ), + onPressed: () => Get.back(), + ), + title: Text( + 'permissionManagement'.tr, + style: TextStyle( + fontSize: 17.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + centerTitle: true, + ), + body: Obx(() { + if (controller.permissionStatuses.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + + return RefreshIndicator( + onRefresh: controller.checkAllPermissions, + child: ListView( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), + children: _buildPermissionGroups(isDarkMode), + ), + ); + }), + ); + } + + List _buildPermissionGroups(bool isDarkMode) { + final groups = PermissionGroup.getPlatformGroups(); + final List widgets = []; + + for (int i = 0; i < groups.length; i++) { + final group = groups[i]; + + // 过滤平台特定权限 + if (_shouldShowGroup(group)) { + widgets.add(_buildPermissionSection( + group.titleKey, + group.icon, + group.permissions, + controller.permissionStatuses, + isDarkMode, + )); + + if (i < groups.length - 1) { + widgets.add(SizedBox(height: 20.h)); + } + } + } + + return widgets; + } + + bool _shouldShowGroup(PermissionGroup group) { + // Android特定权限组 + if (group.titleKey == 'communicationPermissions' || + group.titleKey == 'alarmPermissions') { + return Platform.isAndroid; + } + // 蓝牙权限、通知权限、存储权限和麦克风权限在所有平台显示 + if (group.titleKey == 'bluetoothPermissions' || + group.titleKey == 'notificationPermissions' || + group.titleKey == 'storagePermissions' || + group.titleKey == 'microphonePermissions') { + return true; + } + return true; + } + + Widget _buildPermissionSection( + String titleKey, + IconData icon, + List permissions, + Map statuses, + bool isDarkMode, + ) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 组标题 + Padding( + padding: EdgeInsets.only(left: 4.w, bottom: 8.h), + child: Text( + titleKey.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: isDarkMode ? Colors.blue[400] : Colors.blue[600], + letterSpacing: 1, + ), + ), + ), + + // 权限项容器 + Container( + decoration: BoxDecoration( + color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, + borderRadius: BorderRadius.circular(16.r), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withOpacity(0.2) + : Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(16.r), + child: BackdropFilter( + filter: isDarkMode + ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) + : ImageFilter.blur(sigmaX: 0, sigmaY: 0), + child: Column( + children: _buildPermissionItems(permissions, statuses, isDarkMode), + ), + ), + ), + ), + ], + ); + } + + List _buildPermissionItems( + List permissions, + Map statuses, + bool isDarkMode, + ) { + final List items = []; + + for (int i = 0; i < permissions.length; i++) { + final permission = permissions[i]; + final status = statuses[permission] ?? PermissionStatus.denied; + + items.add( + _buildPermissionItem( + permission: permission, + status: status, + isDarkMode: isDarkMode, + ), + ); + + // 添加分隔线,最后一个不加 + if (i < permissions.length - 1) { + items.add(Divider( + height: 1, + color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200], + )); + } + } + + return items; + } + + Widget _buildPermissionItem({ + required Permission permission, + required PermissionStatus status, + required bool isDarkMode, + }) { + return Padding( + padding: EdgeInsets.all(16.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + controller.getPermissionName(permission), + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + color: isDarkMode ? Colors.white : Colors.black87, + ), + ), + SizedBox(height: 4.h), + Text( + controller.getPermissionDescription(permission), + style: TextStyle( + fontSize: 12.sp, + color: isDarkMode ? Colors.white70 : Colors.grey[600], + ), + ), + SizedBox(height: 4.h), + _buildStatusText(status, isDarkMode), + ], + ), + ), + Obx(() => Switch( + value: status.isGranted, + onChanged: controller.isRequestingPermission.value + ? null + : (value) => _handlePermissionToggle(permission, status, value), + activeColor: Colors.blue[600], + )), + ], + ), + ); + } + + Widget _buildStatusText(PermissionStatus status, bool isDarkMode) { + String text; + Color color; + + if (status.isGranted) { + text = 'authorized'.tr; + color = Colors.green; + } else if (status.isDenied) { + text = 'unauthorized'.tr; + color = Colors.red; + } else if (status.isPermanentlyDenied) { + text = 'needOpenInSystemSettings'.tr; + color = Colors.orange; + } else if (status.isLimited) { + text = 'partialAuthorization'.tr; + color = Colors.orange; + } else { + text = 'unknownStatus'.tr; + color = Colors.grey; + } + + return Text( + text, + style: TextStyle( + color: color, + fontSize: 12.sp, + fontWeight: FontWeight.w500, + ), + ); + } + + void _handlePermissionToggle( + Permission permission, + PermissionStatus status, + bool value, + ) { + if (value) { + // 请求权限 + controller.requestPermission(permission); + } else if (status.isGranted) { + // 已授权但用户想关闭,引导到系统设置 + controller.openSettings(); + } + } +} \ No newline at end of file diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 496e405e3..ffa5c1b64 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -12,7 +12,6 @@ import '../../../data/services/ble_manager.dart'; import '../../../data/services/language_manager.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'dart:async'; import '../../../data/services/version_update_service.dart'; import '../../common/widgets/version_update_dialog.dart'; @@ -129,103 +128,7 @@ class SettingsController extends GetxController { bool isCheckingUpdate = false; //状态锁,防止重复检查 DateTime? lastCheckTime; // 添加上次检查时间记录 - // 权限相关 - // 权限组,用于在UI中显示 - final List> permissionGroups = [ - { - 'name': '通讯权限', - 'icon': Icons.phone_android, - 'permissions': [ - {'type': Permission.phone, 'name': '电话', 'description': '用于拨打电话功能'}, - {'type': Permission.sms, 'name': '短信', 'description': '用于发送短信功能'}, - {'type': Permission.contacts, 'name': '通讯录', 'description': '用于访问联系人'} - ] - }, - { - 'name': '日程权限', - 'icon': Icons.calendar_today, - 'permissions': [ - { - 'type': Permission.calendarFullAccess, - 'name': '日历写入', - 'description': '用于创建日历事件和提醒' - }, - { - 'type': Permission.calendarFullAccess, - 'name': '日历完全访问', - 'description': '用于读取和修改日历事件' - } - ] - }, - { - 'name': '存储权限', - 'icon': Icons.storage, - 'permissions': [ - {'type': Permission.storage, 'name': '存储', 'description': '用于保存录音和文件'}, - {'type': Permission.photos, 'name': '照片', 'description': '用于选择和保存照片'} - ] - }, - { - 'name': '位置权限', - 'icon': Icons.location_on, - 'permissions': [ - { - 'type': Permission.location, - 'name': '位置', - 'description': '用于获取当前位置信息' - }, - { - 'type': Permission.locationAlways, - 'name': '后台位置', - 'description': '用于后台获取位置信息' - } - ] - }, - { - 'name': '媒体权限', - 'icon': Icons.mic, - 'permissions': [ - { - 'type': Permission.microphone, - 'name': '麦克风', - 'description': '用于语音识别和录音' - }, - {'type': Permission.bluetooth, 'name': '蓝牙', 'description': '用于连接蓝牙设备'} - ] - }, - { - 'name': '闹钟权限', - 'icon': Icons.alarm, - 'permissions': [ - { - 'type': Permission.scheduleExactAlarm, - 'name': '闹钟', - 'description': '用于设置精确闹钟' - } - ] - }, - ]; - - // 检查单个权限状态 - - // 打开应用设置 - void openSettings() { - openAppSettings(); - } - - // 检查所有权限状态并返回结果 - Future> checkAllPermissions() async { - Map statuses = {}; - - for (var group in permissionGroups) { - for (var perm in group['permissions']) { - Permission permission = perm['type']; - statuses[permission] = await permission.status; - } - } - return statuses; - } @override void onInit() { diff --git a/lib/modules/settings/views/permissions_view.dart b/lib/modules/settings/views/permissions_view.dart deleted file mode 100644 index 2b0ceaf80..000000000 --- a/lib/modules/settings/views/permissions_view.dart +++ /dev/null @@ -1,402 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:permission_handler/permission_handler.dart'; -import '../controllers/settings_controller.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'dart:ui'; -import 'dart:io' show Platform; - -import '../../../core/utils/permission_util.dart'; // 引入PermissionUtil - -class PermissionsView extends GetView { - const PermissionsView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final isDarkMode = Theme.of(context).brightness == Brightness.dark; - - return Scaffold( - backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], - appBar: AppBar( - backgroundColor: isDarkMode ? Colors.black : Colors.white, - elevation: 0.5, - leading: IconButton( - icon: Icon( - Icons.arrow_back_ios, - size: 20.w, - color: isDarkMode ? Colors.white : Colors.black87, - ), - onPressed: () => Get.back(), - ), - title: Text( - 'permissionManagement'.tr, // 权限管理 - style: TextStyle( - fontSize: 17.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), - ), - centerTitle: true, - ), - body: FutureBuilder>( - future: controller.checkAllPermissions(), //检查权限当前状态 - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } - - if (snapshot.hasError) { - return Center( - child: Text( - '${'getPermissionStatusFailed'.tr}: ${snapshot.error}')); // 获取权限状态失败 - } - - final permissionStatuses = snapshot.data ?? {}; - - return ListView( - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), - children: [ - // 通讯权限组- iOS平台没有通讯权限 - if (Platform.isAndroid) ...[ - _buildPermissionSection( - 'communicationPermissions'.tr, // 通讯权限 - Icons.phone_android, - [Permission.phone, Permission.sms], - permissionStatuses, - isDarkMode), - SizedBox(height: 20.h), - ], - // 闹钟权限组 - if (Platform.isAndroid) ...[ - _buildPermissionSection( - 'alarmPermissions'.tr, // 闹钟权限 - Icons.alarm, - [Permission.scheduleExactAlarm], - permissionStatuses, - isDarkMode, - ), - SizedBox(height: 20.h), - ], - - // 日历权限组 - _buildPermissionSection( - 'calendarPermissions'.tr, // 日历权限 - Icons.calendar_today, - [Permission.calendarFullAccess], - permissionStatuses, - isDarkMode), - - SizedBox(height: 20.h), - - // 位置权限组 - _buildPermissionSection( - 'locationPermissions'.tr, // 位置权限 - Icons.location_on, - [Permission.location], - permissionStatuses, - isDarkMode), - ], - ); - }, - ), - ); - } - - // 构建权限组区域 - Widget _buildPermissionSection( - String title, - IconData icon, - List permissions, - Map statuses, - bool isDarkMode) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 组标题 - Padding( - padding: EdgeInsets.only(left: 4.w, bottom: 8.h), - child: Text( - title.tr, - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.blue[400] : Colors.blue[600], - letterSpacing: 1, - ), - ), - ), - - // 权限项容器 - Container( - decoration: BoxDecoration( - color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, - borderRadius: BorderRadius.circular(16.r), - boxShadow: [ - BoxShadow( - color: isDarkMode - ? Colors.black.withOpacity(0.2) - : Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(16.r), - child: BackdropFilter( - filter: isDarkMode - ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) - : ImageFilter.blur(sigmaX: 0, sigmaY: 0), - child: Column( - children: - _buildPermissionItems(permissions, statuses, isDarkMode), - ), - ), - ), - ), - ], - ); - } - - // 构建权限项列表 - List _buildPermissionItems(List permissions, - Map statuses, bool isDarkMode) { - final List items = []; - - // 获取名称 - String getName(Permission permission) { - switch (permission) { - case Permission.phone: - return 'phone'.tr; // 电话 - case Permission.sms: - return 'sms'.tr; // 短信 - case Permission.calendarWriteOnly: - return 'calendarWrite'.tr; // 日历写入 - case Permission.calendarFullAccess: - return 'calendarFullAccess'.tr; // 日历完整访问 - case Permission.location: - return 'locationInfo'.tr; // 位置信息 - case Permission.scheduleExactAlarm: - return 'alarm'.tr; // 闹钟 - default: - return permission.toString(); - } - } - - // 获取描述文本 - String getDescription(Permission permission) { - switch (permission) { - case Permission.phone: - return 'phonePermissionDesc'.tr; // 用于拨打电话功能 - case Permission.sms: - return 'smsPermissionDesc'.tr; // 用于发送短信功能 - case Permission.calendarWriteOnly: - return 'calendarWritePermissionDesc'.tr; // 用于创建日历事件和提醒 - case Permission.calendarFullAccess: - return 'calendarFullAccessPermissionDesc'.tr; // 用于读取和管理日历事件及提醒 - case Permission.location: - return 'locationPermissionDesc'.tr; // 用于获取当前位置信息 - case Permission.scheduleExactAlarm: - return 'alarmPermissionDesc'.tr; // 设置精确闹钟需要此权限 - default: - return 'appFunctionNeeds'.tr; // 应用功能需要 - } - } - - for (int i = 0; i < permissions.length; i++) { - final permission = permissions[i]; - final status = statuses[permission] ?? PermissionStatus.denied; - - items.add( - _buildPermissionItem( - name: getName(permission), - description: getDescription(permission), - status: status, - permission: permission, - isDarkMode: isDarkMode, - ), - ); - - // 添加分隔线,最后一个不加 - if (i < permissions.length - 1) { - items.add(Divider( - height: 1, - color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200], - )); - } - } - - return items; - } - - // 构建单个权限项 - Widget _buildPermissionItem({ - required String name, - required String description, - required PermissionStatus status, - required Permission permission, - required bool isDarkMode, - }) { - return Padding( - padding: EdgeInsets.all(16.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - name.tr, - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.white : Colors.black87, - ), - ), - SizedBox(height: 4.h), - Text( - description.tr, - style: TextStyle( - fontSize: 12.sp, - color: isDarkMode ? Colors.white70 : Colors.grey[600], - ), - ), - SizedBox(height: 4.h), - _buildStatusText(status, isDarkMode), - ], - ), - ), - Switch( - value: status.isGranted, - onChanged: (value) { - if (value) { - _handlePermissionAction(permission, status); - } else if (status.isGranted) { - controller.openSettings(); - } - }, - activeColor: Colors.blue[600], - ), - ], - ), - ); - } - - // 状态文本 - Widget _buildStatusText(PermissionStatus status, bool isDarkMode) { - String text; - Color color; - - if (status.isGranted) { - text = 'authorized'.tr; // 已授权 - color = Colors.green; - } else if (status.isDenied) { - text = 'unauthorized'.tr; // 未授权 - color = Colors.red; - } else if (status.isPermanentlyDenied) { - text = 'needOpenInSystemSettings'.tr; // 需要在系统设置中开启 - color = Colors.orange; - } else if (status.isLimited) { - text = 'partialAuthorization'.tr; // 部分授权 - color = Colors.orange; - } else { - text = 'unknownStatus'.tr; // 未知状态 - color = Colors.grey; - } - - return Text( - text, - style: TextStyle( - color: color, - fontSize: 12.sp, - fontWeight: FontWeight.w500, - ), - ); - } - - // 处理权限请求 - Future _handlePermissionAction( - Permission permission, PermissionStatus status) async { - try { - // final context = Get.context; - // if (context == null) return; - - final permissionName = _getPermissionName(permission); - final explanationText = getDetailedDescription(permission); - - // 使用 PermissionUtil 请求权限 - final hasPermission = await PermissionUtil.instance.requestPermission( - // context: context, - permissionType: permission, - permissionName: permissionName, - explanationText: explanationText, - permanentDenialText: 'permanentDenialText'.tr.replaceAll( - '{permission}', permissionName), // 您已永久拒绝{permission}权限,请前往应用设置开启 - settingsButtonText: 'goToSettings'.tr, // 去设置 - cancelButtonText: 'cancel'.tr, // 取消 - ); - - if (hasPermission) { - Get.snackbar( - 'success'.tr, // 成功 - 'permissionGranted'.tr, // 权限已授予 - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.green, - colorText: Colors.white, - ); - } - - // 刷新界面,重新获取权限状态 - Get.forceAppUpdate(); - } catch (e) { - Get.snackbar( - 'error'.tr, // 错误 - 'permissionRequestError'.tr, // 请求权限时出错 - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.red, - colorText: Colors.white, - ); - } - } - - // 获取权限的用户友好名称 - String _getPermissionName(Permission permission) { - switch (permission) { - case Permission.phone: - return 'phone'.tr; // 电话 - case Permission.sms: - return 'sms'.tr; // 短信 - case Permission.calendarWriteOnly: - return 'calendarWrite'.tr; // 日历写入 - case Permission.calendarFullAccess: - return 'calendarFullAccess'.tr; // 日历完整访问 - case Permission.location: - return 'locationInfo'.tr; // 位置信息 - case Permission.scheduleExactAlarm: - return 'alarm'.tr; // 闹钟 - default: - return permission.toString(); - } - } - - // 获取权限说明文本 - String getDetailedDescription(Permission permission) { - switch (permission) { - case Permission.phone: - return 'phonePermissionDetailDesc'.tr; // 拨打电话功能需要电话权限 - case Permission.sms: - return 'smsPermissionDetailDesc'.tr; // 发送短信功能需要短信权限 - case Permission.calendarWriteOnly: - return 'calendarWritePermissionDetailDesc'.tr; // 创建日历事件需要日历写入权限 - case Permission.calendarFullAccess: - return 'calendarFullAccessPermissionDetailDesc'.tr; // 管理日历事件需要完整日历权限 - case Permission.location: - return 'locationPermissionDetailDesc'.tr; // 获取位置信息需要位置权限 - case Permission.scheduleExactAlarm: - return 'alarmPermissionDetailDesc'.tr; // 设置精确闹钟需要此权限 - default: - return 'appNeedsPermissionForFullFunction'.tr; // 应用需要此权限以提供完整功能 - } - } -} diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index b6df617fb..34f1fcc40 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -50,8 +50,9 @@ import '../modules/pairing/views/pairing_view.dart'; import '../modules/pairing/bindings/pairing_binding.dart'; import './app_routes.dart'; import '../modules/settings/views/settings_view.dart'; -import '../modules/settings/views/permissions_view.dart'; import '../modules/settings/bindings/settings_binding.dart'; +import '../modules/permissions/views/permissions_view.dart'; +import '../modules/permissions/bindings/permissions_binding.dart'; import '../modules/speech_test/views/speech_test_view.dart'; import '../modules/speech_test/bindings/speech_test_binding.dart'; import '../modules/agent/views/agent_view.dart'; @@ -254,7 +255,7 @@ abstract class AppPages { GetPage( name: Routes.permissions, page: () => const PermissionsView(), - binding: SettingsBinding(), + binding: PermissionsBinding(), transition: Transition.rightToLeft, transitionDuration: Duration(milliseconds: 250), ), diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 74bd2623b..fae16f5da 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -18,6 +19,8 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); FirebaseAuthPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index e548eecfe..44fbd12be 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus file_selector_windows firebase_auth firebase_core