chatapp3-flutter/lib/shared/tools/sc_gift_vap_svga_manager.dart
2026-04-09 21:32:23 +08:00

306 lines
7.4 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 'package:flutter/animation.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_svga/flutter_svga.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/shared/tools/sc_path_utils.dart';
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
import 'package:tancent_vap/utils/constant.dart';
import 'package:tancent_vap/widgets/vap_view.dart';
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
class SCGiftVapSvgaManager {
Map<String, MovieEntity> videoItemCache = {};
static SCGiftVapSvgaManager? _inst;
SCGiftVapSvgaManager._internal();
factory SCGiftVapSvgaManager() => _inst ??= SCGiftVapSvgaManager._internal();
final SCPriorityQueue<SCVapTask> _tq = SCPriorityQueue<SCVapTask>(
(a, b) => a.compareTo(b), // 调用 SCVapTask 的 compareTo 方法
);
VapController? _rgc;
SVGAAnimationController? _rsc;
bool _play = false;
bool _dis = false;
bool _pause = false;
//是否关闭礼物特效声音
bool _mute = false;
void setMute(bool muteMusic) {
_mute = muteMusic;
DataPersistence.setPlayGiftMusic(_mute);
}
bool getMute() {
return _mute;
}
// 绑定控制器
void bindVapCtrl(VapController vapController) {
_mute = DataPersistence.getPlayGiftMusic();
_dis = false;
_rgc = vapController;
_rgc?.setAnimListener(
onVideoStart: () {
},
onVideoComplete: () {
_hcs();
},
onFailed: (code, type, msg) {
_hcs();
},
);
}
void bindSvgaCtrl(SVGAAnimationController svgaController) {
_dis = false;
_rsc = svgaController;
_rsc?.addStatusListener((AnimationStatus status) {
if (status.isCompleted) {
_rsc?.reset();
_play = false;
_pn();
}
});
}
// 播放任务
void play(
String path, {
int priority = 0,
Map<String, VAPContent>? customResources,
int type = 0,
}) {
if (path.isEmpty) {
return;
}
if (SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim) {
if (_dis) return;
final task = SCVapTask(
path: path,
priority: priority,
customResources: customResources,
);
_tq.add(task);
if (!_play) {
_pn();
}
}
}
// 播放下一个任务
Future<void> _pn() async {
if (_pause) {
return;
}
if (_dis || _tq.isEmpty || _play) return;
final task = _tq.removeFirst();
_play = true;
try {
final pathType = SCPathUtils.getPathType(task.path);
if (pathType == PathType.asset) {
await _pa(task);
} else if (pathType == PathType.file) {
await _pf(task);
} else if (pathType == PathType.network) {
await _pnw(task);
}
} catch (e, s) {
print('VAP_SVGA播放失败: $e\n$s');
} finally {
// 确保状态正确重置
// if (!_dis) {
// _pn();
// }
}
}
// 播放资源文件
Future<void> _pa(SCVapTask task) async {
if (_dis) return;
if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
MovieEntity entity;
if (videoItemCache.containsKey(task.path)) {
entity = videoItemCache[task.path]!;
} else {
entity = await SVGAParser.shared.decodeFromAssets(task.path);
videoItemCache[task.path] = entity;
}
_rsc?.videoItem = entity;
_rsc?.reset();
_rsc?.forward();
} else {
if (task.customResources != null) {
task.customResources?.forEach((k, v) async {
await _rgc?.setVapTagContent(k, v);
});
}
await _rgc?.playAsset(task.path);
}
}
// 播放本地文件
Future<void> _pf(SCVapTask task) async {
if (_dis || _rgc == null) return;
await _rgc?.setMute(_mute);
if (task.customResources != null) {
task.customResources?.forEach((k, v) async {
await _rgc?.setVapTagContent(k, v);
});
}
await _rgc!.playFile(task.path);
}
// 播放网络资源
Future<void> _pnw(SCVapTask task) async {
if (_dis) return;
if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
MovieEntity? entity;
if (videoItemCache.containsKey(task.path)) {
entity = videoItemCache[task.path]!;
entity.autorelease = false;
} else {
try {
entity = await SVGAParser.shared.decodeFromURL(task.path);
entity.autorelease = false;
videoItemCache[task.path] = entity;
} catch (e) {
_play = false;
print('svga解析出错$e');
}
}
if (entity != null) {
_rsc?.videoItem = entity;
_rsc?.reset();
_rsc?.forward();
}
} else {
final file = await FileCacheManager.getInstance().getFile(url: task.path);
if (file != null && !_dis) {
await _pf(
SCVapTask(
path: file.path,
priority: task.priority,
customResources: task.customResources,
),
);
}
}
}
// 处理控制器状态变化
void _hcs() {
if (_rgc != null && !_dis) {
_play = false;
// 延迟一小段时间后播放下一个,避免状态冲突
Future.delayed(const Duration(milliseconds: 50), _pn);
}
}
//暂停动画播放
void pauseAnim() {
_pause = true;
}
//恢复动画播放
void resumeAnim() {
_pause = false;
_pn();
}
// 释放资源
void dispose() {
_dis = true;
_play = false;
_tq.clear();
_rgc?.stop();
_rgc?.dispose();
_rgc = null;
_rsc?.stop();
_rsc?.dispose();
_rsc = null;
}
// 清除所有任务
void clearTasks() {
_play = false;
_tq.clear();
_pause = false;
}
}
// 任务模型
// 1. 修改 SCVapTask 类,实现 Comparable
class SCVapTask implements Comparable<SCVapTask> {
final String path;
final int type;
final int priority;
final Map<String, VAPContent>? customResources;
final int _seq;
static int _nextSeq = 0;
SCVapTask({
required this.path,
this.priority = 0,
this.customResources,
this.type = 0,
}) : _seq = _nextSeq++;
@override
int compareTo(SCVapTask other) {
// 先按优先级降序排列
int priorityComparison = other.priority.compareTo(priority);
if (priorityComparison != 0) {
return priorityComparison;
}
// 相同优先级时,按序列号升序排列(先添加的先执行)
return _seq.compareTo(other._seq);
}
@override
String toString() {
return 'SCVapTask{path: $path, priority: $priority, seq: $_seq}';
}
}
// 优先队列实现
class SCPriorityQueue<E> {
final List<E> _els = [];
final Comparator<E> _cmp;
SCPriorityQueue(this._cmp);
void add(E element) {
_els.add(element);
_els.sort(_cmp);
}
E removeFirst() {
if (isEmpty) throw StateError("No elements");
return _els.removeAt(0);
}
E? get first => isEmpty ? null : _els.first;
bool get isEmpty => _els.isEmpty;
bool get isNotEmpty => _els.isNotEmpty;
int get length => _els.length;
void clear() => _els.clear();
List<E> get unorderedElements => List.from(_els);
// 实现 Iterable 接口
Iterator<E> get iterator => _els.iterator;
}