212 lines
6.1 KiB
Dart
212 lines
6.1 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
import 'package:yumi/main.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:yumi/shared/tools/sc_entrance_vap_svga_manager.dart';
|
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/floating/floating_game_screen_widget.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/floating/floating_gift_screen_widget.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/floating/floating_luck_gift_screen_widget.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/floating/floating_room_redenvelope_screen_widget.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/floating/floating_room_rocket_screen_widget.dart';
|
|
import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart';
|
|
|
|
typedef FloatingScreenManager = OverlayManager;
|
|
|
|
class OverlayManager {
|
|
final SCPriorityQueue<SCFloatingMessage> _messageQueue = SCPriorityQueue(
|
|
(a, b) => b.priority.compareTo(a.priority),
|
|
);
|
|
bool _isPlaying = false;
|
|
OverlayEntry? _currentOverlayEntry;
|
|
|
|
bool _isProcessing = false;
|
|
bool _isDisposed = false;
|
|
|
|
static final OverlayManager _instance =
|
|
OverlayManager._internal();
|
|
|
|
factory OverlayManager() => _instance;
|
|
|
|
OverlayManager._internal();
|
|
|
|
void addMessage(SCFloatingMessage message) {
|
|
if (_isDisposed) return;
|
|
if (SCGlobalConfig.isFloatingAnimationInGlobal) {
|
|
_messageQueue.add(message);
|
|
_safeScheduleNext();
|
|
} else {
|
|
_messageQueue.clear();
|
|
_isPlaying = false;
|
|
_isProcessing = false;
|
|
_isDisposed = false;
|
|
}
|
|
}
|
|
|
|
void _safeScheduleNext() {
|
|
if (_isProcessing || _isPlaying || _messageQueue.isEmpty) return;
|
|
_isProcessing = true;
|
|
|
|
try {
|
|
_scheduleNext();
|
|
} finally {
|
|
_isProcessing = false;
|
|
}
|
|
}
|
|
|
|
void _scheduleNext() {
|
|
if (_isPlaying || _messageQueue.isEmpty || _isDisposed) return;
|
|
|
|
try {
|
|
final context = navigatorKey.currentState?.context;
|
|
if (context == null || !context.mounted) return;
|
|
|
|
// 安全地获取第一个消息
|
|
if (_messageQueue.isEmpty) return;
|
|
final messageToProcess = _messageQueue.first;
|
|
|
|
if (messageToProcess?.type == 1) {
|
|
final rtcProvider = Provider.of<RealTimeCommunicationManager>(context, listen: false);
|
|
if (rtcProvider.currenRoom == null) {
|
|
// 从队列中移除第一个元素
|
|
_messageQueue.removeFirst();
|
|
_safeScheduleNext();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 安全地移除并播放消息
|
|
final messageToPlay = _messageQueue.removeFirst();
|
|
_playMessage(messageToPlay);
|
|
} catch (e) {
|
|
debugPrint('播放悬浮消息出错: $e');
|
|
_isPlaying = false;
|
|
_safeScheduleNext();
|
|
}
|
|
}
|
|
|
|
void _playMessage(SCFloatingMessage message) {
|
|
_isPlaying = true;
|
|
final context = navigatorKey.currentState?.context;
|
|
if (context == null || !context.mounted) {
|
|
_isPlaying = false;
|
|
_safeScheduleNext();
|
|
return;
|
|
}
|
|
|
|
_currentOverlayEntry = OverlayEntry(
|
|
builder:
|
|
(_) => Align(
|
|
alignment: AlignmentDirectional.topStart,
|
|
child: Transform.translate(
|
|
offset: Offset(0, 70.w),
|
|
child: _buildScreenWidget(message),
|
|
),
|
|
),
|
|
);
|
|
|
|
Overlay.of(context)?.insert(_currentOverlayEntry!);
|
|
}
|
|
|
|
Widget _buildScreenWidget(SCFloatingMessage message) {
|
|
bool completed = false;
|
|
|
|
void onComplete() {
|
|
if (completed) return;
|
|
completed = true;
|
|
|
|
try {
|
|
_currentOverlayEntry?.remove();
|
|
_currentOverlayEntry = null;
|
|
_isPlaying = false;
|
|
_safeScheduleNext();
|
|
} catch (e) {
|
|
debugPrint('清理悬浮消息出错: $e');
|
|
_isPlaying = false;
|
|
_safeScheduleNext();
|
|
}
|
|
}
|
|
|
|
switch (message.type) {
|
|
case 0:
|
|
return FloatingLuckGiftScreenWidget(
|
|
message: message,
|
|
onAnimationCompleted: onComplete,
|
|
);
|
|
case 1:
|
|
//房间礼物
|
|
return FloatingGiftScreenWidget(
|
|
message: message,
|
|
onAnimationCompleted: onComplete,
|
|
);
|
|
case 2:
|
|
//游戏中奖
|
|
return FloatingGameScreenWidget(
|
|
message: message,
|
|
onAnimationCompleted: onComplete,
|
|
);
|
|
case 3:
|
|
//火箭
|
|
return FloatingRoomRocketScreenWidget(
|
|
message: message,
|
|
onAnimationCompleted: onComplete,
|
|
);
|
|
case 4:
|
|
//红包
|
|
return FloatingRoomRedenvelopeScreenWidget(
|
|
message: message,
|
|
onAnimationCompleted: onComplete,
|
|
);
|
|
default:
|
|
onComplete();
|
|
return Container();
|
|
}
|
|
}
|
|
|
|
void activate() {
|
|
_isDisposed = false;
|
|
}
|
|
|
|
void dispose() {
|
|
_isDisposed = true;
|
|
_currentOverlayEntry?.remove();
|
|
_currentOverlayEntry = null;
|
|
_messageQueue.clear();
|
|
_isPlaying = false;
|
|
_isProcessing = false;
|
|
}
|
|
|
|
void removeRoom() {
|
|
// 正确地从 PriorityQueue 中移除特定类型的消息
|
|
_removeMessagesByType(1);
|
|
}
|
|
|
|
// 辅助方法:移除特定类型的消息
|
|
void _removeMessagesByType(int type) {
|
|
// 由于 PriorityQueue 没有直接的方法来移除特定元素,
|
|
// 我们需要重建队列,排除指定类型的消息
|
|
final newQueue = SCPriorityQueue<SCFloatingMessage>(
|
|
(a, b) => b.priority.compareTo(a.priority),
|
|
);
|
|
|
|
// 遍历原始队列,只添加非指定类型的消息
|
|
while (_messageQueue.isNotEmpty) {
|
|
final message = _messageQueue.removeFirst();
|
|
if (message.type != type) {
|
|
newQueue.add(message);
|
|
}
|
|
}
|
|
|
|
// 将新队列赋值给原队列
|
|
while (newQueue.isNotEmpty) {
|
|
_messageQueue.add(newQueue.removeFirst());
|
|
}
|
|
}
|
|
|
|
// 辅助方法:获取队列信息
|
|
int get queueLength => _messageQueue.length;
|
|
|
|
bool get isPlaying => _isPlaying;
|
|
}
|