Explorar el Código

Merge branch 'v1.0.0' of http://git.atmob.com:28999/Atmob-Flutter/AiKeyboard2025 into v1.0.0

hezihao hace 7 meses
padre
commit
b341e36107

+ 4 - 0
assets/string/base/string.xml

@@ -461,4 +461,8 @@
 
     <string name="developer_name">开发者:阜阳奇幻网络科技有限公司</string>
     <string name="record_number">备案号:皖ICP备2025079055号-2A</string>
+
+    <string name="permission_dialog_title">使用权限说明</string>
+    <string name="permission_dialog_authorize">去开启</string>
+    <string name="permission_authorize_fail">授权失败</string>
 </resources>

+ 120 - 0
lib/dialog/permission_dialog.dart

@@ -0,0 +1,120 @@
+import 'package:flutter/Material.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import '../resource/assets.gen.dart';
+import '../resource/colors.gen.dart';
+import '../utils/styles.dart';
+
+class PermissionDialog {
+  static void showRequestDialog(
+      String titleTxt, Widget desc, String sureTxt, String permissionDesc,
+      {Future<bool> Function()? sureClick}) {
+    const tag = 'PermissionDialog';
+    SmartDialog.show(
+        tag: tag,
+        alignment: const Alignment(0, 0),
+        builder: (_) {
+          return Container(
+              width: 300.w,
+              decoration: BoxDecoration(
+                color: Colors.white,
+                borderRadius: BorderRadius.circular(12),
+              ),
+              child: Stack(
+                children: [
+                  Container(
+                    padding: EdgeInsets.symmetric(horizontal: 19.w),
+                    child: IntrinsicHeight(
+                      child: Column(
+                        children: [
+                          SizedBox(height: 20.h),
+                          Text(titleTxt,
+                              style: TextStyle(
+                                  fontWeight: FontWeight.bold,
+                                  fontSize: 18.sp,
+                                  color: Colors.black)),
+                          SizedBox(height: 14.h),
+                          desc,
+                          SizedBox(height: 30.h),
+                          GestureDetector(
+                            onTap: () async {
+                              if (sureClick != null) {
+                                Get.snackbar(
+                                  animationDuration: Duration(milliseconds: 0),
+                                  '权限请求',
+                                  permissionDesc,
+                                  snackPosition: SnackPosition.TOP,
+                                  backgroundColor:
+                                      Colors.black.withOpacity(0.8),
+                                  colorText: Colors.white,
+                                  duration:
+                                      null, // 设置 duration 为 null,使得 Snackbar 持久显示
+                                );
+                                bool shouldDismiss = await sureClick();
+                                // 显示一个Snackbar,提示用户权限请求的原因
+                                Get.closeAllSnackbars();
+                                if (shouldDismiss) {
+                                  SmartDialog.dismiss(tag: tag);
+                                }
+                                // close snackbar
+                              }
+                            },
+                            child: Container(
+                              decoration: Styles.getActivateButtonDecoration(50.r),
+                              width: 130.w,
+                              height: 40.w,
+                              child: Center(
+                                child: Text(sureTxt,
+                                    style: TextStyle(
+                                        fontSize: 16.sp, color: Colors.white)),
+                              ),
+                            ),
+                          ),
+                          SizedBox(height: 24.h),
+                        ],
+                      ),
+                    ),
+                  ),
+                  Positioned(
+                    top: 17.w,
+                    right: 18.w,
+                    child: GestureDetector(
+                      onTap: () {
+                        SmartDialog.dismiss(tag: tag);
+                      },
+                      child:  Assets.images.iconCustomDialogClose.image(
+                        width: 24.w,
+                        height: 24.w,
+                      ),
+                    ),
+                  )
+                ],
+              ));
+        });
+  }
+
+
+  static Widget buildStorageView() {
+    return RichText(
+      textAlign: TextAlign.center,
+      text: TextSpan(
+          style: TextStyle(fontSize: 15.sp, color: ColorName.black80),
+          children: const <TextSpan>[
+            TextSpan(
+              text: '使用该功能App需要访问您设备权限',
+            ),
+            TextSpan(
+                text: '“照片和媒体”',
+                style: TextStyle(
+                    fontWeight: FontWeight.bold, color: ColorName.black90)),
+            TextSpan(
+              text: ',开启权限后,您即可上传图片并体验相关服务。',
+            ),
+          ]),
+    );
+  }
+
+
+}

+ 6 - 0
lib/resource/string.gen.dart

@@ -343,6 +343,9 @@ class StringName {
   static final String characterCustomListEmptyBtnDesc = 'character_custom_list_empty_btn_desc'.tr; // 立即定制人设 >
   static final String developerName = 'developer_name'.tr; // 开发者:阜阳奇幻网络科技有限公司
   static final String recordNumber = 'record_number'.tr; // 备案号:皖ICP备2025079055号-2A
+  static final String permissionDialogTitle = 'permission_dialog_title'.tr; // 使用权限说明
+  static final String permissionDialogAuthorize = 'permission_dialog_authorize'.tr; // 去开启
+  static final String permissionAuthorizeFail = 'permission_authorize_fail'.tr; // 授权失败
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -689,6 +692,9 @@ class StringMultiSource {
       'character_custom_list_empty_btn_desc': '立即定制人设 >',
       'developer_name': '开发者:阜阳奇幻网络科技有限公司',
       'record_number': '备案号:皖ICP备2025079055号-2A',
+      'permission_dialog_title': '使用权限说明',
+      'permission_dialog_authorize': '去开启',
+      'permission_authorize_fail': '授权失败',
     },
   };
 }

+ 118 - 35
lib/utils/image_picker_util.dart

@@ -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;
 }
+
+

+ 3 - 0
pubspec.yaml

@@ -14,6 +14,9 @@ dependencies:
   #框架
   get: ^4.7.2
 
+  #权限申请
+  permission_handler: ^12.0.0+1
+
   # 网络请求
   dio: ^5.8.0+1
   retrofit: '>=4.0.0 <5.0.0'