28 changed files with 1676 additions and 915 deletions
@ -0,0 +1,202 @@ |
|||
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('确定'), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
Binary file not shown.
@ -1,2 +0,0 @@ |
|||
#Wed May 21 01:03:35 CST 2025 |
|||
gradle.version=8.9 |
|||
@ -1,26 +0,0 @@ |
|||
package com.jieli.otasdk.model |
|||
|
|||
import android.bluetooth.BluetoothDevice |
|||
|
|||
/** |
|||
* @author zqjasonZhong |
|||
* @since 2022/9/9 |
|||
* @email zhongzhuocheng@zh-jieli.com |
|||
* @desc 设备连接状态 |
|||
*/ |
|||
class DeviceConnection(val device: BluetoothDevice?, var state: Int) { |
|||
|
|||
override fun hashCode(): Int { |
|||
return device.hashCode() |
|||
} |
|||
|
|||
override fun equals(other: Any?): Boolean { |
|||
if (other !is DeviceConnection) return false |
|||
return device == other.device |
|||
} |
|||
|
|||
override fun toString(): String { |
|||
return "DeviceConnection(device=$device, state=$state)" |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue