snap_wish/lib/data/datasources/share/share_intent_datasource.dart
2025-09-17 13:32:25 +08:00

348 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}