import 'dart:async'; import 'dart:io'; import 'package:path/path.dart' as path; import 'package:image/image.dart' as img; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import '../../domain/entities/inspiration_image.dart'; import '../../domain/repositories/share_repository.dart'; import '../datasources/share/share_intent_datasource.dart'; import '../../../core/utils/logger.dart'; import '../../../core/utils/image_utils.dart'; import '../../../core/utils/path_utils.dart'; /// 分享仓库实现类 - 实现分享功能的业务逻辑 /// 负责处理分享接收、图片保存和相关的业务逻辑 class ShareRepositoryImpl implements ShareRepository { /// 分享数据源 - 处理系统分享接收 final ShareIntentDataSource _shareDataSource; /// 图片工具类 - 处理图片压缩和格式转换(已使用静态方法) // final ImageUtils _imageUtils; // 静态类,不需要实例 /// 文件工具类 - 处理文件存储(已使用静态方法) // final FileUtils _fileUtils; // 静态类,不需要实例 /// 分享数据流订阅 - 管理分享数据流监听 StreamSubscription>? _shareSubscription; @override Stream>? get sharingStream => _shareDataSource.sharingStream; ShareRepositoryImpl({ required ShareIntentDataSource shareDataSource, // required ImageUtils imageUtils, // 静态类,不需要传入 // required FileUtils fileUtils, // 静态类,不需要传入 }) : _shareDataSource = shareDataSource; /// 初始化分享接收 - 设置分享监听器 /// 在应用启动时调用,开始监听系统分享事件 @override Future initializeShareReceiving() async { try { Logger.info('初始化分享仓库...'); // 初始化分享数据源 await _shareDataSource.initShareReceiving(); // 订阅分享数据流 _shareSubscription = _shareDataSource.sharingStream.listen( _handleSharedImages, onError: _handleShareError, ); Logger.info('分享仓库初始化完成'); } catch (e) { Logger.error('分享仓库初始化失败', error: e); throw Exception('初始化分享功能失败: $e'); } } /// 处理接收到的分享图片 - 验证并准备保存分享图片 /// [sharedFiles] 接收到的分享文件列表 Future _handleSharedImages(List sharedFiles) async { try { Logger.info('开始处理分享图片: ${sharedFiles.length} 张'); if (sharedFiles.isEmpty) { Logger.warning('分享文件列表为空'); return; } // 验证并处理每个分享文件 final processedImages = []; for (final file in sharedFiles) { try { final image = await _processSharedFile(file); if (image != null) { processedImages.add(image); } } catch (e) { Logger.error('处理分享文件失败: ${file.path}', error: e); // 继续处理其他文件,不中断整个流程 } } if (processedImages.isNotEmpty) { Logger.info('分享图片处理完成: ${processedImages.length} 张'); // 这里可以通知UI层有新的分享图片需要处理 // 例如通过事件总线或状态管理 } else { Logger.warning('没有成功处理的分享图片'); } } catch (e) { Logger.error('处理分享图片失败', error: e); throw Exception('处理分享图片失败: $e'); } } /// 处理单个分享文件 - 处理单个分享图片文件 /// [sharedFile] 分享文件对象 /// 返回处理后的灵感图片实体,失败时返回null Future _processSharedFile(SharedMediaFile sharedFile) async { try { Logger.info('处理分享文件: ${sharedFile.path}'); // 验证文件路径 final filePath = sharedFile.path; if (filePath.isEmpty || !filePath.startsWith('/')) { Logger.error('文件路径无效: $filePath'); return null; } // 验证文件是否存在 final file = File(filePath); if (!await file.exists()) { Logger.error('文件不存在: $filePath'); return null; } // 获取文件信息 final fileStat = await file.stat(); final fileSize = fileStat.size; if (fileSize == 0) { Logger.error('文件大小为0: $filePath'); return null; } // 获取文件扩展名和MIME类型 final fileExtension = path.extension(filePath).substring(1); // 移除点号 final mimeType = _getMimeType(fileExtension); Logger.info('文件信息 - 大小: $fileSize bytes, 类型: $mimeType, 扩展名: $fileExtension'); // 生成唯一ID和存储路径 final imageId = PathUtils.generateUniqueId(); final storagePath = await _getStoragePath(); final originalFileName = path.basename(filePath); // 创建目标文件路径 final targetFileName = '$imageId${path.extension(filePath)}'; final targetFilePath = path.join(storagePath, targetFileName); // 复制文件到应用存储目录 await file.copy(targetFilePath); Logger.info('文件已复制到: $targetFilePath'); // 生成缩略图 String? thumbnailPath; try { // 生成缩略图文件名 final thumbnailFileName = PathUtils.generateThumbnailFileName(targetFileName); final thumbnailPathDir = PathUtils.getParentDirectory(targetFilePath); final thumbnailFullPath = path.join(thumbnailPathDir, thumbnailFileName); thumbnailPath = await ImageUtils.generateThumbnail( imagePath: targetFilePath, targetPath: thumbnailFullPath, maxSize: 500, quality: 85, ); Logger.info('缩略图生成完成: $thumbnailPath'); } catch (e) { Logger.error('生成缩略图失败', error: e); // 缩略图失败不影响主流程 } // 获取图片尺寸信息 int? width; int? height; try { // 读取图片文件获取尺寸信息 final imageFile = File(targetFilePath); final imageBytes = await imageFile.readAsBytes(); // 使用image库解码图片获取尺寸 final decodedImage = img.decodeImage(imageBytes); if (decodedImage != null) { width = decodedImage.width; height = decodedImage.height; Logger.info('图片尺寸: ${width}x$height'); } else { Logger.warning('无法解码图片获取尺寸'); } } catch (e) { Logger.error('获取图片尺寸失败', error: e); // 尺寸获取失败不影响主流程 } // 创建灵感图片实体 final inspirationImage = InspirationImage( id: imageId, filePath: targetFilePath, thumbnailPath: thumbnailPath ?? targetFilePath, folderId: null, // 默认无文件夹 tags: const [], // 初始无标签 note: null, // 初始无备注 createdAt: DateTime.now(), updatedAt: DateTime.now(), originalName: originalFileName, fileSize: fileSize, mimeType: mimeType, width: width, height: height, isFavorite: false, ); Logger.info('分享文件处理完成: $imageId'); return inspirationImage; } catch (e) { Logger.error('处理分享文件失败: ${sharedFile.path}', error: e); return null; } } /// 获取存储路径 - 获取图片存储目录路径 /// 返回基于日期的存储路径 Future _getStoragePath() async { final storagePath = await PathUtils.buildImageStoragePath( fileName: 'temp', // 临时文件名,后面会替换 ); // 获取目录路径(去掉文件名) final dirPath = PathUtils.getParentDirectory(storagePath); // 确保目录存在 await Directory(dirPath).create(recursive: true); return dirPath; } /// 获取MIME类型 - 根据文件扩展名获取MIME类型 /// [extension] 文件扩展名(不含点) /// 返回对应的MIME类型 String _getMimeType(String extension) { final mimeTypes = { 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png', 'gif': 'image/gif', 'webp': 'image/webp', 'bmp': 'image/bmp', 'heic': 'image/heic', 'heif': 'image/heif', }; return mimeTypes[extension.toLowerCase()] ?? 'image/jpeg'; } /// 处理分享错误 - 统一处理分享过程中的错误 /// [error] 分享错误信息 void _handleShareError(dynamic error) { Logger.error('分享接收错误', error: error); // 这里可以通知UI层显示错误提示 // 例如通过事件总线或状态管理 } /// 获取待处理的分享文件 - 获取当前待处理的分享文件 /// 返回待处理的分享文件列表 @override List getPendingShareFiles() { return _shareDataSource.getCurrentSharedFiles(); } /// 是否有待处理的分享 - 检查是否有待处理的分享文件 /// 返回是否有待处理文件 @override bool hasPendingShare() { return _shareDataSource.hasPendingShare(); } /// 清除当前分享数据 - 清除已处理的分享数据 /// 在分享处理完成后调用 @override void clearCurrentShare() { _shareDataSource.clearCurrentShare(); } /// 获取分享文件数量 - 获取当前分享文件数量 /// 返回分享文件数量 @override int getShareFileCount() { return _shareDataSource.getShareFileCount(); } /// 释放资源 - 清理分享接收相关资源 /// 在应用退出时调用 @override Future dispose() async { Logger.info('释放分享仓库资源'); // 取消分享数据流订阅 await _shareSubscription?.cancel(); _shareSubscription = null; // 释放分享数据源 await _shareDataSource.dispose(); } }