diff --git a/lib/core/utils/permission_util.dart b/lib/core/utils/permission_util.dart index 1f19ff396..abe4cb80d 100644 --- a/lib/core/utils/permission_util.dart +++ b/lib/core/utils/permission_util.dart @@ -3,6 +3,7 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:app_settings/app_settings.dart'; import 'logger.dart'; import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; /// 权限请求工具类 /// @@ -11,6 +12,7 @@ import 'package:get/get.dart'; /// 2. 权限解释横幅显示(悬浮在UI顶部的解释性UI) /// 3. 权限请求执行 /// 4. 永久拒绝后的设置引导 +/// 5. 智能拒绝次数统计和熔断机制 /// /// 使用单例模式:`PermissionUtil.instance` @@ -24,6 +26,30 @@ class PermissionUtil { /// 权限解释横幅的叠加层入口 //OverlayEntry? _explanationOverlayEntry; + /// 引入 get_storage 用于持久化权限拒绝次数 + final _storage = GetStorage(); + + /// 权限拒绝次数阈值(达到此次数后视为永久拒绝) + static const int _denialThreshold = 2; + + /// 获取权限拒绝次数的存储键 + String _getDenialCountKey(Permission permission) { + return 'permission_denial_count_${permission.toString()}'; + } + + /// 重置指定权限的拒绝次数 + /// + /// 在某些场景下可能需要手动重置拒绝次数,比如用户主动要求重新请求权限 + Future resetDenialCount(Permission permissionType) async { + await _storage.remove(_getDenialCountKey(permissionType)); + Logger.d('PermissionUtil', '已重置 ${permissionType.toString()} 的拒绝次数'); + } + + /// 获取指定权限的拒绝次数 + int getDenialCount(Permission permissionType) { + return _storage.read(_getDenialCountKey(permissionType)) ?? 0; + } + /// 请求指定权限 /// /// 参数: @@ -59,34 +85,75 @@ class PermissionUtil { if (status == PermissionStatus.granted || status == PermissionStatus.provisional) { Logger.i('PermissionUtil', '$permissionName 权限已授予或临时授权'); + // 如果已经授权,清零之前的拒绝记录,以防用户在设置中开启后又关闭 + await _storage.remove(_getDenialCountKey(permissionType)); return true; } - // 显示权限解释横幅(覆盖所有状态的处理) - _showExplanationOverlay( - //context: context, - permissionName: permissionName, - explanationText: explanationText, - ); + // 2. 读取历史拒绝次数 + final denialCountKey = _getDenialCountKey(permissionType); + int denialCount = _storage.read(denialCountKey) ?? 0; + Logger.d('PermissionUtil', '$permissionName 当前拒绝次数: $denialCount'); - // 处理永久拒绝状态(先显示说明横幅,再引导用户去设置) - if (status == PermissionStatus.permanentlyDenied) { - Logger.w('PermissionUtil', '$permissionName 权限被永久拒绝,显示引导对话框'); + // 3. 熔断机制:如果拒绝次数达到阈值或系统返回了 permanentlyDenied,直接视为永久拒绝 + if (denialCount >= _denialThreshold || + status == PermissionStatus.permanentlyDenied) { + Logger.w('PermissionUtil', + '$permissionName 已被拒绝 $denialCount 次或被系统永久拒绝,触发设置引导'); - // 显示永久拒绝对话框(在解释横幅下方显示) + // 显示权限解释横幅 + _showExplanationOverlay( + permissionName: permissionName, + explanationText: explanationText, + ); + + // 显示引导去设置的对话框 final result = await _showPermanentDenialDialog( - // context: context, permissionName: permissionName, permanentDenialText: permanentDenialText, settingsButtonText: finalSettingsButtonText, cancelButtonText: finalCancelButtonText, ); + // 移除解释横幅 _hideExplanationOverlay(); return result; } - // 处理初始拒绝/未决定状态 + // 显示权限解释横幅(覆盖所有状态的处理) + _showExplanationOverlay( + //context: context, + permissionName: permissionName, + explanationText: explanationText, + ); + + // 处理永久拒绝状态(先显示说明横幅,再引导用户去设置) + // 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; + } + + // 处理初始拒绝/未决定状态(真正的权限请求) if (status == PermissionStatus.denied) { Logger.d('PermissionUtil', '$permissionName 权限被拒绝或未决定,显示说明横幅并请求'); // _showExplanationOverlay( @@ -94,19 +161,22 @@ class PermissionUtil { // permissionName: permissionName, // explanationText: explanationText, // ); -//请求权限 + //请求权限 final PermissionStatus newStatus = await permissionType.request(); Logger.d('PermissionUtil', '请求后 $permissionName 的新权限状态: $newStatus'); //_hideExplanationOverlay(); - // 处理请求后的状态 + // 5. 根据结果更新状态和拒绝次数 if (newStatus == PermissionStatus.granted || newStatus == PermissionStatus.provisional) { Logger.i('PermissionUtil', '$permissionName 权限在请求后被授予'); + // 授权成功,清零拒绝次数 + await _storage.remove(denialCountKey); _hideExplanationOverlay(); return true; } + // 处理请求后变为永久拒绝 else if (newStatus == PermissionStatus.permanentlyDenied) { Logger.w('PermissionUtil', '$permissionName 权限在请求后被永久拒绝'); @@ -120,18 +190,16 @@ class PermissionUtil { _hideExplanationOverlay(); return result; } - // 其他拒绝状态 - _hideExplanationOverlay(); - return false; - } - - // 处理受限状态 - if (status == PermissionStatus.restricted || - status == PermissionStatus.limited) { - Logger.w('PermissionUtil', '$permissionName 权限受限或受限访问: $status'); - await _showRestrictedPermissionDialog(permissionName); - _hideExplanationOverlay(); - return false; + // 如果执行到这里,说明权限被拒绝了 (denied) + else { + Logger.w('PermissionUtil', '$permissionName 权限被拒绝'); + // 拒绝次数 +1 + await _storage.write(denialCountKey, denialCount + 1); + Logger.d( + 'PermissionUtil', '$permissionName 拒绝次数更新为: ${denialCount + 1}'); + _hideExplanationOverlay(); + return false; + } } // 其他未知状态处理