|
|
@ -1,15 +1,8 @@ |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:permission_handler/permission_handler.dart'; |
|
|
import 'package:permission_handler/permission_handler.dart'; |
|
|
import 'package:app_settings/app_settings.dart'; |
|
|
import 'package:app_settings/app_settings.dart'; |
|
|
import 'dart:developer'; |
|
|
|
|
|
import 'logger.dart'; |
|
|
import 'logger.dart'; |
|
|
|
|
|
|
|
|
// class Logger { |
|
|
|
|
|
// static void e(String tag, String message) { |
|
|
|
|
|
// debugPrint('[$tag] ERROR: $message'); |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
class PermissionUtil { |
|
|
class PermissionUtil { |
|
|
static final PermissionUtil instance = PermissionUtil._(); |
|
|
static final PermissionUtil instance = PermissionUtil._(); |
|
|
PermissionUtil._(); |
|
|
PermissionUtil._(); |
|
|
@ -27,18 +20,18 @@ class PermissionUtil { |
|
|
}) async { |
|
|
}) async { |
|
|
try { |
|
|
try { |
|
|
final PermissionStatus status = await permissionType.status; |
|
|
final PermissionStatus status = await permissionType.status; |
|
|
log('初始权限状态 for $permissionName: $status'); |
|
|
Logger.d('PermissionUtil', '初始权限状态 [$permissionName]: $status'); |
|
|
|
|
|
|
|
|
// 处理已授权状态(包括授予状态和临时通知权限) |
|
|
// 处理已授权状态(包括授予状态和临时通知权限) |
|
|
if (status == PermissionStatus.granted || |
|
|
if (status == PermissionStatus.granted || |
|
|
status == PermissionStatus.provisional) { |
|
|
status == PermissionStatus.provisional) { |
|
|
log('$permissionName 权限已授予或临时授权。'); |
|
|
Logger.i('PermissionUtil', '$permissionName 权限已授予或临时授权'); |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 处理永久拒绝状态 |
|
|
// 处理永久拒绝状态 |
|
|
if (status == PermissionStatus.permanentlyDenied) { |
|
|
if (status == PermissionStatus.permanentlyDenied) { |
|
|
log('$permissionName 权限被永久拒绝,显示引导对话框。'); |
|
|
Logger.w('PermissionUtil', '$permissionName 权限被永久拒绝,显示引导对话框'); |
|
|
await _showPermanentDenialDialog( |
|
|
await _showPermanentDenialDialog( |
|
|
context: context, |
|
|
context: context, |
|
|
permissionName: permissionName, |
|
|
permissionName: permissionName, |
|
|
@ -51,7 +44,7 @@ class PermissionUtil { |
|
|
|
|
|
|
|
|
// 处理初始拒绝/未决定状态 |
|
|
// 处理初始拒绝/未决定状态 |
|
|
if (status == PermissionStatus.denied) { |
|
|
if (status == PermissionStatus.denied) { |
|
|
log('$permissionName 权限被拒绝或未决定,显示说明横幅并请求。'); |
|
|
Logger.d('PermissionUtil', '$permissionName 权限被拒绝或未决定,显示说明横幅并请求'); |
|
|
_showExplanationOverlay( |
|
|
_showExplanationOverlay( |
|
|
context: context, |
|
|
context: context, |
|
|
permissionName: permissionName, |
|
|
permissionName: permissionName, |
|
|
@ -59,17 +52,17 @@ class PermissionUtil { |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
final PermissionStatus newStatus = await permissionType.request(); |
|
|
final PermissionStatus newStatus = await permissionType.request(); |
|
|
log('请求后 $permissionName 的新权限状态: $newStatus'); |
|
|
Logger.d('PermissionUtil', '请求后 $permissionName 的新权限状态: $newStatus'); |
|
|
|
|
|
|
|
|
_hideExplanationOverlay(); |
|
|
_hideExplanationOverlay(); |
|
|
|
|
|
|
|
|
// 处理请求后的状态 |
|
|
// 处理请求后的状态 |
|
|
if (newStatus == PermissionStatus.granted || |
|
|
if (newStatus == PermissionStatus.granted || |
|
|
newStatus == PermissionStatus.provisional) { |
|
|
newStatus == PermissionStatus.provisional) { |
|
|
log('$permissionName 权限在请求后被授予或临时授权。'); |
|
|
Logger.i('PermissionUtil', '$permissionName 权限在请求后被授予'); |
|
|
return true; |
|
|
return true; |
|
|
} else if (newStatus == PermissionStatus.permanentlyDenied) { |
|
|
} else if (newStatus == PermissionStatus.permanentlyDenied) { |
|
|
log('$permissionName 权限在请求后被永久拒绝,再次显示引导对话框。'); |
|
|
Logger.w('PermissionUtil', '$permissionName 权限在请求后被永久拒绝'); |
|
|
await _showPermanentDenialDialog( |
|
|
await _showPermanentDenialDialog( |
|
|
context: context, |
|
|
context: context, |
|
|
permissionName: permissionName, |
|
|
permissionName: permissionName, |
|
|
@ -84,21 +77,29 @@ class PermissionUtil { |
|
|
// 处理受限状态 |
|
|
// 处理受限状态 |
|
|
if (status == PermissionStatus.restricted || |
|
|
if (status == PermissionStatus.restricted || |
|
|
status == PermissionStatus.limited) { |
|
|
status == PermissionStatus.limited) { |
|
|
log('$permissionName 权限受限或受限访问: $status'); |
|
|
Logger.w('PermissionUtil', '$permissionName 权限受限或受限访问: $status'); |
|
|
await _showRestrictedPermissionDialog(context, permissionName); |
|
|
await _showRestrictedPermissionDialog(context, permissionName); |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 其他未知状态处理 |
|
|
// 其他未知状态处理 |
|
|
log('$permissionName 处于未知状态: $status'); |
|
|
Logger.e('PermissionUtil', '$permissionName 处于未知状态: $status'); |
|
|
return false; |
|
|
return false; |
|
|
} catch (e) { |
|
|
} catch (e, stackTrace) { |
|
|
Logger.e('PermissionUtil', '请求 $permissionName 权限失败: $e'); |
|
|
Logger.e( |
|
|
|
|
|
'PermissionUtil', |
|
|
|
|
|
'请求 $permissionName 权限失败: $e', |
|
|
|
|
|
e, |
|
|
|
|
|
stackTrace, |
|
|
|
|
|
); |
|
|
_hideExplanationOverlay(); |
|
|
_hideExplanationOverlay(); |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 显示权限解释横幅(漂浮在UI顶部的说明性UI) |
|
|
|
|
|
/// |
|
|
|
|
|
/// 在请求权限前显示,向用户解释为什么需要这个权限 |
|
|
void _showExplanationOverlay({ |
|
|
void _showExplanationOverlay({ |
|
|
required BuildContext context, |
|
|
required BuildContext context, |
|
|
required String permissionName, |
|
|
required String permissionName, |
|
|
@ -114,7 +115,7 @@ class PermissionUtil { |
|
|
child: Material( |
|
|
child: Material( |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
elevation: 4, |
|
|
elevation: 4, |
|
|
color: Colors.white, |
|
|
color: Theme.of(context).colorScheme.background, |
|
|
child: Padding( |
|
|
child: Padding( |
|
|
padding: const EdgeInsets.all(16), |
|
|
padding: const EdgeInsets.all(16), |
|
|
child: Column( |
|
|
child: Column( |
|
|
@ -143,16 +144,23 @@ class PermissionUtil { |
|
|
Future.delayed(Duration.zero, () { |
|
|
Future.delayed(Duration.zero, () { |
|
|
if (_explanationOverlayEntry != null) { |
|
|
if (_explanationOverlayEntry != null) { |
|
|
Overlay.of(context).insert(_explanationOverlayEntry!); |
|
|
Overlay.of(context).insert(_explanationOverlayEntry!); |
|
|
log('权限说明叠加层已显示。'); |
|
|
Logger.i('PermissionUtil', '权限说明叠加层已显示 ($permissionName)'); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 隐藏权限解释横幅 |
|
|
void _hideExplanationOverlay() { |
|
|
void _hideExplanationOverlay() { |
|
|
_explanationOverlayEntry?.remove(); |
|
|
if (_explanationOverlayEntry != null) { |
|
|
_explanationOverlayEntry = null; |
|
|
_explanationOverlayEntry?.remove(); |
|
|
|
|
|
_explanationOverlayEntry = null; |
|
|
|
|
|
Logger.d('PermissionUtil', '权限说明叠加层已移除'); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 显示永久拒绝后的设置引导对话框 |
|
|
|
|
|
/// |
|
|
|
|
|
/// 在权限被永久拒绝时调用,提示用户前往设置手动开启权限 |
|
|
Future<void> _showPermanentDenialDialog({ |
|
|
Future<void> _showPermanentDenialDialog({ |
|
|
required BuildContext context, |
|
|
required BuildContext context, |
|
|
required String permissionName, |
|
|
required String permissionName, |
|
|
@ -160,6 +168,8 @@ class PermissionUtil { |
|
|
required String settingsButtonText, |
|
|
required String settingsButtonText, |
|
|
required String cancelButtonText, |
|
|
required String cancelButtonText, |
|
|
}) async { |
|
|
}) async { |
|
|
|
|
|
Logger.i('PermissionUtil', '显示永久拒绝对话框 ($permissionName)'); |
|
|
|
|
|
|
|
|
await showDialog<void>( |
|
|
await showDialog<void>( |
|
|
context: context, |
|
|
context: context, |
|
|
barrierDismissible: false, |
|
|
barrierDismissible: false, |
|
|
@ -168,11 +178,15 @@ class PermissionUtil { |
|
|
content: Text(permanentDenialText), |
|
|
content: Text(permanentDenialText), |
|
|
actions: [ |
|
|
actions: [ |
|
|
TextButton( |
|
|
TextButton( |
|
|
onPressed: () => Navigator.pop(context), |
|
|
onPressed: () { |
|
|
|
|
|
Logger.d('PermissionUtil', '用户取消设置 ($permissionName)'); |
|
|
|
|
|
Navigator.pop(context); |
|
|
|
|
|
}, |
|
|
child: Text(cancelButtonText), |
|
|
child: Text(cancelButtonText), |
|
|
), |
|
|
), |
|
|
ElevatedButton( |
|
|
ElevatedButton( |
|
|
onPressed: () { |
|
|
onPressed: () { |
|
|
|
|
|
Logger.d('PermissionUtil', '用户同意前往设置 ($permissionName)'); |
|
|
Navigator.pop(context); |
|
|
Navigator.pop(context); |
|
|
AppSettings.openAppSettings(); |
|
|
AppSettings.openAppSettings(); |
|
|
}, |
|
|
}, |
|
|
@ -183,8 +197,12 @@ class PermissionUtil { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 显示受限权限对话框 |
|
|
|
|
|
/// |
|
|
|
|
|
/// 在权限受限状态(如iOS上的受限照片访问)时调用 |
|
|
Future<void> _showRestrictedPermissionDialog( |
|
|
Future<void> _showRestrictedPermissionDialog( |
|
|
BuildContext context, String permissionName) async { |
|
|
BuildContext context, String permissionName) async { |
|
|
|
|
|
Logger.w('PermissionUtil', '显示受限权限对话框 ($permissionName)'); |
|
|
await showDialog( |
|
|
await showDialog( |
|
|
context: context, |
|
|
context: context, |
|
|
builder: (context) => AlertDialog( |
|
|
builder: (context) => AlertDialog( |
|
|
@ -192,7 +210,10 @@ class PermissionUtil { |
|
|
content: const Text('此功能在当前设备上不可用或受限,请检查设备设置或系统限制'), |
|
|
content: const Text('此功能在当前设备上不可用或受限,请检查设备设置或系统限制'), |
|
|
actions: [ |
|
|
actions: [ |
|
|
TextButton( |
|
|
TextButton( |
|
|
onPressed: () => Navigator.pop(context), |
|
|
onPressed: () { |
|
|
|
|
|
Logger.d('PermissionUtil', '用户确认受限权限 ($permissionName)'); |
|
|
|
|
|
Navigator.pop(context); |
|
|
|
|
|
}, |
|
|
child: const Text('确定'), |
|
|
child: const Text('确定'), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
|