| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- 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<ClassificationProgress>();
- StreamSubscription<ClassificationEvent?>? _subscription;
- 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;
- 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;
- Future<void> 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<void> 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<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 ?? [];
- 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<void> 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;
- }
- }
|