You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.3 KiB

import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:app_settings/app_settings.dart';
import 'dart:developer';
import 'logger.dart';
// class Logger {
// static void e(String tag, String message) {
// debugPrint('[$tag] ERROR: $message');
// }
// }
class PermissionUtil {
static final PermissionUtil instance = PermissionUtil._();
PermissionUtil._();
OverlayEntry? _explanationOverlayEntry;
Future<bool> requestPermission({
required BuildContext context,
required Permission permissionType,
required String permissionName,
required String explanationText,
required String permanentDenialText,
String settingsButtonText = '前往设置',
String cancelButtonText = '取消',
}) async {
try {
final PermissionStatus status = await permissionType.status;
log('初始权限状态 for $permissionName: $status');
// 处理已授权状态(包括授予状态和临时通知权限)
if (status == PermissionStatus.granted ||
status == PermissionStatus.provisional) {
log('$permissionName 权限已授予或临时授权。');
return true;
}
// 处理永久拒绝状态
if (status == PermissionStatus.permanentlyDenied) {
log('$permissionName 权限被永久拒绝,显示引导对话框。');
await _showPermanentDenialDialog(
context: context,
permissionName: permissionName,
permanentDenialText: permanentDenialText,
settingsButtonText: settingsButtonText,
cancelButtonText: cancelButtonText,
);
return false;
}
// 处理初始拒绝/未决定状态
if (status == PermissionStatus.denied) {
log('$permissionName 权限被拒绝或未决定,显示说明横幅并请求。');
_showExplanationOverlay(
context: context,
permissionName: permissionName,
explanationText: explanationText,
);
final PermissionStatus newStatus = await permissionType.request();
log('请求后 $permissionName 的新权限状态: $newStatus');
_hideExplanationOverlay();
// 处理请求后的状态
if (newStatus == PermissionStatus.granted ||
newStatus == PermissionStatus.provisional) {
log('$permissionName 权限在请求后被授予或临时授权。');
return true;
} else if (newStatus == PermissionStatus.permanentlyDenied) {
log('$permissionName 权限在请求后被永久拒绝,再次显示引导对话框。');
await _showPermanentDenialDialog(
context: context,
permissionName: permissionName,
permanentDenialText: permanentDenialText,
settingsButtonText: settingsButtonText,
cancelButtonText: cancelButtonText,
);
}
return false;
}
// 处理受限状态
if (status == PermissionStatus.restricted ||
status == PermissionStatus.limited) {
log('$permissionName 权限受限或受限访问: $status');
await _showRestrictedPermissionDialog(context, permissionName);
return false;
}
// 其他未知状态处理
log('$permissionName 处于未知状态: $status');
return false;
} catch (e) {
Logger.e('PermissionUtil', '请求 $permissionName 权限失败: $e');
_hideExplanationOverlay();
return false;
}
}
void _showExplanationOverlay({
required BuildContext context,
required String permissionName,
required String explanationText,
}) {
_hideExplanationOverlay(); // 确保移除旧叠加层
_explanationOverlayEntry = OverlayEntry(
builder: (context) => Positioned(
top: MediaQuery.of(context).padding.top + 10,
left: 16,
right: 16,
child: Material(
borderRadius: BorderRadius.circular(12),
elevation: 4,
color: Colors.white,
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!);
log('权限说明叠加层已显示。');
}
});
}
void _hideExplanationOverlay() {
_explanationOverlayEntry?.remove();
_explanationOverlayEntry = null;
}
Future<void> _showPermanentDenialDialog({
required BuildContext context,
required String permissionName,
required String permanentDenialText,
required String settingsButtonText,
required String cancelButtonText,
}) async {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text('需要$permissionName权限'),
content: Text(permanentDenialText),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(cancelButtonText),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
AppSettings.openAppSettings();
},
child: Text(settingsButtonText),
),
],
),
);
}
Future<void> _showRestrictedPermissionDialog(
BuildContext context, String permissionName) async {
await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('$permissionName权限受限'),
content: const Text('此功能在当前设备上不可用或受限,请检查设备设置或系统限制'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('确定'),
),
],
),
);
}
}