|
|
@@ -1,68 +1,149 @@
|
|
|
import 'dart:io';
|
|
|
|
|
|
+import 'package:device_info_plus/device_info_plus.dart';
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
import 'package:keyboard/utils/app_setting_util.dart';
|
|
|
import 'package:keyboard/utils/toast_util.dart';
|
|
|
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
|
|
|
|
|
+import '../dialog/permission_dialog.dart';
|
|
|
import '../resource/colors.gen.dart';
|
|
|
import '../resource/string.gen.dart';
|
|
|
-
|
|
|
+import 'package:permission_handler/permission_handler.dart';
|
|
|
/// 本地选择图片工具类
|
|
|
class ImagePickerUtil {
|
|
|
static final Color _themeColor = ColorName.colorBrand;
|
|
|
|
|
|
+
|
|
|
+ /// 请求图片权限(适配 iOS / Android)
|
|
|
+ static Future<bool> requestPermissionExtend() async {
|
|
|
+ if (Platform.isAndroid) {
|
|
|
+ final androidInfo = await DeviceInfoPlugin().androidInfo;
|
|
|
+ if (androidInfo.version.sdkInt <= 32) {
|
|
|
+ // Android 版本 <= 32,使用 Permission.storage
|
|
|
+ final status = await Permission.storage.request();
|
|
|
+ return status.isGranted;
|
|
|
+ } else {
|
|
|
+ // Android 版本 > 32,使用 Permission.photos
|
|
|
+ final status = await Permission.photos.request();
|
|
|
+ return status.isGranted;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // iOS 请求照片权限
|
|
|
+ final status = await Permission.photos.request();
|
|
|
+ return status.isGranted;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 判断是否已有权限
|
|
|
+ static Future<bool> hasPermission() async {
|
|
|
+ if (Platform.isAndroid) {
|
|
|
+ final androidInfo = await DeviceInfoPlugin().androidInfo;
|
|
|
+ if (androidInfo.version.sdkInt <= 32) {
|
|
|
+ // Android 版本 <= 32,使用 Permission.storage
|
|
|
+ final status = await Permission.storage.status;
|
|
|
+ return status.isGranted;
|
|
|
+ } else {
|
|
|
+ // Android 版本 > 32,使用 Permission.photos
|
|
|
+ final status = await Permission.photos.status;
|
|
|
+ return status.isGranted;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // iOS 判断照片权限
|
|
|
+ final status = await Permission.photos.status;
|
|
|
+ return status.isGranted;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 判断是否为“永久拒绝权限”
|
|
|
+ static Future<bool> isPermissionPermanentlyDenied() async {
|
|
|
+ if (Platform.isAndroid) {
|
|
|
+ final androidInfo = await DeviceInfoPlugin().androidInfo;
|
|
|
+ if (androidInfo.version.sdkInt <= 32) {
|
|
|
+ // Android 版本 <= 32,使用 Permission.storage
|
|
|
+ final status = await Permission.storage.status;
|
|
|
+ return status.isPermanentlyDenied;
|
|
|
+ } else {
|
|
|
+ // Android 版本 > 32,使用 Permission.photos
|
|
|
+ final status = await Permission.photos.status;
|
|
|
+ return status.isPermanentlyDenied;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // iOS 判断照片权限是否永久拒绝
|
|
|
+ final status = await Permission.photos.status;
|
|
|
+ return status.isPermanentlyDenied;
|
|
|
+ }
|
|
|
+ }
|
|
|
/// 选择图片
|
|
|
static Future<List<AssetEntity>?> pickImage(
|
|
|
- BuildContext context, {
|
|
|
- required int maxAssetsCount,
|
|
|
- List<AssetEntity> selectedAssets = const [],
|
|
|
- }) async {
|
|
|
+ BuildContext context, {
|
|
|
+ required int maxAssetsCount,
|
|
|
+ List<AssetEntity> selectedAssets = const [],
|
|
|
+ }) async {
|
|
|
+
|
|
|
+ bool isAllow = await ImagePickerUtil.hasPermission();
|
|
|
+ if (!isAllow) {
|
|
|
+
|
|
|
+ PermissionDialog.showRequestDialog(
|
|
|
+ StringName.permissionDialogTitle,
|
|
|
+ PermissionDialog.buildStorageView(),
|
|
|
+ StringName.permissionDialogAuthorize,
|
|
|
+ "使用该功能App需要访问您设备“照片和媒体”权限,开启权限后,您可以上传图片使用该功能使用",
|
|
|
+ sureClick: () async {
|
|
|
+ bool isGranted = await ImagePickerUtil.requestPermissionExtend();
|
|
|
+ if (isGranted) {
|
|
|
+ await pickImage(context, maxAssetsCount: maxAssetsCount, selectedAssets: selectedAssets);
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ if (await ImagePickerUtil.isPermissionPermanentlyDenied()) {
|
|
|
+ Future.delayed(const Duration(milliseconds: 400), () {
|
|
|
+ AppSettingUtil.jumpSystemAppSetting();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ ToastUtil.show(StringName.pickerImagePermissionDeniedTip);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ );
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 2. 权限已授权,打开选择器
|
|
|
try {
|
|
|
return await AssetPicker.pickAssets(
|
|
|
- context,
|
|
|
- pickerConfig: AssetPickerConfig(
|
|
|
- // 最大选择数量
|
|
|
- maxAssets: maxAssetsCount,
|
|
|
- // 已选择的图片列表
|
|
|
- selectedAssets: selectedAssets,
|
|
|
- // 自定义按钮文字
|
|
|
- textDelegate: const CustomChineseDelegate(),
|
|
|
- // 主题
|
|
|
- pickerTheme: AssetPicker.themeData(
|
|
|
- // 主题色
|
|
|
- _themeColor,
|
|
|
- // 深色默认
|
|
|
- light: false,
|
|
|
- ),
|
|
|
- // 设置为不能预览的模式
|
|
|
- specialPickerType: SpecialPickerType.noPreview,
|
|
|
- // 只能选取图片类型
|
|
|
- requestType: RequestType.image,
|
|
|
- // 关闭拽托选择
|
|
|
- dragToSelect: false,
|
|
|
- // 实现最近相册的名字显示
|
|
|
- pathNameBuilder:
|
|
|
- (AssetPathEntity path) => switch (path) {
|
|
|
- final p when p.isAll => StringName.recently,
|
|
|
- _ => path.name,
|
|
|
- },
|
|
|
- ),
|
|
|
- ) ??
|
|
|
+ context,
|
|
|
+ pickerConfig: AssetPickerConfig(
|
|
|
+ maxAssets: maxAssetsCount,
|
|
|
+ selectedAssets: selectedAssets,
|
|
|
+ textDelegate: const CustomChineseDelegate(),
|
|
|
+ pickerTheme: AssetPicker.themeData(
|
|
|
+ _themeColor,
|
|
|
+ light: false,
|
|
|
+ ),
|
|
|
+ specialPickerType: SpecialPickerType.noPreview,
|
|
|
+ requestType: RequestType.image,
|
|
|
+ dragToSelect: false,
|
|
|
+ pathNameBuilder: (AssetPathEntity path) => switch (path) {
|
|
|
+ final p when p.isAll => StringName.recently,
|
|
|
+ _ => path.name,
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ) ??
|
|
|
[];
|
|
|
} catch (e) {
|
|
|
if (e is StateError) {
|
|
|
ToastUtil.show(StringName.pickerImagePermissionDeniedTip);
|
|
|
- Future.delayed(Duration(milliseconds: 400), () {
|
|
|
+ Future.delayed(const Duration(milliseconds: 400), () {
|
|
|
AppSettingUtil.jumpSystemAppSetting();
|
|
|
});
|
|
|
} else {
|
|
|
rethrow;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/// 跳转到图片预览
|
|
|
static Future<List<AssetEntity>> goImagePreview(
|
|
|
BuildContext context,
|
|
|
@@ -108,3 +189,5 @@ class CustomChineseDelegate extends AssetPickerTextDelegate {
|
|
|
@override
|
|
|
String get confirm => StringName.nextStep;
|
|
|
}
|
|
|
+
|
|
|
+
|