image_picker_util.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. import 'dart:io';
  2. import 'dart:math';
  3. import 'package:clean/data/bean/photos_type.dart';
  4. import 'package:clean/model/asset_group.dart';
  5. import 'package:clean/module/locations_photo/locations_photo_controller.dart';
  6. import 'package:clean/module/locations_photo/locations_single_photo_controller.dart';
  7. import 'package:clean/module/people_photo/people_photo_controller.dart';
  8. import 'package:clean/module/people_photo/photo_group.dart';
  9. import 'package:clean/module/photo_preview/photo_preview_controller.dart';
  10. import 'package:clean/module/screenshots_blurry/screenshots_controller.dart';
  11. import 'package:clean/module/similar_photo/similar_photo_controller.dart';
  12. import 'package:get/get.dart';
  13. import 'package:photo_manager/photo_manager.dart';
  14. class ImagePickerUtil {
  15. ImagePickerUtil._();
  16. static const RequestType permissionType = RequestType.image;
  17. static final RxInt similarPhotoCount = 0.obs;
  18. // 全局存储不同类型的照片
  19. // 截图图片
  20. static final RxList<AssetEntity> screenshotPhotos = <AssetEntity>[].obs;
  21. // 模糊图片
  22. static final RxList<AssetEntity> blurryPhotos = <AssetEntity>[].obs;
  23. // 相似图片
  24. static final RxList<List<AssetEntity>> similarPhotos =
  25. <List<AssetEntity>>[].obs;
  26. // 地点图片
  27. static final RxMap<String, List<AssetEntity>> locationPhotos =
  28. <String, List<AssetEntity>>{}.obs;
  29. // 人物图片
  30. static final RxList<AssetEntity> peoplePhotos = <AssetEntity>[].obs;
  31. static final RxSet<String> selectedScreenshotPhotosIds = <String>{}.obs;
  32. static final RxSet<String> selectedSimilarPhotosIds = <String>{}.obs;
  33. static final RxSet<String> selectedLocationPhotosIds = <String>{}.obs;
  34. static final RxSet<String> selectedPeoplePhotosIds = <String>{}.obs;
  35. static final RxSet<String> selectedBlurryPhotosIds = <String>{}.obs;
  36. // 添加大小信息的变量
  37. static final Rx<int> screenshotsSize = 0.obs;
  38. static final Rx<int> blurrySize = 0.obs;
  39. static final Rx<int> locationsSize = 0.obs;
  40. static final Rx<int> peopleSize = 0.obs;
  41. static final Rx<int> similarPhotosSize = 0.obs;
  42. // 用来缓存文件大小
  43. static final Map<String, double> fileSizeCache = {};
  44. // 清除所有照片数据
  45. static void clearAllPhotos() {
  46. screenshotPhotos.clear();
  47. blurryPhotos.clear();
  48. similarPhotos.clear();
  49. locationPhotos.clear();
  50. peoplePhotos.clear();
  51. }
  52. static Future<void> updatePhotoGroupDate(PhotosType photosType,Set<String> selectedPhotosIds) async {
  53. // 1. 统一移除相关的照片列表
  54. screenshotPhotos.removeWhere((element) => selectedPhotosIds.contains(element.id));
  55. for (var group in similarPhotos) {
  56. group.removeWhere((element) => selectedPhotosIds.contains(element.id));
  57. }
  58. locationPhotos.forEach((key, group) => group.removeWhere((element) => selectedPhotosIds.contains(element.id)));
  59. peoplePhotos.removeWhere((element) => selectedPhotosIds.contains(element.id));
  60. blurryPhotos.removeWhere((element) => selectedPhotosIds.contains(element.id));
  61. // 2. 移除空的集合
  62. similarPhotos.removeWhere((element) => element.isEmpty);
  63. locationPhotos.removeWhere((key, value) => value.isEmpty);
  64. selectedScreenshotPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  65. selectedSimilarPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  66. selectedLocationPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  67. selectedPeoplePhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  68. selectedBlurryPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  69. // 3. 根据photosType来处理不同的控制器和photoGroups
  70. switch (photosType) {
  71. case PhotosType.screenshots:
  72. ScreenShotsController screenshotController = Get.find<ScreenShotsController>();
  73. screenshotController.photoGroups.removeWhere((element) => element.images.any((image) => selectedPhotosIds.contains(image.id)));
  74. screenshotController.photoGroups.removeWhere((element) => element.images.isEmpty);
  75. selectedScreenshotPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  76. screenshotController.restoreSelections();
  77. break;
  78. case PhotosType.similarPhotos:
  79. SimilarPhotoController similarController = Get.find<SimilarPhotoController>();
  80. similarController.photoGroups.removeWhere((element) => element.images.any((image) => selectedPhotosIds.contains(image.id)));
  81. similarController.photoGroups.removeWhere((element) => element.images.isEmpty);
  82. selectedSimilarPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  83. similarController.restoreSelections();
  84. break;
  85. case PhotosType.locationPhotos:
  86. LocationsSinglePhotoController locationsSingleController = Get.find<LocationsSinglePhotoController>();
  87. locationsSingleController.photoGroups.removeWhere((element) => element.images.any((image) => selectedPhotosIds.contains(image.id)));
  88. locationsSingleController.photoGroups.removeWhere((element) => element.images.isEmpty);
  89. LocationsPhotoController locationsPhotoController = Get.find<LocationsPhotoController>();
  90. locationsPhotoController.photoGroups.removeWhere((element) => element.images.any((image) => selectedPhotosIds.contains(image.id)));
  91. locationsPhotoController.photoGroups.removeWhere((element) => element.images.isEmpty);
  92. selectedLocationPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  93. locationsSingleController.restoreSelections();
  94. locationsPhotoController.restoreSelections();
  95. break;
  96. case PhotosType.peoplePhotos:
  97. PeoplePhotoController peoplePhotoController = Get.find<PeoplePhotoController>();
  98. peoplePhotoController.photoGroups.removeWhere((element) => element.images.any((image) => selectedPhotosIds.contains(image.id)));
  99. peoplePhotoController.photoGroups.removeWhere((element) => element.images.isEmpty);
  100. selectedPeoplePhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  101. peoplePhotoController.restoreSelections();
  102. break;
  103. case PhotosType.blurryPhotos:
  104. ScreenShotsController blurryController = Get.find<ScreenShotsController>();
  105. blurryController.photoGroups.removeWhere((element) => element.images.any((image) => selectedPhotosIds.contains(image.id)));
  106. blurryController.photoGroups.removeWhere((element) => element.images.isEmpty);
  107. selectedBlurryPhotosIds.removeWhere((element) => selectedPhotosIds.contains(element));
  108. blurryController.restoreSelections();
  109. break;
  110. }
  111. }
  112. // 更新照片数据
  113. static Future<void> updatePhotos(
  114. List<Map<String, dynamic>> photoGroups) async {
  115. clearAllPhotos();
  116. for (var group in photoGroups) {
  117. String type = group['type'] as String;
  118. Map<dynamic, dynamic> groupData = group['group'] as Map<dynamic, dynamic>;
  119. List<dynamic> photos = groupData['photos'] as List<dynamic>;
  120. int totalSize = groupData['totalSize'] as int;
  121. int count = groupData['count'] as int;
  122. switch (type) {
  123. case 'screenshots':
  124. screenshotPhotos.value = await _convertToAssetEntities(photos);
  125. screenshotsSize.value = totalSize;
  126. break;
  127. case 'similar':
  128. similarPhotos.add(await _convertToAssetEntities(photos));
  129. similarPhotosSize.value = totalSize;
  130. similarPhotoCount.value = count;
  131. break;
  132. case 'location':
  133. String location = group['name'] as String;
  134. locationPhotos[location] = await _convertToAssetEntities(photos);
  135. locationsSize.value = totalSize;
  136. break;
  137. case 'people':
  138. peoplePhotos.value = await _convertToAssetEntities(photos);
  139. peopleSize.value = totalSize;
  140. break;
  141. case 'blurry':
  142. blurryPhotos.value = await _convertToAssetEntities(photos);
  143. blurrySize.value = totalSize;
  144. break;
  145. }
  146. }
  147. }
  148. // 将原始照片数据转换为 AssetEntity 列表
  149. static Future<List<AssetEntity>> _convertToAssetEntities(
  150. List<dynamic> photos) async {
  151. List<AssetEntity> entities = [];
  152. for (var photo in photos) {
  153. final entity = await AssetEntity.fromId(photo['id'] as String);
  154. if (entity != null) {
  155. entities.add(entity);
  156. }
  157. }
  158. return entities;
  159. }
  160. //申请权限
  161. static Future<bool> requestPermissionExtend() async {
  162. final PermissionState ps = await PhotoManager.requestPermissionExtend(
  163. requestOption: const PermissionRequestOption(
  164. androidPermission: AndroidPermission(
  165. type: permissionType,
  166. mediaLocation: false,
  167. )));
  168. return ps.hasAccess;
  169. }
  170. //判断是否有权限
  171. static Future<bool> hasPermission() async {
  172. final PermissionState ps = await PhotoManager.getPermissionState(
  173. requestOption: const PermissionRequestOption(
  174. androidPermission: AndroidPermission(
  175. type: permissionType,
  176. mediaLocation: false,
  177. )));
  178. return ps.hasAccess;
  179. }
  180. static String formatFileSize(int bytes, {int decimals = 2}) {
  181. if (bytes <= 0) return '0 B';
  182. const suffixes = ['B', 'KB', 'MB', 'GB', 'TB'];
  183. var i = (log(bytes) / log(1024)).floor();
  184. // 确保不超过数组范围
  185. i = i < suffixes.length ? i : suffixes.length - 1;
  186. // 计算实际大小
  187. final size = bytes / pow(1024, i);
  188. // 格式化数字,处理小数点位数
  189. String sizeStr;
  190. if (size >= 100) {
  191. // 大于100的只保留整数
  192. sizeStr = size.round().toString();
  193. } else {
  194. // 小于100的保留指定小数位
  195. sizeStr = size.toStringAsFixed(decimals);
  196. }
  197. // 移除末尾的0和不必要的小数点
  198. sizeStr = sizeStr.replaceAll(RegExp(r'\.?0+$'), '');
  199. return '$sizeStr ${suffixes[i]}';
  200. }
  201. /// 直接转换为 GB 单位
  202. static String formatToGB(int bytes, {int decimals = 2}) {
  203. if (bytes <= 0) return '0 GB';
  204. final gb = bytes / pow(1024, 3); // 直接转换为 GB
  205. // 格式化数字,处理小数点位数
  206. String sizeStr;
  207. if (gb >= 100) {
  208. // 大于100的只保留整数
  209. sizeStr = gb.round().toString();
  210. } else {
  211. // 小于100的保留指定小数位
  212. sizeStr = gb.toStringAsFixed(decimals);
  213. }
  214. // 移除末尾的0和不必要的小数点
  215. sizeStr = sizeStr.replaceAll(RegExp(r'\.?0+$'), '');
  216. return '$sizeStr GB';
  217. }
  218. /// 格式化存储大小
  219. static String formatSize(double sizeInGB) {
  220. if (sizeInGB < 0.001) {
  221. // 小于 1MB
  222. return '${(sizeInGB * 1024 * 1024).toStringAsFixed(1)} ';
  223. } else if (sizeInGB < 1) {
  224. // 小于 1GB
  225. return '${(sizeInGB * 1024).toStringAsFixed(1)} ';
  226. } else if (sizeInGB < 1024) {
  227. // 小于 1TB
  228. return '${sizeInGB.toStringAsFixed(1)} ';
  229. } else {
  230. // 大于等于 1TB
  231. return '${(sizeInGB / 1024).toStringAsFixed(1)} ';
  232. }
  233. }
  234. static Future<List<AssetEntity>> loadAssetsAndroid({bool sortByDate = true}) async {
  235. final PermissionState result = await PhotoManager.requestPermissionExtend();
  236. if (!result.isAuth) return [];
  237. // 选择相册
  238. final List<AssetPathEntity> albums = await PhotoManager.getAssetPathList(
  239. type: RequestType.image,
  240. filterOption: FilterOptionGroup(
  241. orders: [
  242. // 根据创建日期排序,降序(最新的在前)
  243. OrderOption(
  244. type: OrderOptionType.createDate,
  245. asc: !sortByDate, // 是否升序排列
  246. ),
  247. ],
  248. ),
  249. );
  250. if (albums.isEmpty) return [];
  251. // 获取图片资源
  252. final List<AssetEntity> assets = await albums.first.getAssetListPaged(
  253. page: 0,
  254. size: 10000, // 获取 10000 张图片
  255. );
  256. return assets;
  257. }
  258. }