348 lines
11 KiB
Dart
348 lines
11 KiB
Dart
import 'dart:async';
|
||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||
import '../../../core/utils/logger.dart';
|
||
|
||
/// 分享接收数据源 - 处理系统分享接收功能
|
||
/// 负责接收来自其他应用的图片分享,支持单张和多张图片
|
||
/// 优化批量处理和性能,支持大量图片分享场景
|
||
class ShareIntentDataSource {
|
||
/// 分享接收流控制器 - 管理分享数据的异步流
|
||
final StreamController<List<SharedMediaFile>> _sharingStreamController =
|
||
StreamController<List<SharedMediaFile>>.broadcast();
|
||
|
||
/// 当前接收到的分享文件列表 - 临时存储接收到的文件
|
||
List<SharedMediaFile> _currentSharedFiles = [];
|
||
|
||
/// 分享接收流 - 外部订阅分享事件
|
||
Stream<List<SharedMediaFile>> get sharingStream => _sharingStreamController.stream;
|
||
|
||
/// 是否正在处理分享 - 防止重复处理
|
||
bool _isProcessingShare = false;
|
||
|
||
/// 批量处理配置 - 控制批量处理的参数
|
||
static const int _maxBatchSize = 20; // 最大批量处理数量
|
||
static const Duration _batchProcessingDelay = Duration(milliseconds: 100); // 批量处理延迟
|
||
|
||
/// 分享文件队列 - 用于批量处理分享文件
|
||
final List<SharedMediaFile> _shareQueue = [];
|
||
|
||
/// 批量处理定时器 - 控制批量处理的时机
|
||
Timer? _batchProcessingTimer;
|
||
|
||
|
||
/// 初始化分享接收 - 设置分享监听器
|
||
/// 在应用启动时调用,监听系统分享事件
|
||
Future<void> initShareReceiving() async {
|
||
try {
|
||
Logger.info('初始化分享接收功能...');
|
||
|
||
// 监听前台分享接收(应用运行时)
|
||
ReceiveSharingIntent.instance.getMediaStream().listen(
|
||
_handleSharedMedia,
|
||
onError: _handleShareError,
|
||
);
|
||
|
||
// 获取初始分享数据(应用从分享启动时)
|
||
final initialSharedMedia = await ReceiveSharingIntent.instance.getInitialMedia();
|
||
if (initialSharedMedia.isNotEmpty) {
|
||
Logger.info('检测到应用启动时的分享数据: ${initialSharedMedia.length} 个文件');
|
||
_handleSharedMedia(initialSharedMedia);
|
||
}
|
||
|
||
Logger.info('分享接收功能初始化完成');
|
||
} catch (e) {
|
||
Logger.error('分享接收初始化失败', error: e);
|
||
throw ShareIntentException('初始化分享接收功能失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 处理接收到的媒体文件 - 验证并处理分享的图片文件
|
||
/// 支持多张图片的批量处理,优化性能和用户体验
|
||
/// [sharedFiles] 接收到的分享文件列表
|
||
void _handleSharedMedia(List<SharedMediaFile> sharedFiles) {
|
||
if (_isProcessingShare) {
|
||
Logger.warning('正在处理其他分享,将新文件加入队列');
|
||
_addToShareQueue(sharedFiles);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
_isProcessingShare = true;
|
||
Logger.info('接收到分享文件: ${sharedFiles.length} 个');
|
||
|
||
// 大量文件分批处理,避免UI卡顿
|
||
if (sharedFiles.length > _maxBatchSize) {
|
||
Logger.info('文件数量超过$_maxBatchSize个,启用分批处理');
|
||
_processShareFilesInBatches(sharedFiles);
|
||
return;
|
||
}
|
||
|
||
// 普通数量的文件直接处理
|
||
_processShareFiles(sharedFiles);
|
||
|
||
} catch (e) {
|
||
Logger.error('处理分享文件失败', error: e);
|
||
_sharingStreamController.addError(
|
||
ShareIntentException('处理分享文件失败: $e'),
|
||
);
|
||
} finally {
|
||
_isProcessingShare = false;
|
||
// 处理队列中的剩余文件
|
||
_processShareQueue();
|
||
}
|
||
}
|
||
|
||
/// 处理分享文件 - 处理普通数量的分享文件
|
||
/// [sharedFiles] 要处理的分享文件列表
|
||
void _processShareFiles(List<SharedMediaFile> sharedFiles) {
|
||
// 验证分享文件
|
||
final validFiles = _validateSharedFiles(sharedFiles);
|
||
if (validFiles.isEmpty) {
|
||
Logger.warning('没有有效的图片文件');
|
||
_sharingStreamController.addError(
|
||
ShareIntentException('未找到有效的图片文件'),
|
||
);
|
||
return;
|
||
}
|
||
|
||
// 更新当前分享文件
|
||
_currentSharedFiles = validFiles;
|
||
|
||
// 记录详细的文件信息
|
||
Logger.info('处理文件详情:');
|
||
Logger.info(' 有效文件数: ${validFiles.length}');
|
||
Logger.info(' 总文件数: ${sharedFiles.length}');
|
||
Logger.info(' 跳过的文件数: ${sharedFiles.length - validFiles.length}');
|
||
|
||
for (final file in validFiles) {
|
||
Logger.info('分享文件:');
|
||
Logger.info(' 路径: ${file.path}');
|
||
Logger.info(' 类型: ${file.type}');
|
||
if (file.thumbnail != null) {
|
||
Logger.info(' 缩略图: ${file.thumbnail}');
|
||
}
|
||
if (file.duration != null) {
|
||
Logger.info(' 持续时间: ${file.duration}');
|
||
}
|
||
}
|
||
|
||
// 通知监听器新的分享数据
|
||
_sharingStreamController.add(validFiles);
|
||
Logger.info('分享文件处理完成,已通知监听器');
|
||
}
|
||
|
||
/// 分批处理分享文件 - 处理大量分享文件
|
||
/// [sharedFiles] 要分批处理的分享文件列表
|
||
void _processShareFilesInBatches(List<SharedMediaFile> sharedFiles) {
|
||
Logger.info('开始分批处理${sharedFiles.length}个分享文件');
|
||
|
||
final batches = _createBatches(sharedFiles, _maxBatchSize);
|
||
Logger.info('创建${batches.length}个批次,每批最多$_maxBatchSize个文件');
|
||
|
||
// 处理第一批文件(立即处理)
|
||
if (batches.isNotEmpty) {
|
||
_processShareFiles(batches.first);
|
||
}
|
||
|
||
// 将其余批次加入队列,稍后处理
|
||
if (batches.length > 1) {
|
||
for (int i = 1; i < batches.length; i++) {
|
||
_addToShareQueue(batches[i]);
|
||
}
|
||
Logger.info('已将${batches.length - 1}个批次加入处理队列');
|
||
}
|
||
}
|
||
|
||
/// 创建文件批次 - 将文件列表分成指定大小的批次
|
||
/// [files] 文件列表
|
||
/// [batchSize] 每批的大小
|
||
/// 返回批次列表
|
||
List<List<SharedMediaFile>> _createBatches(List<SharedMediaFile> files, int batchSize) {
|
||
final batches = <List<SharedMediaFile>>[];
|
||
|
||
for (int i = 0; i < files.length; i += batchSize) {
|
||
final end = (i + batchSize < files.length) ? i + batchSize : files.length;
|
||
batches.add(files.sublist(i, end));
|
||
}
|
||
|
||
return batches;
|
||
}
|
||
|
||
/// 添加文件到分享队列 - 将文件加入待处理队列
|
||
/// [files] 要加入队列的文件列表
|
||
void _addToShareQueue(List<SharedMediaFile> files) {
|
||
Logger.info('添加${files.length}个文件到分享队列');
|
||
_shareQueue.addAll(files);
|
||
|
||
// 设置批量处理定时器
|
||
_scheduleBatchProcessing();
|
||
}
|
||
|
||
/// 调度批量处理 - 设置定时器处理队列中的文件
|
||
void _scheduleBatchProcessing() {
|
||
// 取消现有的定时器
|
||
_batchProcessingTimer?.cancel();
|
||
|
||
// 设置新的定时器
|
||
_batchProcessingTimer = Timer(_batchProcessingDelay, () {
|
||
_processShareQueue();
|
||
});
|
||
|
||
Logger.info('已调度批量处理定时器');
|
||
}
|
||
|
||
/// 处理分享队列 - 处理队列中的待处理文件
|
||
void _processShareQueue() {
|
||
if (_shareQueue.isEmpty) {
|
||
Logger.debug('分享队列为空,无需处理');
|
||
return;
|
||
}
|
||
|
||
if (_isProcessingShare) {
|
||
Logger.debug('正在处理其他分享,稍后重试');
|
||
_scheduleBatchProcessing(); // 重新调度
|
||
return;
|
||
}
|
||
|
||
try {
|
||
Logger.info('开始处理分享队列,剩余${_shareQueue.length}个文件');
|
||
|
||
// 从队列中取出最多maxBatchSize个文件
|
||
final batchSize = _shareQueue.length > _maxBatchSize ? _maxBatchSize : _shareQueue.length;
|
||
final batch = _shareQueue.sublist(0, batchSize);
|
||
_shareQueue.removeRange(0, batchSize);
|
||
|
||
// 处理这批文件
|
||
_processShareFiles(batch);
|
||
|
||
// 如果队列中还有文件,继续调度处理
|
||
if (_shareQueue.isNotEmpty) {
|
||
Logger.info('队列中还有${_shareQueue.length}个文件,继续调度');
|
||
_scheduleBatchProcessing();
|
||
}
|
||
|
||
} catch (e) {
|
||
Logger.error('处理分享队列失败', error: e);
|
||
}
|
||
}
|
||
|
||
|
||
/// 验证分享文件 - 过滤有效的图片文件
|
||
/// [files] 所有接收到的文件
|
||
/// 返回有效的图片文件列表
|
||
List<SharedMediaFile> _validateSharedFiles(List<SharedMediaFile> files) {
|
||
final validFiles = <SharedMediaFile>[];
|
||
|
||
for (final file in files) {
|
||
try {
|
||
// 检查文件是否存在
|
||
if (!file.path.startsWith('/')) {
|
||
Logger.warning('文件路径无效: ${file.path}');
|
||
continue;
|
||
}
|
||
|
||
// 检查文件类型(只处理图片)
|
||
if (file.type != SharedMediaType.image) {
|
||
Logger.warning('跳过非图片文件: ${file.type}');
|
||
continue;
|
||
}
|
||
|
||
// 检查文件扩展名
|
||
final path = file.path.toLowerCase();
|
||
final validExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.heic', '.heif'];
|
||
final hasValidExtension = validExtensions.any((ext) => path.endsWith(ext));
|
||
|
||
if (!hasValidExtension) {
|
||
Logger.warning('文件扩展名不支持: ${file.path}');
|
||
continue;
|
||
}
|
||
|
||
validFiles.add(file);
|
||
Logger.info('验证通过的文件: ${file.path}');
|
||
} catch (e) {
|
||
Logger.error('验证文件失败: ${file.path}', error: e);
|
||
}
|
||
}
|
||
|
||
return validFiles;
|
||
}
|
||
|
||
/// 处理分享错误 - 统一处理分享过程中的错误
|
||
/// [error] 分享错误信息
|
||
void _handleShareError(dynamic error) {
|
||
Logger.error('分享接收错误', error: error);
|
||
_sharingStreamController.addError(
|
||
ShareIntentException('接收分享失败: $error'),
|
||
);
|
||
}
|
||
|
||
/// 获取当前分享文件 - 获取最近接收到的分享文件
|
||
/// 返回当前分享文件列表,可能为空
|
||
List<SharedMediaFile> getCurrentSharedFiles() {
|
||
return List.from(_currentSharedFiles);
|
||
}
|
||
|
||
/// 清除当前分享数据 - 清理已处理的分享文件
|
||
/// 通常在分享处理完成后调用
|
||
void clearCurrentShare() {
|
||
Logger.info('清除当前分享数据');
|
||
_currentSharedFiles.clear();
|
||
}
|
||
|
||
/// 重置分享接收状态 - 重置所有分享相关状态
|
||
/// 在错误恢复或重新初始化时调用
|
||
void reset() {
|
||
Logger.info('重置分享接收状态');
|
||
_currentSharedFiles.clear();
|
||
_isProcessingShare = false;
|
||
}
|
||
|
||
/// 检查是否有待处理的分享 - 判断是否有未处理的分享文件
|
||
/// 返回是否有待处理的分享
|
||
bool hasPendingShare() {
|
||
return _currentSharedFiles.isNotEmpty;
|
||
}
|
||
|
||
/// 获取分享文件数量 - 获取当前分享文件的数量
|
||
/// 返回分享文件数量
|
||
int getShareFileCount() {
|
||
return _currentSharedFiles.length;
|
||
}
|
||
|
||
/// 释放资源 - 清理分享接收相关资源
|
||
/// 在应用退出时调用
|
||
Future<void> dispose() async {
|
||
Logger.info('释放分享接收资源');
|
||
|
||
// 取消批量处理定时器
|
||
_batchProcessingTimer?.cancel();
|
||
_batchProcessingTimer = null;
|
||
|
||
// 清空分享队列
|
||
_shareQueue.clear();
|
||
|
||
// 关闭分享流控制器
|
||
await _sharingStreamController.close();
|
||
|
||
// 清空当前分享文件
|
||
_currentSharedFiles.clear();
|
||
|
||
Logger.info('分享接收资源已释放');
|
||
}
|
||
}
|
||
|
||
/// 分享接收异常 - 分享接收过程中的自定义异常
|
||
class ShareIntentException implements Exception {
|
||
/// 异常消息
|
||
final String message;
|
||
|
||
/// 异常原因(可选)
|
||
final dynamic cause;
|
||
|
||
ShareIntentException(this.message, {this.cause});
|
||
|
||
@override
|
||
String toString() {
|
||
return cause != null ? '$message: $cause' : message;
|
||
}
|
||
} |