Browse Source

feat(permission): (针对部分华为手机)添加权限拒绝次数统计和熔断机制

添加智能拒绝次数统计功能,当拒绝次数达到阈值时直接引导用户去设置
引入 GetStorage 持久化存储拒绝次数
新增重置拒绝次数和获取当前拒绝次数的方法
优化权限请求流程,在授权时自动清零拒绝记录
newdev_shunjiawei
lxm 1 year ago
parent
commit
6afbd5d622
  1. 120
      lib/core/utils/permission_util.dart

120
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<void> resetDenialCount(Permission permissionType) async {
await _storage.remove(_getDenialCountKey(permissionType));
Logger.d('PermissionUtil', '已重置 ${permissionType.toString()} 的拒绝次数');
}
/// 获取指定权限的拒绝次数
int getDenialCount(Permission permissionType) {
return _storage.read<int>(_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<int>(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;
}
}
// 其他未知状态处理

Loading…
Cancel
Save