From 8532c4efad9e1bb5cb73ef384a6e85b28fe13bf5 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Sat, 30 Aug 2025 00:07:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor(permission):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=AF=B7=E6=B1=82=E9=80=BB=E8=BE=91=E5=B9=B6?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构权限请求流程,简化状态处理逻辑 - 移除未使用的注释代码和冗余方法 - 改进权限横幅显示和隐藏的可靠性 - 使用更清晰的权限状态检查方法 --- lib/core/utils/permission_util.dart | 263 ++++++---------------------- 1 file changed, 53 insertions(+), 210 deletions(-) diff --git a/lib/core/utils/permission_util.dart b/lib/core/utils/permission_util.dart index abe4cb80d..647dc3989 100644 --- a/lib/core/utils/permission_util.dart +++ b/lib/core/utils/permission_util.dart @@ -23,9 +23,6 @@ class PermissionUtil { /// 私有构造函数 PermissionUtil._(); - /// 权限解释横幅的叠加层入口 - //OverlayEntry? _explanationOverlayEntry; - /// 引入 get_storage 用于持久化权限拒绝次数 final _storage = GetStorage(); @@ -63,7 +60,6 @@ class PermissionUtil { /// /// 返回值:`Future` 权限是否最终授予 Future requestPermission({ - // required BuildContext context, required Permission permissionType, required String permissionName, required String explanationText, @@ -75,145 +71,82 @@ class PermissionUtil { final String finalSettingsButtonText = settingsButtonText ?? 'goToSettings'.tr; final String finalCancelButtonText = cancelButtonText ?? 'cancel'.tr; + bool isOverlayShown = false; try { // 检查初始权限状态 - final PermissionStatus status = await permissionType.status; + PermissionStatus status = await permissionType.status; Logger.d('PermissionUtil', '初始权限状态 [$permissionName]: $status'); - // 处理已授权状态(包括授予状态和临时通知权限) - if (status == PermissionStatus.granted || - status == PermissionStatus.provisional) { - Logger.i('PermissionUtil', '$permissionName 权限已授予或临时授权'); + // 1. <--- 已授权处理 --->(包括授予状态和临时通知权限) + if (status.isGranted) { + Logger.i('PermissionUtil', '$permissionName 权限已授予或临时授予'); // 如果已经授权,清零之前的拒绝记录,以防用户在设置中开启后又关闭 - await _storage.remove(_getDenialCountKey(permissionType)); + await resetDenialCount(permissionType); // 清零拒绝记录 return true; } - // 2. 读取历史拒绝次数 - final denialCountKey = _getDenialCountKey(permissionType); - int denialCount = _storage.read(denialCountKey) ?? 0; + // 2. <--- 熔断机制:检查我们自己的拒绝次数 ---> + final denialCount = getDenialCount(permissionType); Logger.d('PermissionUtil', '$permissionName 当前拒绝次数: $denialCount'); - - // 3. 熔断机制:如果拒绝次数达到阈值或系统返回了 permanentlyDenied,直接视为永久拒绝 - if (denialCount >= _denialThreshold || - status == PermissionStatus.permanentlyDenied) { - Logger.w('PermissionUtil', - '$permissionName 已被拒绝 $denialCount 次或被系统永久拒绝,触发设置引导'); - - // 显示权限解释横幅 - _showExplanationOverlay( - permissionName: permissionName, - explanationText: explanationText, - ); - - // 显示引导去设置的对话框 - final result = await _showPermanentDenialDialog( + // 如果拒绝次数达到阈值或系统返回了 permanentlyDenied,直接视为永久拒绝 + if (status.isPermanentlyDenied || denialCount >= _denialThreshold) { + Logger.w('PermissionUtil', '$permissionName 已被系统永久拒绝或自定义计数达到阈值($denialCount),触发设置引导'); + await _showPermanentDenialDialog( permissionName: permissionName, permanentDenialText: permanentDenialText, settingsButtonText: finalSettingsButtonText, cancelButtonText: finalCancelButtonText, ); - - // 移除解释横幅 - _hideExplanationOverlay(); - return result; + // 引导用户后,返回false,因为权限当前未被授予 + return false; } - // 显示权限解释横幅(覆盖所有状态的处理) + // 3. <--- 发起正常的权限请求 (此时 status 必然是 denied 或受限) ---> _showExplanationOverlay( - //context: context, permissionName: permissionName, explanationText: explanationText, ); + isOverlayShown = true; - // 处理永久拒绝状态(先显示说明横幅,再引导用户去设置) - // if (status == PermissionStatus.permanentlyDenied) { - // Logger.w('PermissionUtil', '$permissionName 权限被永久拒绝,显示引导对话框'); - // // 显示永久拒绝对话框(在解释横幅下方显示) - // final result = await _showPermanentDenialDialog( - // // context: context, - // permissionName: permissionName, - // permanentDenialText: permanentDenialText, - // settingsButtonText: finalSettingsButtonText, - // cancelButtonText: finalCancelButtonText, - // ); - // // 移除解释横幅 - // _hideExplanationOverlay(); - // return result; - // } - // 处理受限状态 - if (status == PermissionStatus.restricted || - status == PermissionStatus.limited) { - Logger.w('PermissionUtil', '$permissionName 权限受限或受限访问: $status'); - await _showRestrictedPermissionDialog(permissionName); - _hideExplanationOverlay(); - return false; - } + final PermissionStatus newStatus = await permissionType.request(); + Logger.d('PermissionUtil', '请求后 $permissionName 的新权限状态: $newStatus'); - // 处理初始拒绝/未决定状态(真正的权限请求) - if (status == PermissionStatus.denied) { - Logger.d('PermissionUtil', '$permissionName 权限被拒绝或未决定,显示说明横幅并请求'); - // _showExplanationOverlay( - // context: context, - // permissionName: permissionName, - // explanationText: explanationText, - // ); - //请求权限 - final PermissionStatus newStatus = await permissionType.request(); - Logger.d('PermissionUtil', '请求后 $permissionName 的新权限状态: $newStatus'); + _hideExplanationOverlay(); + isOverlayShown = false; - //_hideExplanationOverlay(); + // 4. <--- 处理请求后的结果 ---> + if (newStatus.isGranted) { + Logger.i('PermissionUtil', '$permissionName 权限在请求后被授予'); + await resetDenialCount(permissionType); // 授权成功,清零 + return true; + } - // 5. 根据结果更新状态和拒绝次数 - if (newStatus == PermissionStatus.granted || - newStatus == PermissionStatus.provisional) { - Logger.i('PermissionUtil', '$permissionName 权限在请求后被授予'); - // 授权成功,清零拒绝次数 - await _storage.remove(denialCountKey); - _hideExplanationOverlay(); - return true; - } + if (newStatus.isPermanentlyDenied) { + Logger.w('PermissionUtil', '$permissionName 权限在请求后被系统永久拒绝'); + await _showPermanentDenialDialog( + permissionName: permissionName, + permanentDenialText: permanentDenialText, + settingsButtonText: finalSettingsButtonText, + cancelButtonText: finalCancelButtonText, + ); + return false; + } - // 处理请求后变为永久拒绝 - else if (newStatus == PermissionStatus.permanentlyDenied) { - Logger.w('PermissionUtil', '$permissionName 权限在请求后被永久拒绝'); - final result = await _showPermanentDenialDialog( - // context: context, - permissionName: permissionName, - permanentDenialText: permanentDenialText, - settingsButtonText: finalSettingsButtonText, - cancelButtonText: finalCancelButtonText, - ); - _hideExplanationOverlay(); - return result; - } - // 如果执行到这里,说明权限被拒绝了 (denied) - else { - Logger.w('PermissionUtil', '$permissionName 权限被拒绝'); - // 拒绝次数 +1 - await _storage.write(denialCountKey, denialCount + 1); - Logger.d( - 'PermissionUtil', '$permissionName 拒绝次数更新为: ${denialCount + 1}'); - _hideExplanationOverlay(); - return false; - } + if (newStatus.isDenied) { + Logger.w('PermissionUtil', '$permissionName 权限被拒绝,增加拒绝计数'); + await _storage.write(_getDenialCountKey(permissionType), denialCount + 1); } - // 其他未知状态处理 - Logger.e('PermissionUtil', '$permissionName 处于未知状态: $status'); - _hideExplanationOverlay(); + // 所有未授权的情况最终都返回 false return false; } catch (e, stackTrace) { - Logger.e( - 'PermissionUtil', - '请求 $permissionName 权限失败: $e', - e, - stackTrace, - ); - _hideExplanationOverlay(); + Logger.e('PermissionUtil', '请求 $permissionName 权限失败: $e', e, stackTrace); + if (isOverlayShown) { + _hideExplanationOverlay(); + } return false; } } @@ -236,7 +169,7 @@ class PermissionUtil { borderRadius: BorderRadius.circular(12), elevation: 8, color: Colors.grey[300], - shadowColor: Colors.black.withOpacity(0.3), + shadowColor: Colors.black.withValues(alpha: 0.3), child: Padding( padding: const EdgeInsets.all(16), child: Column( @@ -264,64 +197,23 @@ class PermissionUtil { ), ], ), + // 防止用户通过点击背景关闭解释横幅 + barrierDismissible: false, ); - // _hideExplanationOverlay(); // 确保移除旧叠加层 - - // _explanationOverlayEntry = OverlayEntry( - // builder: (context) => Positioned( - // top: MediaQuery.of(context).padding.top + 10, - // left: 16, - // right: 16, - // child: Material( - // borderRadius: BorderRadius.circular(12), - // elevation: 8, - // color: Theme.of(context).colorScheme.background, - // shadowColor: Colors.black.withOpacity(0.3), - // child: Padding( - // padding: const EdgeInsets.all(16), - // child: Column( - // mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text( - // '需要$permissionName权限', - // style: const TextStyle( - // fontWeight: FontWeight.bold, - // fontSize: 16, - // ), - // ), - // const SizedBox(height: 8), - // Text( - // explanationText, - // style: const TextStyle(fontSize: 14), - // ), - // ], - // ), - // ), - // ), - // ), - // ); - - // Future.delayed(Duration.zero, () { - // if (_explanationOverlayEntry != null) { - // Overlay.of(context).insert(_explanationOverlayEntry!); - // Logger.i('PermissionUtil', '权限说明叠加层已显示 ($permissionName)'); - // } - // }); } /// 隐藏权限解释横幅 void _hideExplanationOverlay() { - Get.back(); - //_explanationOverlayEntry!.remove(); - Logger.d('PermissionUtil', '权限说明叠加层已移除'); + if (Get.isDialogOpen ?? false) { + Get.back(); + Logger.d('PermissionUtil', '权限说明叠加层已移除'); + } } /// 显示永久拒绝后的设置引导对话框 /// /// 在权限被永久拒绝时调用,提示用户前往设置手动开启权限 - Future _showPermanentDenialDialog({ - //required BuildContext context, + Future _showPermanentDenialDialog({ required String permissionName, required String permanentDenialText, required String settingsButtonText, @@ -355,59 +247,10 @@ class PermissionUtil { barrierDismissible: false, ); - // bool? userChoice = await showDialog( - // context: context, - // barrierDismissible: false, - // builder: (context) => - // ); - - // 用户选择前往设置 if (userChoice == true) { AppSettings.openAppSettings(); - return false; // 不能立即获取权限,需要用户手动设置 } - - // 用户取消 - return false; } - /// 显示受限权限对话框 - /// - /// 在权限受限状态(如iOS上的受限照片访问)时调用 - Future _showRestrictedPermissionDialog(String permissionName) async { - Logger.w('PermissionUtil', '显示受限权限对话框 ($permissionName)'); - Get.dialog( - AlertDialog( - title: Text(permissionName + 'permissionUtilRestricted'.tr), - content: Text('permissionUtilRestrictedMessage'.tr), - actions: [ - TextButton( - onPressed: () { - Logger.d('PermissionUtil', '用户确认受限权限 ($permissionName)'); - // Navigator.pop(context); - Get.back(); - }, - child: Text('permissionUtilConfirm'.tr), - ), - ], - ), - ); - - // await showDialog( - // context: context, - // builder: (context) => AlertDialog( - // title: Text('$permissionName权限受限'), - // content: const Text('此功能在当前设备上不可用或受限,请检查设备设置或系统限制'), - // actions: [ - // TextButton( - // onPressed: () { - // Logger.d('PermissionUtil', '用户确认受限权限 ($permissionName)'); - // Navigator.pop(context); - // }, - // child: const Text('确定'), - // ), - // ], - // ), - // ); - } + // 移除了多余的_showRestrictedPermissionDialog,因为永久拒绝流程已能覆盖 }