import 'dart:async'; import 'package:clean/base/base_controller.dart'; import 'package:clean/base/base_photo_controller.dart'; import 'package:clean/data/bean/photos_type.dart'; import 'package:clean/data/consts/constants.dart'; import 'package:clean/data/repositories/user_repository.dart'; import 'package:clean/dialog/photo_delete_finish_dialog.dart'; import 'package:clean/dialog/photo_deleting_dialog.dart'; import 'package:clean/dialog/photo_preview_tip_dialog.dart'; import 'package:clean/module/image_picker/image_picker_util.dart'; import 'package:clean/module/locations_photo/locations_single_photo_controller.dart'; import 'package:clean/module/people_photo/people_photo_controller.dart'; import 'package:clean/module/people_photo/photo_group.dart'; import 'package:clean/module/photo_preview/phtoto_selected_preview_view.dart'; import 'package:clean/module/screenshots_blurry/screenshots_controller.dart'; import 'package:clean/module/similar_photo/similar_photo_controller.dart'; import 'package:clean/module/store/store_view.dart'; import 'package:clean/utils/file_utils.dart'; import 'package:clean/utils/toast_util.dart'; import 'package:flutter/Material.dart'; import 'package:flutter_card_swiper/flutter_card_swiper.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:lottie/lottie.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart'; import '../../data/consts/event_report_id.dart'; import '../../dialog/play_video_dialog.dart'; import '../../handler/event_handler.dart'; import '../../utils/file_size_calculator_util.dart'; class PhotoPreviewController extends BaseController with GetSingleTickerProviderStateMixin { Rx cardSwiperController = CardSwiperController().obs; final RxList listAssetEntity = [].obs; final RxSet selectedPhotosIds = {}.obs; final RxBool isSwiperEnd = false.obs; RxInt groupIndex = 0.obs; late PhotosType photosType; late String? currentImageId; RxDouble selectedFilesSize = 0.0.obs; RxInt selectedFileCount = 0.obs; late AnimationController animationController; RxBool animationIsComplete = false.obs; @override void onInit() { super.onInit(); animationController = AnimationController( vsync: this, duration: const Duration(seconds: 3), ); print('PhotoPreviewController onInit'); _getArgs(); // 获取传递的参数 _initData(); // 初始化数据 WidgetsBinding.instance.addPostFrameCallback((_) { if (currentImageId != null) { for (int i = 0; i < listAssetEntity.length; i++) { if (listAssetEntity[i].id == currentImageId) { print('photoGroups[i].id ${listAssetEntity[i].id},i $i'); groupIndex.value = i; cardSwiperController.value.moveTo(i); break; } } } }); animationController.addStatusListener((status) { if (status == AnimationStatus.forward) { // 延迟一秒 Future.delayed(Duration(seconds: 1), () { animationIsComplete.value = true; EventHandler.report(EventId.event_04008); }); } if (status == AnimationStatus.completed) { Future.delayed(Duration(seconds: 1), () { PhotoSelectedPreviewPage.start(photosType, selectedPhotosIds); }); } }); } @override void onReady() { // TODO: implement onReady super.onReady(); EventHandler.report(EventId.event_04002); print('PhotoPreviewController onReady'); if (isFirstOpenPhotoPreview()) { photoPreviewTipDialog(clickCallback: () { setFirstOpenPhotoPreview(false); EventHandler.report(EventId.event_04001); }); } } // 获取参数 void _getArgs() { photosType = parameters?['photosType']; currentImageId = parameters?['currentImageId']; } void _assignPhotosAndSelections(BasePhotoController controller) { listAssetEntity.assignAll(controller.getAllPhotos()); selectedPhotosIds.assignAll(controller.selectedPhotosIds); selectedFileCount.value = selectedPhotosIds.length; } void _initData() { listAssetEntity.clear(); selectedPhotosIds.clear(); switch (photosType) { case PhotosType.peoplePhotos: final PeoplePhotoController controller = Get.find(); _assignPhotosAndSelections(controller); break; case PhotosType.screenshots: final ScreenShotsController controller = Get.find(); _assignPhotosAndSelections(controller); break; case PhotosType.similarPhotos: final SimilarPhotoController controller = Get.find(); _assignPhotosAndSelections(controller); break; case PhotosType.locationPhotos: final LocationsSinglePhotoController controller = Get.find(); _assignPhotosAndSelections(controller); break; case PhotosType.blurryPhotos: final ScreenShotsController controller = Get.find(); _assignPhotosAndSelections(controller); break; } updateSelectedFilesSize(); } //删除成功清除选中的图片 void cleanSelections() async { debugPrint('PhotoPreviewController cleanSelections'); listAssetEntity .removeWhere((element) => selectedPhotosIds.contains(element.id)); selectedPhotosIds.clear(); if (listAssetEntity.isEmpty) { return; } isSwiperEnd.value = false; if (listAssetEntity.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { groupIndex.value = 0; cardSwiperController.value.moveTo(0); }); } updateSelectedFilesSize(); selectedFileCount.value = selectedPhotosIds.length; } // 恢复状态 void restoreSelections() { if (listAssetEntity.isEmpty) { clickBack(); return; } isSwiperEnd.value = false; if (listAssetEntity.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { groupIndex.value = 0; cardSwiperController.value.moveTo(0); }); } updateSelectedFilesSize(); selectedFileCount.value = selectedPhotosIds.length; } Future updateSelectedFilesSize() async { if (selectedPhotosIds.isEmpty) { selectedFilesSize.value = 0; return; } FileSizeCalculatorUtil.calculateTotalSize( assetIds: selectedPhotosIds, updateValue: (double totalSize) { selectedFilesSize.value = totalSize; }, ); PhotoManager.clearFileCache(); } void updateSelections(Set selectedIds) { print( 'PhotoPreviewController updateSelections photosType $photosType selectedIds $selectedIds'); selectedPhotosIds.assignAll(selectedIds); restoreSelections(); } void recoverSelectPhoto() { print('PhotoPreviewController recoverSelectPhoto'); cardSwiperController.value.undo(); } void clickSelect() { cardSwiperController.value.swipe(CardSwiperDirection.left); } @override void onClose() { print('PhotoPreviewController onClose'); animationController.dispose(); super.onClose(); isSwiperEnd.value = false; // 清理操作,释放资源 cardSwiperController.value.dispose(); // 清理缓存 PhotoManager.clearFileCache(); } void clickBack() { EventHandler.report(EventId.event_04003); _saveSelectedPhotos(photosType); Get.back(); } void clickJumpSelected() { debugPrint('PhotoPreviewController clickJumpSelected'); EventHandler.report(EventId.event_04005); PhotoSelectedPreviewPage.start(photosType, selectedPhotosIds); } // // 保存选择的图片ID列表 void _saveSelectedPhotos(PhotosType type) { switch (type) { case PhotosType.peoplePhotos: PeoplePhotoController controller = Get.find(); controller.updateSelections(selectedPhotosIds); controller.restoreSelections(); break; case PhotosType.screenshots: ScreenShotsController controller = Get.find(); controller.updateSelections(selectedPhotosIds); controller.restoreSelections(); break; case PhotosType.similarPhotos: SimilarPhotoController controller = Get.find(); controller.updateSelections(selectedPhotosIds); controller.restoreSelections(); break; case PhotosType.locationPhotos: LocationsSinglePhotoController controller = Get.find(); controller.updateSelections(selectedPhotosIds); controller.restoreSelections(); break; case PhotosType.blurryPhotos: ScreenShotsController controller = Get.find(); controller.updateSelections(selectedPhotosIds); controller.restoreSelections(); break; } } void clickUnselect() { print("clickUnselect"); cardSwiperController.value.swipe(CardSwiperDirection.right); } FutureOr onSwipe( int previousIndex, int? currentIndex, CardSwiperDirection direction, ) { print( 'onSwipe The card $previousIndex was swiped to the ${direction.name}. Now the card $currentIndex is on top', ); if (currentIndex != null) { groupIndex.value = currentIndex; } // 如果direction是left, if (direction == CardSwiperDirection.left) { // 先看看图片id是不是在selectedPhotosIds里面,如果在,不处理,如果不在,添加到selectedPhotosIds里面 if (!selectedPhotosIds.contains(listAssetEntity[previousIndex].id)) { print( 'add photoGroups[groupIndex.value].id ${listAssetEntity[previousIndex].id}'); selectedPhotosIds.add(listAssetEntity[previousIndex].id); } } else if (direction == CardSwiperDirection.right) { // 先看看图片id是不是在selectedPhotosIds里面,如果在,在selectedPhotosIds移除,不处理,如果不在,不处理 if (selectedPhotosIds.contains(listAssetEntity[previousIndex].id)) { print( 'remove photoGroups[groupIndex.value].id ${listAssetEntity[previousIndex].id}'); selectedPhotosIds.remove(listAssetEntity[previousIndex].id); } } selectedFileCount.value = selectedPhotosIds.length; updateSelectedFilesSize(); return true; } bool onSwiperUndo( int? previousIndex, int currentIndex, CardSwiperDirection direction, ) { print( 'onSwiperUndo The card $currentIndex was swiped back to the ${direction.name}. Now the card $previousIndex is on top'); groupIndex.value = currentIndex; // 撤销之前左滑的操作 if (direction == CardSwiperDirection.left) { print( 'photoGroups[groupIndex.value].id ${listAssetEntity[groupIndex.value].id}'); if (selectedPhotosIds.contains(listAssetEntity[groupIndex.value].id)) { selectedPhotosIds.remove(listAssetEntity[groupIndex.value].id); } } selectedFileCount.value = selectedPhotosIds.length; updateSelectedFilesSize(); return true; } onSwiperEnd() { isSwiperEnd.value = true; print('onSwiperEnd'); // 延迟500ms Future.delayed(Duration(milliseconds: 200), () { animationController.forward(from: 0); }); PhotoManager.clearFileCache(); // PhotoSelectedPreviewPage.start(photosType, selectedPhotosIds); } clickDelete() async { print('clickDelete'); if (selectedPhotosIds.isEmpty) { ToastUtil.show('Please select the picture'); return; } EventHandler.report(EventId.event_04004); if (userRepository.isVip()) { switch (photosType) { case PhotosType.peoplePhotos: ImagePickerUtil.selectedPeoplePhotosIds.assignAll(selectedPhotosIds); break; case PhotosType.screenshots: ImagePickerUtil.selectedScreenshotPhotosIds .assignAll(selectedPhotosIds); break; case PhotosType.similarPhotos: ImagePickerUtil.selectedSimilarPhotosIds.assignAll(selectedPhotosIds); break; case PhotosType.locationPhotos: ImagePickerUtil.selectedLocationPhotosIds .assignAll(selectedPhotosIds); break; case PhotosType.blurryPhotos: ImagePickerUtil.selectedBlurryPhotosIds.assignAll(selectedPhotosIds); break; } if (selectedPhotosIds.isNotEmpty) { photoDeletingDialog(); // 获取要删除的资产 final assetsToDelete = listAssetEntity .where( (asset) => selectedPhotosIds.contains(asset.id), ) .toList(); // 调用方法会返回被删除的资源,如果全部失败会返回空列表。 final List result = await PhotoManager.editor.deleteWithIds( assetsToDelete.map((e) => e.id).toList(), ); print('PhotoPreviewController result $result'); // 比较result和selectedPhotosIds,如果result和selectedPhotosIds相等,说明删除成功,走下面的逻辑 // 如果不相等,说明有删除失败的,走else逻辑 if (result.length == selectedPhotosIds.length) { switch (photosType) { case PhotosType.peoplePhotos: ImagePickerUtil.updatePhotoGroupDate( photosType, ImagePickerUtil.selectedPeoplePhotosIds); break; case PhotosType.screenshots: ImagePickerUtil.updatePhotoGroupDate( photosType, ImagePickerUtil.selectedScreenshotPhotosIds); break; case PhotosType.similarPhotos: ImagePickerUtil.updatePhotoGroupDate( photosType, ImagePickerUtil.selectedSimilarPhotosIds); break; case PhotosType.locationPhotos: ImagePickerUtil.updatePhotoGroupDate( photosType, ImagePickerUtil.selectedLocationPhotosIds); break; case PhotosType.blurryPhotos: ImagePickerUtil.updatePhotoGroupDate( photosType, ImagePickerUtil.selectedBlurryPhotosIds); break; } cleanSelections(); ToastUtil.show('Delete success'); Future.delayed(Duration(seconds: 2), () { SmartDialog.dismiss(tag: 'photoDeletingDialog'); photoDeleteFinishDialog(); }); } else { SmartDialog.dismiss(tag: 'photoDeletingDialog'); // 删除失败 ToastUtil.show("Delete failed"); } } } else { StorePage.start(); } // 清理缓存 PhotoManager.clearFileCache(); } void playVideo(AssetEntity asset) async { playVideoDialog(asset); } // 将selectedFilesSize转成String类型,然后单位转换,如果超过1MB,则转成MB,超过1GB,则转成GB,否则KB String get selectedFilesSizeString { final double sizeInKB = selectedFilesSize.value; if (sizeInKB >= 1024 * 1024) { // 先检查最大单位(GB) return "${(sizeInKB / (1024 * 1024)).toStringAsFixed(2)}GB"; } else if (sizeInKB >= 1024) { // 然后检查MB return "${(sizeInKB / 1024).toStringAsFixed(2)}MB"; } else { // 最后是KB return "${sizeInKB.toStringAsFixed(2)}KB"; } } }