| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663 |
- import 'dart:async';
- import 'dart:io';
- import 'package:classify_photo/classify_photo.dart';
- import 'package:clean/widget/multi_segment_circle_indicator.dart';
- import 'package:disk_space/disk_space.dart';
- import 'package:flutter/Material.dart';
- import 'package:get/get.dart';
- import 'package:permission_handler/permission_handler.dart';
- import 'package:wechat_assets_picker/wechat_assets_picker.dart';
- import '../../module/image_picker/image_picker_util.dart';
- import '../utils/toast_util.dart';
- class PhotoScanHandler {
- static final _photoClassify = ClassifyPhoto();
- static RxBool isSimilarScanned = false.obs;
- static RxBool isPeopleScanned = false.obs;
- static RxBool isScreenShotScanned = false.obs;
- static RxBool isBlurryScanned = false.obs;
- // 人物图片
- static RxList<AssetEntity> peoplePhotos = <AssetEntity>[].obs;
- // 地点图片
- static Rx<AssetEntity?> locationPhoto = Rx<AssetEntity?>(null);
- // 截图照片
- static Rx<AssetEntity?> screenshotPhoto = Rx<AssetEntity?>(null);
- // 模糊照片
- static Rx<AssetEntity?> blurryPhoto = Rx<AssetEntity?>(null);
- // 相似照片
- static RxList<AssetEntity> similarPhotos = <AssetEntity>[].obs;
- // 添加一个标志来跟踪当前扫描状态
- bool _isScanningCancelled = false;
- List<StreamSubscription> _scanningTasks = [];
- static Rx<double> totalSpace = 0.0.obs;
- static Rx<double> usedSpace = 0.0.obs;
- static Rx<double> photoSpace = 0.0.obs;
- static Rx<double> freeSpace = 0.0.obs;
- static Rx<String> totalSpaceStr = "0.0 GB".obs;
- static Rx<String> usedSpaceStr = "0.0 GB".obs;
- static Rx<String> photoSpaceStr = "0.0 GB".obs;
- static Rx<String> freeSpaceStr = "0.0 GB".obs;
- // 计算已用存储百分比
- static double get usedSpacePercentage => (usedSpace.value / totalSpace.value) * 100;
- // 计算照片占用存储百分比
- static double get photoSpacePercentage =>
- (photoSpace.value / totalSpace.value) * 100;
- // 计算可用存储百分比
- static double get freeSpacePercentage => (freeSpace.value / totalSpace.value) * 100;
- static List<PieData> get pieDataList => [
- PieData("PhotoSpace", photoSpacePercentage, Colors.blue),
- PieData("OtherUsed", usedSpacePercentage - photoSpacePercentage,
- Colors.red),
- PieData("totalSpace", totalSpace.value, Colors.grey.withOpacity(0.1)),
- ];
- // 存储是否扫描完成
- static RxBool isStorageScanned = false.obs;
- // 监听照片库变化
- void _listenToPhotoLibraryChanges() {
- PhotoManager.addChangeCallback((change) {
- print("照片库发生变化: $change");
- // 取消当前正在进行的扫描
- _cancelCurrentScan();
- // 延迟一小段时间后重新开始扫描
- Future.delayed(Duration(milliseconds: 500), () {
- _restartScan();
- });
- });
- // 开始监听
- PhotoManager.startChangeNotify();
- }
- // 取消当前扫描
- void _cancelCurrentScan() {
- _isScanningCancelled = true;
- // 取消所有正在进行的异步任务
- for (var subscription in _scanningTasks) {
- subscription.cancel();
- }
- _scanningTasks.clear();
- print("已取消当前扫描任务");
- }
- // 重新开始扫描
- void _restartScan() {
- _isScanningCancelled = false;
- isSimilarScanned.value = false;
- isPeopleScanned.value = false;
- isBlurryScanned.value = false;
- isScreenShotScanned.value = false;
- // 清除之前的结果
- similarPhotos.clear();
- peoplePhotos.clear();
- locationPhoto.value = null;
- screenshotPhoto.value = null;
- blurryPhoto.value = null;
- print("开始重新扫描照片");
- // 重新开始扫描流程
- startScanningProcess();
- }
- // 开始扫描过程
- Future<void> startScanningProcess() async {
- // 确保在开始新扫描前取消标志被重置
- _isScanningCancelled = false;
- // 清空之前的任务
- _scanningTasks.clear();
- try {
- // 按顺序执行扫描任务
- print('开始顺序扫描照片');
- // 1. 先扫描截图照片
- if (!_isScanningCancelled) {
- var screenTask = _handleScreenPhotosWithCancellation();
- _scanningTasks.add(screenTask);
- // 等待截图扫描完成
- await Future.delayed(Duration(milliseconds: 500));
- }
- // 2. 再扫描模糊照片
- if (!_isScanningCancelled) {
- var blurryTask = _handleBlurryPhotosWithCancellation();
- _scanningTasks.add(blurryTask);
- // 等待模糊照片扫描完成
- await Future.delayed(Duration(milliseconds: 500));
- }
- // 3. 然后扫描人物照片
- if (!_isScanningCancelled) {
- var peopleTask = _handlePeoplePhotosWithCancellation();
- _scanningTasks.add(peopleTask);
- // 等待人物照片扫描完成
- await Future.delayed(Duration(milliseconds: 500));
- }
- // 4. 最后扫描相似照片
- if (!_isScanningCancelled) {
- var similarTask = _handleSimilarPhotosWithCancellation();
- _scanningTasks.add(similarTask);
- }
- print('所有扫描任务已启动');
- } catch (e) {
- print('扫描过程中出错: $e');
- }
- }
- Future<void> getStorageInfo() async {
- if (Platform.isAndroid) {
- // 延迟3秒
- Future.delayed(const Duration(seconds: 3), () {
- isStorageScanned.value = true;
- totalSpace.value = 200.0;
- usedSpace.value = 50.0;
- photoSpace.value = 30.0;
- freeSpace.value = 150.0;
- });
- }
- final totalSpaceGB = (await DiskSpace.getTotalDiskSpace ?? 0) / 1000;
- final freeSpaceGB = (await DiskSpace.getFreeDiskSpace ?? 0) / 1024;
- final usedSpaceGB = ((await DiskSpace.getTotalDiskSpace ?? 0) - (await DiskSpace.getFreeDiskSpace ?? 0)) / 1024;
- totalSpaceStr.value = ImagePickerUtil.formatFileSize(
- (await DiskSpace.getTotalDiskSpace ?? 0).round(),
- decimals: 1);
- freeSpaceStr.value = ImagePickerUtil.formatFileSize(
- (await DiskSpace.getFreeDiskSpace ?? 0).round(),
- decimals: 1);
- usedSpaceStr.value = ImagePickerUtil.formatFileSize(
- ((await DiskSpace.getTotalDiskSpace ?? 0) - (await DiskSpace.getFreeDiskSpace ?? 0)).round(),
- decimals: 1);
- totalSpace.value = totalSpaceGB.round().toDouble();
- freeSpace.value = freeSpaceGB;
- usedSpace.value = usedSpaceGB;
- final classifyPhoto = ClassifyPhoto();
- try {
- final storageInfo = await classifyPhoto.getStorageInfo();
- // 转换为 GB
- final totalSpaceGB = storageInfo['totalSpace']! / (1000 * 1000 * 1000);
- final freeSpaceGB = storageInfo['freeSpace']! / (1024 * 1024 * 1024);
- final usedSpaceGB = storageInfo['usedSpace']! / (1024 * 1024 * 1024);
- final photoSpaceGB = storageInfo['photoSpace']! / (1024 * 1024 * 1024);
- totalSpaceStr.value = ImagePickerUtil.formatFileSize(
- storageInfo['totalSpace']!,
- decimals: 1);
- freeSpaceStr.value = ImagePickerUtil.formatFileSize(
- storageInfo['freeSpace']!,
- decimals: 1);
- usedSpaceStr.value = ImagePickerUtil.formatFileSize(
- storageInfo['usedSpace']!,
- decimals: 1);
- photoSpaceStr.value = ImagePickerUtil.formatFileSize(
- storageInfo['photoSpace']!,
- decimals: 1);
- totalSpace.value = totalSpaceGB.round().toDouble();
- freeSpace.value = freeSpaceGB;
- usedSpace.value = usedSpaceGB;
- photoSpace.value = photoSpaceGB;
- print('总容量: $totalSpaceStr');
- print('可用空间: $freeSpaceStr');
- print('已用空间: $usedSpaceStr');
- print('照片占用: $photoSpaceStr');
- isStorageScanned.value = true;
- } catch (e) {
- print('获取存储信息失败: $e');
- }
- }
- /// 执行所有的照片处理操作
- Future<void> handleAllPhotos() async {
- // var request = Platform.isIOS
- // ? await Permission.photos.request()
- // : await Permission.storage.request();
- //
- // if (request.isGranted) {
- // PhotoManager.clearFileCache();
- // await handleScreenPhotos();
- // await handleBlurryPhotos();
- // await handlePeoplePhotos();
- // await handleSimilarPhotos();
- // if (Platform.isAndroid) {
- // await handleAndroidPhotos();
- // }
- // } else if (request.isPermanentlyDenied) {
- // ToastUtil.show("Please enable the album permission");
- // openAppSettings();
- // } else {
- // ToastUtil.show("Please enable the album permission");
- //
- // isSimilarScanned.value = false;
- // isPeopleScanned.value = false;
- // isBlurryScanned.value = false;
- // isScreenShotScanned.value = false;
- // }
- var currentStatus = await Permission.photos.status;
- if (currentStatus.isGranted) {
- // 已有完全权限,直接扫描
- PhotoManager.clearFileCache();
- // _listenToPhotoLibraryChanges(); // 添加监听
- await handleScreenPhotos();
- await handleBlurryPhotos();
- await handlePeoplePhotos();
- await handleSimilarPhotos();
- } else if (currentStatus.isLimited) {
- // 已有有限权限,显示自定义弹窗
- PhotoManager.clearFileCache();
- await handleScreenPhotos();
- await handleBlurryPhotos();
- await handlePeoplePhotos();
- await handleSimilarPhotos();
- } else {
- // 未授权,请求权限
- var result = await Permission.photos.request();
- if (result.isGranted || result.isLimited) {
- PhotoManager.clearFileCache();
- getStorageInfo();
- await handleScreenPhotos();
- await handleBlurryPhotos();
- await handlePeoplePhotos();
- await handleSimilarPhotos();
- } else {
- isSimilarScanned.value = true;
- isPeopleScanned.value = true;
- isBlurryScanned.value = true;
- isScreenShotScanned.value = true;
- }
- }
- }
- // 可取消的截图照片扫描
- StreamSubscription _handleScreenPhotosWithCancellation() {
- var controller = StreamController<bool>();
- Future<void> task() async {
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- try {
- print('开始获取截图照片');
- final photoClassify = ClassifyPhoto();
- final photos = await photoClassify.getScreenshots();
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- print('获取截图照片完成: ${photos?.length ?? 0} 组照片');
- isScreenShotScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
- var asset = ImagePickerUtil.screenshotPhotos.first;
- screenshotPhoto.value = asset;
- }
- }
- controller.add(true);
- } catch (e) {
- print('获取截图照片失败: $e');
- controller.add(false);
- }
- }
- task();
- return controller.stream.listen((success) {
- controller.close();
- });
- }
- // 可取消的模糊照片扫描
- StreamSubscription _handleBlurryPhotosWithCancellation() {
- var controller = StreamController<bool>();
- Future<void> task() async {
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- try {
- print('开始获取模糊照片');
- final photoClassify = ClassifyPhoto();
- final photos = await photoClassify.getBlurryPhotos();
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- print('获取模糊照片完成: ${photos?.length ?? 0} 组照片');
- isBlurryScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
- var asset = ImagePickerUtil.blurryPhotos.first;
- blurryPhoto.value = asset;
- }
- }
- controller.add(true);
- } catch (e) {
- print('获取模糊照片失败: $e');
- controller.add(false);
- }
- }
- task();
- return controller.stream.listen((success) {
- controller.close();
- });
- }
- // 可取消的人物照片扫描
- StreamSubscription _handlePeoplePhotosWithCancellation() {
- var controller = StreamController<bool>();
- Future<void> task() async {
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- try {
- print('开始获取人物照片');
- final photoClassify = ClassifyPhoto();
- final photos = await photoClassify.getPeoplePhotos();
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- print('获取人物照片完成: ${photos?.length ?? 0} 组照片');
- isPeopleScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- // 处理人物照片
- peoplePhotos.clear();
- if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
- for (var personPhotos in ImagePickerUtil.peoplePhotos) {
- if (_isScanningCancelled) break;
- peoplePhotos.add(personPhotos);
- if (peoplePhotos.length == 2) {
- break;
- }
- }
- }
- }
- controller.add(true);
- } catch (e) {
- print('获取人物照片失败: $e');
- controller.add(false);
- }
- }
- task();
- return controller.stream.listen((success) {
- controller.close();
- });
- }
- // 可取消的相似照片扫描
- StreamSubscription _handleSimilarPhotosWithCancellation() {
- var controller = StreamController<bool>();
- Future<void> task() async {
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- try {
- print('开始获取相似照片');
- final photoClassify = ClassifyPhoto();
- final photos = await photoClassify.getSimilarPhotos();
- if (_isScanningCancelled) {
- controller.add(false);
- return;
- }
- print('获取相似照片完成: ${photos?.length ?? 0} 组照片');
- isSimilarScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- similarPhotos.clear();
- if (ImagePickerUtil.similarPhotos.isNotEmpty) {
- for (var group in ImagePickerUtil.similarPhotos) {
- if (_isScanningCancelled) break;
- for (var asset in group) {
- similarPhotos.add(asset);
- if (similarPhotos.length == 4) {
- break;
- }
- }
- if (similarPhotos.length == 4) {
- break;
- }
- }
- }
- }
- controller.add(true);
- } catch (e) {
- print('获取相似照片失败: $e');
- controller.add(false);
- }
- }
- task();
- return controller.stream.listen((success) {
- controller.close();
- });
- }
- /// 截图照片
- Future<bool> handleScreenPhotos() async {
- try {
- final photos = await _photoClassify.getScreenshots();
- print('获取截图照片完成: ${photos?.length ?? 0} 组照片');
- isScreenShotScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
- var asset = ImagePickerUtil.screenshotPhotos.first;
- screenshotPhoto.value = asset;
- }
- }
- } catch (e, stackTrace) {
- print('获取截图失败: $e\n$stackTrace');
- }
- isScreenShotScanned.value = true;
- return true;
- }
- /// 模糊照片
- Future<bool> handleBlurryPhotos() async {
- try {
- final photos = await _photoClassify.getBlurryPhotos();
- print('获取模糊照片完成: ${photos?.length ?? 0} 组照片');
- isBlurryScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
- var asset = ImagePickerUtil.blurryPhotos.first;
- blurryPhoto.value = asset;
- }
- return true;
- }
- } catch (e, stackTrace) {
- print('获取模糊失败: $e\n$stackTrace');
- }
- isBlurryScanned.value = true;
- return true;
- }
- /// 人物照片
- Future<bool> handlePeoplePhotos() async {
- try {
- final photos = await _photoClassify.getPeoplePhotos();
- print('获取人物照片完成: ${photos?.length ?? 0} 组照片');
- isPeopleScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- // 处理人物照片
- peoplePhotos.clear();
- if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
- for (var personPhotos in ImagePickerUtil.peoplePhotos) {
- peoplePhotos.add(personPhotos);
- if (peoplePhotos.length == 2) {
- break;
- }
- }
- }
- return true;
- }
- } catch (e, stackTrace) {
- print('获取人物失败: $e\n$stackTrace');
- }
- isPeopleScanned.value = true;
- return true;
- }
- /// 相似照片
- Future<bool> handleSimilarPhotos() async {
- try {
- print('开始获取相似照片');
- final photos = await _photoClassify.getSimilarPhotos();
- print('获取相似照片完成: ${photos?.length ?? 0} 组照片');
- isSimilarScanned.value = true;
- if (photos != null) {
- await ImagePickerUtil.updatePhotos(photos);
- similarPhotos.clear();
- if (ImagePickerUtil.similarPhotos.isNotEmpty) {
- for (var group in ImagePickerUtil.similarPhotos) {
- for (var asset in group) {
- similarPhotos.add(asset);
- if (similarPhotos.length == 4) {
- break;
- }
- }
- }
- }
- return true;
- }
- } catch (e, stackTrace) {
- print('获取相似失败: $e\n$stackTrace');
- }
- isSimilarScanned.value = true;
- return true;
- }
- /// Android平台处理方式
- Future<void> handleAndroidPhotos() async {
- final List<AssetEntity> result = await ImagePickerUtil.loadAssets();
- ImagePickerUtil.peoplePhotos.value = result ?? [];
- ImagePickerUtil.locationPhotos['location'] = result ?? [];
- ImagePickerUtil.screenshotPhotos.value = result ?? [];
- ImagePickerUtil.similarPhotos.add(result ?? []);
- ImagePickerUtil.blurryPhotos.value = result ?? [];
- print("handleAndroidPhotos $result");
- print(
- "ImagePickerUtil.peoplePhotos.value ${ImagePickerUtil.peoplePhotos
- .length}");
- isSimilarScanned.value = true;
- isPeopleScanned.value = true;
- isBlurryScanned.value = true;
- isScreenShotScanned.value = true;
- similarPhotos.clear();
- if (ImagePickerUtil.similarPhotos.isNotEmpty) {
- for (var group in ImagePickerUtil.similarPhotos) {
- print(
- " ImagePickerUtil.similarPhotos ${ImagePickerUtil.similarPhotos
- .length}");
- for (var asset in group) {
- similarPhotos.add(asset);
- if (similarPhotos.length == 4) {
- break;
- }
- }
- }
- }
- peoplePhotos.clear();
- if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
- for (var personPhotos in ImagePickerUtil.peoplePhotos) {
- peoplePhotos.add(personPhotos);
- if (peoplePhotos.length == 2) {
- break;
- }
- }
- }
- if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
- var asset = ImagePickerUtil.blurryPhotos.first;
- blurryPhoto.value = asset;
- }
- if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
- var asset = ImagePickerUtil.screenshotPhotos.first;
- screenshotPhoto.value = asset;
- }
- }
- }
|