import 'dart:async'; import 'dart:io'; import 'package:classify_photo/classify_photo.dart'; import 'package:clean/widget/multi_segment_circle_indicator.dart'; import 'package:flutter/Material.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_classifier/models.dart'; import 'package:photo_classifier/photo_classifier.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart'; import '../../module/image_picker/image_picker_util.dart'; import '../utils/file_size_calculator_util.dart'; class PhotoScanHandler { final classifier = PhotoClassifier(); var hasPermission = false.obs; var isClassifying = false.obs; var errorMessage = ''.obs; static var progress = Rxn(); StreamSubscription? _subscription; static RxBool isSimilarScanned = false.obs; static RxBool isPeopleScanned = false.obs; static RxBool isScreenShotScanned = false.obs; static RxBool isBlurryScanned = false.obs; // 人物图片 static RxList peoplePhotos = [].obs; // 地点图片 static Rx locationPhoto = Rx(null); // 截图照片 static Rx screenshotPhoto = Rx(null); // 模糊照片 static Rx blurryPhoto = Rx(null); // 相似照片 static RxList similarPhotos = [].obs; static Rx totalSpace = 0.0.obs; static Rx usedSpace = 0.0.obs; static Rx photoSpace = 0.0.obs; static Rx freeSpace = 0.0.obs; static Rx totalSpaceStr = "0.0 GB".obs; static Rx usedSpaceStr = "0.0 GB".obs; static Rx photoSpaceStr = "0.0 GB".obs; static Rx 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 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; Future getStorageInfo() async { 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 handleAllPhotos() async { var currentStatus = await Permission.photos.status; if (Platform.isAndroid) { currentStatus = await Permission.storage.status; } if (currentStatus.isGranted) { if (Platform.isAndroid) { await handleAndroidPhotos(); } // 已有完全权限,直接扫描 PhotoManager.clearFileCache(); startClassification(); } else if (currentStatus.isLimited) { // 已有有限权限,显示自定义弹窗 PhotoManager.clearFileCache(); startClassification(); } else { // 未授权,请求权限 var result = await Permission.photos.request(); if (Platform.isAndroid) { result = await Permission.storage.request(); } if (result.isGranted || result.isLimited) { PhotoManager.clearFileCache(); getStorageInfo(); startClassification(); if (Platform.isAndroid) { await handleAndroidPhotos(); } } else { isSimilarScanned.value = true; isPeopleScanned.value = true; isBlurryScanned.value = true; isScreenShotScanned.value = true; } } } /// Android平台处理方式 Future handleAndroidPhotos() async { final List result = await ImagePickerUtil.loadAssets(); ImagePickerUtil.peoplePhotos.value = result ?? []; ImagePickerUtil.locationPhotos['location'] = result ?? []; ImagePickerUtil.screenshotPhotos.value = result ?? []; ImagePickerUtil.similarPhotos.add(result ?? []); ImagePickerUtil.blurryPhotos.value = result ?? []; FileSizeCalculatorUtil.calculateTotalSize( assetIds: ImagePickerUtil.blurryPhotos.map((e) => e.id).toSet(), updateValue: (double totalSize) { ImagePickerUtil.blurrySize.value = totalSize.toInt()*1024; }); // result = await ImagePickerUtil.loadAssetsPaged(page: 1, pageSize: 9); // ImagePickerUtil.similarPhotos.add(result ?? []); // result = await ImagePickerUtil.loadAssetsPaged(page: 2, pageSize: 9); // ImagePickerUtil.similarPhotos.add(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 personPhoto in ImagePickerUtil.peoplePhotos) { peoplePhotos.add(personPhoto); 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; } } Future startClassification() async { isClassifying.value = true; errorMessage.value = ''; progress.value = null; ImagePickerUtil.similarResult.clear(); ImagePickerUtil.peopleResult.clear(); ImagePickerUtil.screenshotResult.clear(); ImagePickerUtil.blurryResult.clear(); try { await classifier.configureClassifier( batchSize: 200, maxConcurrentProcessing: 4, similarityThreshold: 0.75, ); _subscription = classifier.startClassificationStream().listen( (event) async { if (event == null) return; progress.value = event.progress; final result = event.result; if (result != null) { await ImagePickerUtil.loadClassifiedPhotoData(result); } if (event.progress?.isCompleted == true) { isClassifying.value = false; _subscription?.cancel(); _subscription = null; completeClassification(); } }, onError: (error) { errorMessage.value = '分类过程中出错: $error'; isClassifying.value = false; }, onDone: () { if (progress.value?.isCompleted != true) { errorMessage.value = '分类过程意外结束'; isClassifying.value = false; } }, ); } catch (e) { errorMessage.value = '启动分类失败: $e'; isClassifying.value = false; } } void cancelClassification() { _subscription?.cancel(); _subscription = null; isClassifying.value = false; classifier.resetClassifier(); } void resetAll() { progress.value = null; ImagePickerUtil.similarResult.clear(); ImagePickerUtil.peopleResult.clear(); ImagePickerUtil.screenshotResult.clear(); ImagePickerUtil.blurryResult.clear(); errorMessage.value = ''; isClassifying.value = false; } void completeClassification() { isSimilarScanned.value = true; isPeopleScanned.value = true; isBlurryScanned.value = true; isScreenShotScanned.value = true; } }