Browse Source

feat(权限管理): 重构权限管理模块并添加新权限支持

将权限管理功能从设置模块分离为独立模块,新增蓝牙、通知、存储和麦克风权限支持。优化权限分组显示逻辑,根据平台显示不同权限组,并完善权限状态管理和多语言支持。

- 新增权限分组模型和独立控制器
- 添加蓝牙、通知等新权限支持
- 优化权限请求流程和状态显示
- 完善中英文多语言翻译
- 移除旧版权限视图并更新路由配置
newdev_shunjiawei
lxm 1 year ago
parent
commit
b56d4698b9
  1. 14
      lib/core/translations/language/en_us.dart
  2. 11
      lib/core/translations/language/zh_cn.dart
  3. 2
      lib/data/services/music_manager.dart
  4. 2
      lib/data/services/novice_guide_service.dart
  5. 28
      lib/modules/agent/controllers/agent_controller.dart
  6. 9
      lib/modules/permissions/bindings/permissions_binding.dart
  7. 222
      lib/modules/permissions/controllers/permissions_controller.dart
  8. 67
      lib/modules/permissions/models/permission_group.dart
  9. 276
      lib/modules/permissions/views/permissions_view.dart
  10. 97
      lib/modules/settings/controllers/settings_controller.dart
  11. 402
      lib/modules/settings/views/permissions_view.dart
  12. 5
      lib/routes/app_pages.dart
  13. 3
      windows/flutter/generated_plugin_registrant.cc
  14. 1
      windows/flutter/generated_plugins.cmake

14
lib/core/translations/language/en_us.dart

@ -487,11 +487,18 @@ const Map<String, String> 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<String, String> 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':

11
lib/core/translations/language/zh_cn.dart

@ -455,11 +455,18 @@ const Map<String, String> zhCN = {
// 权限管理页面
'getPermissionStatusFailed': '获取权限状态失败',
'communicationPermissions': '通讯权限',
'bluetoothPermissions': '蓝牙权限',
'notificationPermissions': '通知权限',
'storagePermissions': '存储权限',
'microphonePermissions': '麦克风权限',
'alarmPermissions': '闹钟权限',
'calendarPermissions': '日历权限',
'locationPermissions': '位置权限',
'phone': '电话',
'sms': '短信',
'contacts': '联系人',
//'bluetooth': '蓝牙',
'notification': '通知',
'alarm': '闹钟',
'calendarWrite': '日历写入',
'calendarFullAccess': '日历完整访问',
@ -486,6 +493,10 @@ const Map<String, String> zhCN = {
'locationPermissionDetailDesc': '获取位置信息需要位置权限',
'contactsPermissionDesc': '用于访问联系人信息',
'contactsPermissionDetailDesc': '拨打电话和发送短信功能需要联系人权限以选择联系人',
'bluetoothPermissionDesc': '用于连接和管理蓝牙设备',
'bluetoothPermissionDetailDesc': '连接蓝牙耳机等设备需要蓝牙权限',
'notificationPermissionDesc': '用于发送通知消息',
'notificationPermissionDetailDesc': '应用需要通知权限以向您发送重要提醒和消息',
'appNeedsPermissionForFullFunction': '应用需要此权限以提供完整功能',
'permanentDenialText': '您已永久拒绝{permission}权限,请前往应用设置开启',
'goToSettings': '去设置',

2
lib/data/services/music_manager.dart

@ -22,7 +22,7 @@ class MusicManager extends GetxService {
// Logger.error('无法获取 BuildContext');
// return;
// }
requestNotificationPermission();
//requestNotificationPermission();
Logger.info('初始化权限请求');
}

2
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');

28
lib/modules/agent/controllers/agent_controller.dart

@ -1060,20 +1060,20 @@ class AgentController extends GetxController {
Future<void> 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();

9
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>(() => PermissionsController());
}
}

222
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<Permission, PermissionStatus> permissionStatuses =
<Permission, PermissionStatus>{}.obs;
// 是否正在请求权限
final RxBool isRequestingPermission = false.obs;
@override
void onInit() {
super.onInit();
checkAllPermissions();
}
/// 检查所有权限状态
Future<Map<Permission, PermissionStatus>> checkAllPermissions() async {
final Map<Permission, PermissionStatus> 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<Permission> _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<bool> 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,
);
}
}

67
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<Permission> permissions;
const PermissionGroup({
required this.titleKey,
required this.icon,
required this.permissions,
});
static List<PermissionGroup> 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],
),
];
}
}

276
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<PermissionsController> {
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<Widget> _buildPermissionGroups(bool isDarkMode) {
final groups = PermissionGroup.getPlatformGroups();
final List<Widget> 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<Permission> permissions,
Map<Permission, PermissionStatus> 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<Widget> _buildPermissionItems(
List<Permission> permissions,
Map<Permission, PermissionStatus> statuses,
bool isDarkMode,
) {
final List<Widget> 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();
}
}
}

97
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<Map<String, dynamic>> 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<Map<Permission, PermissionStatus>> checkAllPermissions() async {
Map<Permission, PermissionStatus> 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() {

402
lib/modules/settings/views/permissions_view.dart

@ -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<SettingsController> {
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<Map<Permission, PermissionStatus>>(
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<Permission> permissions,
Map<Permission, PermissionStatus> 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<Widget> _buildPermissionItems(List<Permission> permissions,
Map<Permission, PermissionStatus> statuses, bool isDarkMode) {
final List<Widget> 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<void> _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; // 应用需要此权限以提供完整功能
}
}
}

5
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),
),

3
windows/flutter/generated_plugin_registrant.cc

@ -6,6 +6,7 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_auth/firebase_auth_plugin_c_api.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
@ -18,6 +19,8 @@
#include <window_size/window_size_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FirebaseAuthPluginCApiRegisterWithRegistrar(

1
windows/flutter/generated_plugins.cmake

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
file_selector_windows
firebase_auth
firebase_core

Loading…
Cancel
Save