|
@@ -3,17 +3,33 @@ import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:classify_photo/classify_photo.dart';
|
|
import 'package:classify_photo/classify_photo.dart';
|
|
|
import 'package:clean/widget/multi_segment_circle_indicator.dart';
|
|
import 'package:clean/widget/multi_segment_circle_indicator.dart';
|
|
|
-import 'package:disk_space/disk_space.dart';
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
import 'package:flutter/Material.dart';
|
|
import 'package:flutter/Material.dart';
|
|
|
import 'package:get/get.dart';
|
|
import 'package:get/get.dart';
|
|
|
import 'package:permission_handler/permission_handler.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 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
|
|
|
|
|
|
|
import '../../module/image_picker/image_picker_util.dart';
|
|
import '../../module/image_picker/image_picker_util.dart';
|
|
|
-import '../utils/toast_util.dart';
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
class PhotoScanHandler {
|
|
class PhotoScanHandler {
|
|
|
- static final _photoClassify = ClassifyPhoto();
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ final classifier = PhotoClassifier();
|
|
|
|
|
+ var hasPermission = false.obs;
|
|
|
|
|
+ var isClassifying = false.obs;
|
|
|
|
|
+ var errorMessage = ''.obs;
|
|
|
|
|
+
|
|
|
|
|
+ static var progress = Rxn<ClassificationProgress>();
|
|
|
|
|
+ static var similarResult = <ClassifiedImageGroup>[].obs;
|
|
|
|
|
+ static var peopleResult = <ClassifiedImage>[].obs;
|
|
|
|
|
+ static var screenshotResult = <ClassifiedImage>[].obs;
|
|
|
|
|
+ static var blurryResult = <ClassifiedImage>[].obs;
|
|
|
|
|
+
|
|
|
|
|
+ StreamSubscription<ClassificationEvent?>? _subscription;
|
|
|
|
|
|
|
|
static RxBool isSimilarScanned = false.obs;
|
|
static RxBool isSimilarScanned = false.obs;
|
|
|
static RxBool isPeopleScanned = false.obs;
|
|
static RxBool isPeopleScanned = false.obs;
|
|
@@ -35,11 +51,6 @@ class PhotoScanHandler {
|
|
|
// 相似照片
|
|
// 相似照片
|
|
|
static RxList<AssetEntity> similarPhotos = <AssetEntity>[].obs;
|
|
static RxList<AssetEntity> similarPhotos = <AssetEntity>[].obs;
|
|
|
|
|
|
|
|
- // 添加一个标志来跟踪当前扫描状态
|
|
|
|
|
- bool _isScanningCancelled = false;
|
|
|
|
|
-
|
|
|
|
|
- List<StreamSubscription> _scanningTasks = [];
|
|
|
|
|
-
|
|
|
|
|
static Rx<double> totalSpace = 0.0.obs;
|
|
static Rx<double> totalSpace = 0.0.obs;
|
|
|
static Rx<double> usedSpace = 0.0.obs;
|
|
static Rx<double> usedSpace = 0.0.obs;
|
|
|
static Rx<double> photoSpace = 0.0.obs;
|
|
static Rx<double> photoSpace = 0.0.obs;
|
|
@@ -51,157 +62,30 @@ class PhotoScanHandler {
|
|
|
static Rx<String> freeSpaceStr = "0.0 GB".obs;
|
|
static Rx<String> freeSpaceStr = "0.0 GB".obs;
|
|
|
|
|
|
|
|
// 计算已用存储百分比
|
|
// 计算已用存储百分比
|
|
|
- static double get usedSpacePercentage => (usedSpace.value / totalSpace.value) * 100;
|
|
|
|
|
|
|
+ static double get usedSpacePercentage =>
|
|
|
|
|
+ (usedSpace.value / totalSpace.value) * 100;
|
|
|
|
|
|
|
|
// 计算照片占用存储百分比
|
|
// 计算照片占用存储百分比
|
|
|
static double get photoSpacePercentage =>
|
|
static double get photoSpacePercentage =>
|
|
|
(photoSpace.value / totalSpace.value) * 100;
|
|
(photoSpace.value / totalSpace.value) * 100;
|
|
|
|
|
|
|
|
// 计算可用存储百分比
|
|
// 计算可用存储百分比
|
|
|
- static double get freeSpacePercentage => (freeSpace.value / totalSpace.value) * 100;
|
|
|
|
|
|
|
+ static double get freeSpacePercentage =>
|
|
|
|
|
+ (freeSpace.value / totalSpace.value) * 100;
|
|
|
|
|
|
|
|
static List<PieData> get pieDataList => [
|
|
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)),
|
|
|
|
|
- ];
|
|
|
|
|
|
|
+ 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;
|
|
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 {
|
|
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();
|
|
final classifyPhoto = ClassifyPhoto();
|
|
|
|
|
|
|
@@ -244,420 +128,209 @@ class PhotoScanHandler {
|
|
|
|
|
|
|
|
/// 执行所有的照片处理操作
|
|
/// 执行所有的照片处理操作
|
|
|
Future<void> handleAllPhotos() async {
|
|
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) {
|
|
|
|
|
- // 已有有限权限,显示自定义弹窗
|
|
|
|
|
|
|
+ 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();
|
|
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;
|
|
|
|
|
|
|
+ getStorageInfo();
|
|
|
|
|
+ startClassification();
|
|
|
|
|
+
|
|
|
|
|
+ if (Platform.isAndroid) {
|
|
|
|
|
+ await handleAndroidPhotos();
|
|
|
}
|
|
}
|
|
|
|
|
+ } 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;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /// 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 ?? [];
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- print('开始获取截图照片');
|
|
|
|
|
- final photoClassify = ClassifyPhoto();
|
|
|
|
|
- final photos = await photoClassify.getScreenshots();
|
|
|
|
|
|
|
+ // result = await ImagePickerUtil.loadAssetsPaged(page: 1, pageSize: 9);
|
|
|
|
|
+ // ImagePickerUtil.similarPhotos.add(result ?? []);
|
|
|
|
|
+ // result = await ImagePickerUtil.loadAssetsPaged(page: 2, pageSize: 9);
|
|
|
|
|
+ // ImagePickerUtil.similarPhotos.add(result ?? []);
|
|
|
|
|
|
|
|
- if (_isScanningCancelled) {
|
|
|
|
|
- controller.add(false);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ print("handleAndroidPhotos $result");
|
|
|
|
|
+ print(
|
|
|
|
|
+ "ImagePickerUtil.peoplePhotos.value ${ImagePickerUtil.peoplePhotos.length}");
|
|
|
|
|
|
|
|
- print('获取截图照片完成: ${photos?.length ?? 0} 组照片');
|
|
|
|
|
- isScreenShotScanned.value = true;
|
|
|
|
|
|
|
+ isSimilarScanned.value = true;
|
|
|
|
|
+ isPeopleScanned.value = true;
|
|
|
|
|
+ isBlurryScanned.value = true;
|
|
|
|
|
+ isScreenShotScanned.value = true;
|
|
|
|
|
|
|
|
- if (photos != null) {
|
|
|
|
|
- await ImagePickerUtil.updatePhotos(photos);
|
|
|
|
|
- if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
|
|
|
|
|
- var asset = ImagePickerUtil.screenshotPhotos.first;
|
|
|
|
|
- screenshotPhoto.value = asset;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ 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;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- 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);
|
|
|
|
|
|
|
+ peoplePhotos.clear();
|
|
|
|
|
+ if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
|
|
|
|
|
+ for (var personPhotos in ImagePickerUtil.peoplePhotos) {
|
|
|
|
|
+ peoplePhotos.add(personPhotos);
|
|
|
|
|
+ if (peoplePhotos.length == 2) {
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- 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);
|
|
|
|
|
|
|
+ if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
|
|
|
|
|
+ var asset = ImagePickerUtil.blurryPhotos.first;
|
|
|
|
|
+ blurryPhoto.value = asset;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 处理人物照片
|
|
|
|
|
- peoplePhotos.clear();
|
|
|
|
|
- if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
|
|
|
|
|
- for (var personPhotos in ImagePickerUtil.peoplePhotos) {
|
|
|
|
|
- if (_isScanningCancelled) break;
|
|
|
|
|
|
|
+ if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
|
|
|
|
|
+ var asset = ImagePickerUtil.screenshotPhotos.first;
|
|
|
|
|
+ screenshotPhoto.value = asset;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- peoplePhotos.add(personPhotos);
|
|
|
|
|
- if (peoplePhotos.length == 2) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Future<void> startClassification() async {
|
|
|
|
|
+ isClassifying.value = true;
|
|
|
|
|
+ errorMessage.value = '';
|
|
|
|
|
+ progress.value = null;
|
|
|
|
|
+ similarResult.clear();
|
|
|
|
|
+ peopleResult.clear();
|
|
|
|
|
+ screenshotResult.clear();
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ similarResult.assignAll(result.similarGroups ?? []);
|
|
|
|
|
+ for (var group in similarResult) {
|
|
|
|
|
+ await ImagePickerUtil.newUpdatePhotos("similar", group.images);
|
|
|
}
|
|
}
|
|
|
|
|
+ peopleResult.assignAll (result.peopleImages ?? []);
|
|
|
|
|
+ await ImagePickerUtil.newUpdatePhotos("people", peopleResult);
|
|
|
|
|
+ screenshotResult.addAll(result.screenshotImages ?? []);
|
|
|
|
|
+ await ImagePickerUtil.newUpdatePhotos(
|
|
|
|
|
+ "screenshot", screenshotResult);
|
|
|
|
|
+ blurryResult.assignAll(result.blurryImages ?? []);
|
|
|
|
|
+ await ImagePickerUtil.newUpdatePhotos("blurry", blurryResult);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- 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;
|
|
|
|
|
|
|
+ if (event.progress?.isCompleted == true) {
|
|
|
|
|
+ isClassifying.value = false;
|
|
|
|
|
+ _subscription?.cancel();
|
|
|
|
|
+ _subscription = null;
|
|
|
|
|
+ completeClassification();
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- 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;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- controller.add(true);
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- print('获取相似照片失败: $e');
|
|
|
|
|
- controller.add(false);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- task();
|
|
|
|
|
|
|
+ void cancelClassification() {
|
|
|
|
|
+ _subscription?.cancel();
|
|
|
|
|
+ _subscription = null;
|
|
|
|
|
+ isClassifying.value = false;
|
|
|
|
|
+ classifier.resetClassifier();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return controller.stream.listen((success) {
|
|
|
|
|
- controller.close();
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ void resetAll() {
|
|
|
|
|
+ progress.value = null;
|
|
|
|
|
+ similarResult.clear();
|
|
|
|
|
+ peopleResult.clear();
|
|
|
|
|
+ screenshotResult.clear();
|
|
|
|
|
+ blurryResult.clear();
|
|
|
|
|
+ errorMessage.value = '';
|
|
|
|
|
+ isClassifying.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- /// 截图照片
|
|
|
|
|
- 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;
|
|
|
|
|
|
|
+ void completeClassification() {
|
|
|
|
|
+ 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;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- } 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;
|
|
|
|
|
|
|
+ if (ImagePickerUtil.peoplePhotos.isNotEmpty) {
|
|
|
|
|
+ for (var personPhotos in ImagePickerUtil.peoplePhotos) {
|
|
|
|
|
+ peoplePhotos.add(personPhotos);
|
|
|
|
|
+ if (peoplePhotos.length == 2) {
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
- } 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;
|
|
|
|
|
|
|
+ if (ImagePickerUtil.blurryPhotos.isNotEmpty) {
|
|
|
|
|
+ var asset = ImagePickerUtil.blurryPhotos.first;
|
|
|
|
|
+ blurryPhoto.value = asset;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// 相似照片
|
|
|
|
|
- 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;
|
|
|
|
|
|
|
+ if (ImagePickerUtil.screenshotPhotos.isNotEmpty) {
|
|
|
|
|
+ var asset = ImagePickerUtil.screenshotPhotos.first;
|
|
|
|
|
+ screenshotPhoto.value = asset;
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- /// 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;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+}
|