import 'dart:async'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:fluro/fluro.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:aslan/app_localizations.dart'; import 'package:aslan/chatvibe_core/constants/at_room_msg_type.dart'; import 'package:aslan/chatvibe_core/utilities/at_heartbeat_utils.dart'; import 'package:aslan/chatvibe_core/utilities/at_permission_utils.dart'; import 'package:aslan/chatvibe_core/utilities/at_room_utils.dart'; import 'package:aslan/chatvibe_data/sources/local/user_manager.dart'; import 'package:aslan/chatvibe_data/sources/repositories/family_repository_impl.dart'; import 'package:aslan/chatvibe_data/sources/repositories/room_repository_imp.dart'; import 'package:aslan/chatvibe_managers/room_manager.dart'; import 'package:aslan/chatvibe_managers/rtm_manager.dart'; import 'package:aslan/chatvibe_managers/user_profile_manager.dart'; import 'package:aslan/chatvibe_ui/widgets/room/redpack/room_redenvelope_list_page.dart'; import 'package:aslan/chatvibe_ui/widgets/room/room_msg_item.dart'; import 'package:provider/provider.dart'; import 'package:aslan/chatvibe_ui/components/at_float_ichart.dart'; import 'package:aslan/chatvibe_core/constants/at_global_config.dart'; import 'package:aslan/chatvibe_core/routes/at_routes.dart'; import 'package:aslan/chatvibe_core/routes/at_fluro_navigator.dart'; import 'package:aslan/chatvibe_core/utilities/at_lk_dialog_util.dart'; import 'package:aslan/chatvibe_core/utilities/at_loading_manager.dart'; import 'package:aslan/chatvibe_core/utilities/at_gift_vap_svga_manager.dart'; import 'package:aslan/chatvibe_data/sources/repositories/user_repository_impl.dart'; import 'package:aslan/chatvibe_domain/models/res/family_base_info_res.dart'; import 'package:aslan/chatvibe_domain/models/res/game_ludo_res.dart'; import 'package:aslan/chatvibe_domain/models/res/join_room_res.dart'; import 'package:aslan/chatvibe_domain/models/res/login_res.dart'; import 'package:aslan/chatvibe_domain/models/res/mic_res.dart'; import 'package:aslan/chatvibe_domain/models/res/room_res.dart'; import 'package:aslan/chatvibe_features/room/voice_room_route.dart'; import 'package:aslan/chatvibe_ui/widgets/room/empty_mai_select.dart'; import 'package:aslan/chatvibe_ui/widgets/room/join_room_member_page.dart'; import 'package:aslan/chatvibe_ui/widgets/room/at_room_user_info_card.dart'; import 'package:aslan/chatvibe_managers/audio_manager.dart'; import '../chatvibe_data/models/enum/at_heartbeat_status.dart'; import '../chatvibe_data/models/enum/at_room_info_event_type.dart'; import '../chatvibe_data/models/enum/at_room_roles_type.dart'; import '../chatvibe_data/models/enum/at_vip_type.dart'; import '../chatvibe_data/sources/local/at_foreground_service_helper.dart'; import '../chatvibe_domain/models/res/at_get_list_game_config_res.dart'; import '../chatvibe_domain/models/res/at_is_follow_room_res.dart'; import '../chatvibe_domain/models/res/at_room_red_packet_list_res.dart'; import '../chatvibe_domain/models/res/at_room_rocket_status_res.dart'; import '../chatvibe_domain/models/res/at_room_theme_list_res.dart'; import '../chatvibe_features/gift/gift_page.dart'; import '../chatvibe_ui/components/at_tts.dart'; import '../chatvibe_ui/widgets/room/game/room_game_list_page.dart'; typedef OnSoundVoiceChange = Function(num index, int volum); typedef RtcProvider = RealTimeCommunicationManager; class RealTimeCommunicationManager extends ChangeNotifier { bool needUpDataUserInfo = false; ///当前所在房间 JoinRoomRes? currenRoom; /// 声音音量变化监听 List _onSoundVoiceChangeList = []; ///麦位 Map roomWheatMap = {}; final Set _remoteRtcUidsInChannel = {}; Timer? _roomStateResyncTimer; Timer? _pendingMicResyncTimer; bool _isRefreshingMicList = false; static const Duration _roomStateResyncInterval = Duration(seconds: 15); static const Duration _micChangeResyncDelay = Duration(milliseconds: 800); BigInt? _lastMicChangeTimeId; RtcEngine? engine; BuildContext? context; RtmProvider? rtmProvider; ATIsFollowRoomRes? isFollowRoomRes; ATRoomRocketStatusRes? roomRocketStatus; ChatVibeFamilyBaseInfoRes? familyBaseInfoRes; ///正在玩的ludo游戏 GameLudoRes? playingLudoGame; ///是否是最小化ludo游戏 bool isMinLudoGame = false; ///房间是否静音 bool roomIsMute = false; bool closeFullGame = false; ///禁音开关 默认关闭 bool isMic = true; ///在线用户列表 List onlineUsers = []; ///房间管理员 List managerUsers = []; ///音乐是否正在播放 bool isMusicPlaying = false; ///房间红包列表 List redPacketList = []; num roomTaskClaimableCount = 0; bool _isJoiningRoom = false; bool _isExitingRoom = false; int _roomSessionId = 0; Future? _exitRoomFuture; init(BuildContext context) { this.context = context; } int _nextRoomSessionId() { _roomSessionId++; return _roomSessionId; } bool _isRoomSessionActive(int roomSessionId, String roomId) { return !_isExitingRoom && _roomSessionId == roomSessionId && currenRoom?.roomProfile?.roomProfile?.id == roomId; } Future joinAgoraChannel({ required int roomSessionId, required String roomId, }) async { try { final RtcEngine rtcEngine = await _initAgoraRtcEngine(); if (!_isRoomSessionActive(roomSessionId, roomId)) { await _shutdownRtcEngine(rtcEngine); return; } engine = rtcEngine; _remoteRtcUidsInChannel.clear(); rtcEngine.setAudioProfile( profile: AudioProfileType.audioProfileIot, scenario: AudioScenarioType.audioScenarioMeeting, ); rtcEngine.enableAudioVolumeIndication( interval: 500, smooth: 3, reportVad: true, ); await rtcEngine.disableVideo(); await rtcEngine.setLocalPublishFallbackOption( StreamFallbackOptions.streamFallbackOptionAudioOnly, ); rtcEngine.registerEventHandler( RtcEngineEventHandler( onError: (ErrorCodeType err, String msg) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } print('rtc错误${err}'); }, onLocalAudioStateChanged: ( RtcConnection connection, LocalAudioStreamState state, LocalAudioStreamReason reason, ) {}, onAudioRoutingChanged: (routing) {}, onAudioMixingStateChanged: ( AudioMixingStateType state, AudioMixingReasonType reason, ) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } Provider.of( context!, listen: false, ).setPlayState(state, context!); switch (state) { case AudioMixingStateType.audioMixingStatePlaying: isMusicPlaying = true; break; case AudioMixingStateType.audioMixingStateStopped: case AudioMixingStateType.audioMixingStateFailed: isMusicPlaying = false; break; default: break; } }, onRemoteAudioStateChanged: ( RtcConnection connection, int remoteUid, RemoteAudioState state, RemoteAudioStateReason reason, int elapsed, ) { // print('用户 $remoteUid 音频状态: $state, 原因: $reason'); }, onJoinChannelSuccess: (RtcConnection connection, int elapsed) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } print('rtc 自己加入 ${connection.channelId} ${connection.localUid}'); }, onUserJoined: (connection, remoteUid, elapsed) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } print('rtc用户 $remoteUid 加入了频道'); _remoteRtcUidsInChannel.add(remoteUid); _applyRemoteAudioPolicy(); }, // 监听远端用户离开 onUserOffline: (connection, remoteUid, reason) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } print('rtc用户 $remoteUid 离开了频道 (原因: ${reason})'); _remoteRtcUidsInChannel.remove(remoteUid); _applyRemoteAudioPolicy(); }, onTokenPrivilegeWillExpire: ( RtcConnection connection, String token, ) async { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } var rtcToken = await AccountRepository().getRtcToken( roomId, AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); if (_isRoomSessionActive(roomSessionId, roomId) && identical(engine, rtcEngine)) { rtcEngine.renewToken(rtcToken.rtcToken ?? ""); } }, onAudioVolumeIndication: ( RtcConnection connection, List speakers, int speakerNumber, int totalVolume, ) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } initOnAudioVolumeIndication( connection, speakers, speakerNumber, totalVolume, ); }, ), ); var rtcToken = await AccountRepository().getRtcToken( roomId, AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); if (!_isRoomSessionActive(roomSessionId, roomId)) { if (identical(engine, rtcEngine)) { engine = null; } await _shutdownRtcEngine(rtcEngine); return; } await rtcEngine.joinChannel( token: rtcToken.rtcToken ?? "", channelId: roomId, uid: int.parse( AccountStorage().getCurrentUser()?.userProfile?.account ?? "0", ), options: ChannelMediaOptions( // 自动订阅所有视频流 autoSubscribeVideo: false, // 自动订阅所有音频流 autoSubscribeAudio: true, // 发布摄像头采集的视频 publishCameraTrack: false, // 发布麦克风采集的音频 publishMicrophoneTrack: true, // 设置用户角色为 clientRoleBroadcaster(主播)或 clientRoleAudience(观众) clientRoleType: ClientRoleType.clientRoleAudience, channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, ), ); if (!_isRoomSessionActive(roomSessionId, roomId)) { if (identical(engine, rtcEngine)) { engine = null; } await _shutdownRtcEngine(rtcEngine); return; } rtcEngine.muteAllRemoteAudioStreams(roomIsMute); await syncLocalPublishStateFromMicSnapshot(); await _applyRemoteAudioPolicy(); } catch (e) { if (_isRoomSessionActive(roomSessionId, roomId)) { ATTts.show("Join room fail"); extRoom(false); } print('加入失败:${e.runtimeType},${e.toString()}'); } } Future _initAgoraRtcEngine() async { RtcEngine? engine; while (engine == null) { engine = createAgoraRtcEngine(); await engine.initialize( RtcEngineContext(appId: ATGlobalConfig.agoraRtcAppid), ); } return engine; } int? _parseRtcUid(String? account) { if (account == null || account.isEmpty) { return null; } return int.tryParse(account); } Set _buildAllowedSpeakerUids() { final Set allowed = {}; for (final mic in roomWheatMap.values) { if (mic.user == null) { continue; } if (mic.micMute == true) { continue; } final int? uid = _parseRtcUid(mic.user?.account); if (uid != null) { allowed.add(uid); } } return allowed; } Future _applyRemoteAudioPolicy() async { if (engine == null) { return; } final Set allowedSpeakerUids = _buildAllowedSpeakerUids(); for (final int uid in _remoteRtcUidsInChannel) { final bool shouldMute = roomIsMute || !allowedSpeakerUids.contains(uid); try { await engine?.muteRemoteAudioStream(uid: uid, mute: shouldMute); } catch (_) { // 忽略单个用户静音失败,避免中断整体策略应用 } } } void _startRoomStateResyncTimer() { _stopRoomStateResyncTimer(); _roomStateResyncTimer = Timer.periodic(_roomStateResyncInterval, (_) { if (currenRoom == null) { return; } getMicList(); getOnlineUsers(); }); } void _stopRoomStateResyncTimer() { _roomStateResyncTimer?.cancel(); _roomStateResyncTimer = null; } void _scheduleMicStateResync() { _pendingMicResyncTimer?.cancel(); _pendingMicResyncTimer = Timer(_micChangeResyncDelay, () { if (currenRoom != null && !_isRefreshingMicList && !_isExitingRoom) { getMicList(); } }); } BigInt? _parseMicChangeTimeId(String? timeId) { if (timeId == null || timeId.isEmpty) { return null; } return BigInt.tryParse(timeId); } bool _shouldAcceptMicChange(String? timeId) { final BigInt? nextTimeId = _parseMicChangeTimeId(timeId); if (nextTimeId == null) { return true; } if (_lastMicChangeTimeId != null && nextTimeId < _lastMicChangeTimeId!) { return false; } _lastMicChangeTimeId = nextTimeId; return true; } MicRes _cloneMicWithUser(MicRes mic, ChatVibeUserProfile? user) { return MicRes( roomId: mic.roomId, micIndex: mic.micIndex, micLock: mic.micLock, micMute: mic.micMute, user: user, roomToken: mic.roomToken, emojiPath: mic.emojiPath, type: mic.type, number: mic.number, ); } List _normalizeMicSnapshot(List mics) { final Set seenUserIds = {}; final List normalized = []; for (final mic in mics) { final String? userId = mic.user?.id; if (userId != null && userId.isNotEmpty) { if (seenUserIds.contains(userId)) { normalized.add(_cloneMicWithUser(mic, null)); continue; } seenUserIds.add(userId); } normalized.add(mic); } return normalized; } Future _shutdownRtcEngine(RtcEngine currentEngine) async { try { await currentEngine.muteLocalAudioStream(true); } catch (_) {} try { await currentEngine.muteAllRemoteAudioStreams(true); } catch (_) {} try { await currentEngine.stopAudioMixing(); } catch (_) {} try { await currentEngine.setClientRole( role: ClientRoleType.clientRoleAudience, ); } catch (_) {} try { await currentEngine.leaveChannel(); } catch (_) {} try { await Future.delayed(Duration(milliseconds: 100)); await currentEngine.release(); } catch (_) {} } Future _disposeRtcEngine() async { final currentEngine = engine; engine = null; if (currentEngine == null) { _remoteRtcUidsInChannel.clear(); return; } await _shutdownRtcEngine(currentEngine); _remoteRtcUidsInChannel.clear(); } Future syncLocalPublishStateFromMicSnapshot() async { if (engine == null) { return; } final String? myUserId = AccountStorage().getCurrentUser()?.userProfile?.id; if (myUserId == null || myUserId.isEmpty) { return; } final num myMicIndex = userOnMaiInIndex(myUserId); final bool isOnMic = myMicIndex > -1; final bool isMicMutedByRoom = isOnMic ? (roomWheatMap[myMicIndex]?.micMute ?? true) : true; final bool shouldPublish = isOnMic && !isMicMutedByRoom && !isMic; final bool shouldKeepMusicPublishing = isOnMic && !isMicMutedByRoom && isMic && isMusicPlaying; if (shouldPublish) { await engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); adjustRecordingSignalVolume(100); await engine?.muteLocalAudioStream(false); ATHeartbeatUtils.scheduleAnchorHeartbeat( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); return; } if (shouldKeepMusicPublishing) { await engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); adjustRecordingSignalVolume(0); await engine?.muteLocalAudioStream(false); ATHeartbeatUtils.scheduleAnchorHeartbeat( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); return; } await engine?.setClientRole(role: ClientRoleType.clientRoleAudience); await engine?.muteLocalAudioStream(true); if (!isOnMic) { isMic = true; stopMusic(); } } void initOnAudioVolumeIndication( RtcConnection connection, List speakers, int speakerNumber, int totalVolume, ) { // if (user == null) { // throw "本地User尚未初始化"; // } // print('初始化声音监听器:${user.toJson()}'); ///声音全部清零 roomWheatMap.forEach((k, v) { roomWheatMap[k]!.setVolume = 0; }); for (var info in speakers) { num? uid = info.uid; int voloum = info.volume!; print('${info.uid}在说话---${voloum}'); if (voloum > 0) { ///是本地声音 if (uid == 0) { uid = num.parse( AccountStorage().getCurrentUser()!.userProfile!.account!, ); } ///在哪个麦 roomWheatMap.forEach((k, v) { if (v.user != null) { if (num.parse(v.user!.account!) == uid) { roomWheatMap[k]!.setVolume = voloum; for (var element in _onSoundVoiceChangeList) { element.call(k, voloum); } } } }); } } } ///关注/取消房间 void followRoom() async { var result = await AccountRepository().followRoom( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); if (isFollowRoomRes != null) { isFollowRoomRes = isFollowRoomRes!.copyWith( followRoom: !isFollowRoomRes!.followRoom!, ); } else { isFollowRoomRes = ATIsFollowRoomRes(followRoom: result); } SmartDialog.dismiss(tag: "unFollowDialog"); notifyListeners(); } int startTime = 0; void joinRoom( BuildContext context, String roomId, { String? pwd, bool clearRoomData = false, bool needOpenRedenvelope = false, String redPackId = "", bool openGameDialog = false, bool openGiftDialogToLuckyGift = false, bool openGiftDialogToGift = false, }) async { if (_isJoiningRoom || _isExitingRoom) { return; } _isJoiningRoom = true; int nextTime = DateTime.now().millisecondsSinceEpoch; if (nextTime - startTime < 1000) { //频繁点击 _isJoiningRoom = false; return; } startTime = nextTime; try { if (clearRoomData) { if (currenRoom != null || engine != null) { await extRoom(false, navigateHome: false); } else { _nextRoomSessionId(); _clearData(); } } bool hasPermission = await ATPermissionUtils.checkMicrophonePermission(); if (!hasPermission) { ATTts.show('Microphone permission is denied.'); return; } if (roomId == currenRoom?.roomProfile?.roomProfile?.id) { ///最小化进入房间,或者进入的是同一个房间 loadRoomInfo(currenRoom?.roomProfile?.roomProfile?.id ?? ""); Provider.of( context, listen: false, ).getMyUserInfo(); getMicList(); ATFloatIchart().remove(); ATNavigatorUtils.push( context, '${VoiceRoomRoute.voiceRoom}?id=$roomId', transition: TransitionType.custom, transitionDuration: kOpenRoomTransitionDuration, transitionBuilder: ( BuildContext context, Animation animation, Animation secondaryAnimation, Widget child, ) { return kOpenRoomTransitionBuilder( context, animation, secondaryAnimation, child, ); }, ); } else { ATFloatIchart().remove(); if (currenRoom != null) { // 切房时先退出当前房间,但不跳回 Home,避免 context 失效导致后续 join 偶发失败 await extRoom(false, navigateHome: false); } rtmProvider = Provider.of(context, listen: false); ATLoadingManager.exhibitOperation(context: context); currenRoom = await AccountRepository().entryRoom( roomId, pwd: pwd, redPackId: redPackId, needOpenRedenvelope: needOpenRedenvelope, ); final int roomSessionId = _nextRoomSessionId(); if (currenRoom?.entrants?.roles == ATRoomRolesType.TOURIST.name) { SmartDialog.show( tag: "showJoinRoomMember", alignment: Alignment.center, animationType: SmartAnimationType.fade, clickMaskDismiss: false, backType: SmartBackType.block, builder: (_) { return JoinRoomMemberPage( currenRoom?.roomProfile?.roomProfile?.id ?? "", () { ATLoadingManager.exhibitOperation(context: context); //加入会员 ChatRoomRepository() .changeRoomRole( roomId, ATRoomRolesType.ADMIN.name, AccountStorage().getCurrentUser()?.userProfile?.id ?? "", AccountStorage().getCurrentUser()?.userProfile?.id ?? "", "ONESELF_JOIN", ) .then( (result) async { currenRoom = currenRoom?.copyWith( entrants: currenRoom?.entrants?.copyWith( roles: ATRoomRolesType.MEMBER.name, ), ); await initRoon( roomSessionId: roomSessionId, needOpenRedenvelope: needOpenRedenvelope, redPackId: redPackId, ); ATLoadingManager.veilRoutine(); notifyListeners(); }, onError: (e) { currenRoom = null; ATLoadingManager.veilRoutine(); }, ); }, () { _nextRoomSessionId(); currenRoom = null; }, () async { await initRoon( roomSessionId: roomSessionId, needOpenRedenvelope: needOpenRedenvelope, redPackId: redPackId, ); ATLoadingManager.veilRoutine(); notifyListeners(); }, ); }, ); ATLoadingManager.veilRoutine(); } else { await initRoon( roomSessionId: roomSessionId, needOpenRedenvelope: needOpenRedenvelope, redPackId: redPackId, ); ATLoadingManager.veilRoutine(); notifyListeners(); } } } catch (e) { ATLoadingManager.veilRoutine(); print('joinRoom fail: $e'); } finally { _isJoiningRoom = false; } if (openGameDialog) { showBottomInBottomDialog(context, RoomGameListPage()); } if (openGiftDialogToLuckyGift) { SmartDialog.show( tag: "showGiftControl", alignment: Alignment.bottomCenter, maskColor: Colors.transparent, animationType: SmartAnimationType.fade, clickMaskDismiss: true, builder: (_) { return GiftPage(initialIndex: 1); }, ); } if (openGiftDialogToGift) { SmartDialog.show( tag: "showGiftControl", alignment: Alignment.bottomCenter, maskColor: Colors.transparent, animationType: SmartAnimationType.fade, clickMaskDismiss: true, builder: (_) { return GiftPage(); }, ); } } ///初始化房间相关 Future initRoon({ required int roomSessionId, bool needOpenRedenvelope = false, String? redPackId, }) async { final String roomId = currenRoom?.roomProfile?.roomProfile?.id ?? ""; if (roomId.isEmpty || !_isRoomSessionActive(roomSessionId, roomId)) { return; } if ((currenRoom?.roomProfile?.roomProfile?.event == ATRoomInfoEventType.WAITING_CONFIRMED.name || currenRoom?.roomProfile?.roomProfile?.event == ATRoomInfoEventType.ID_CHANGE.name) && currenRoom?.entrants?.roles == ATRoomRolesType.HOMEOWNER.name) { ///需要去修改房间信息,创建群聊 ATNavigatorUtils.push( context!, "${VoiceRoomRoute.roomEdit}?need=true", replace: false, ); return Future; } ATRoomUtils.roomAtGlobalConfig( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); if (currenRoom?.roomProfile?.roomProfile?.id != AccountStorage().getCurrentUser()?.userProfile?.id) { ChatRoomRepository() .isFollowRoom(currenRoom?.roomProfile?.roomProfile?.id ?? "") .then((value) { isFollowRoomRes = value; notifyListeners(); }); } ATNavigatorUtils.push(context!, VoiceRoomRoute.voiceRoom, replace: false); ///暂时让他每次都走一次创建群组,新的腾讯云IM数据迁移 await Provider.of(context!, listen: false).createRoomGroup( currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", currenRoom?.roomProfile?.roomProfile?.roomName ?? "", ); var joinResult = await rtmProvider?.joinRoomGroup( currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", "", ); if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } ChatRoomRepository() .rocketClaim(currenRoom?.roomProfile?.roomProfile?.id ?? "") .catchError((e) => false); rtmProvider?.addMsg( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", msg: ATAppLocalizations.of(context!)!.systemRoomTips, type: ATRoomMsgType.systemTips, ), ); rtmProvider?.addMsg( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", msg: currenRoom?.roomProfile?.roomProfile?.roomDesc, type: ATRoomMsgType.systemTips, ), ); ///获取麦位 getMicList(); Provider.of( context!, listen: false, ).contributionLevel(currenRoom?.roomProfile?.roomProfile?.id ?? ""); await joinAgoraChannel(roomSessionId: roomSessionId, roomId: roomId); if (_isRoomSessionActive(roomSessionId, roomId)) { _startRoomStateResyncTimer(); } else { return; } if (joinResult?.code == 0) { Msg joinMsg = Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", user: AccountStorage().getCurrentUser()?.userProfile, msg: "", role: currenRoom?.entrants?.roles ?? "", type: ATRoomMsgType.joinRoom, ); rtmProvider?.sendMsg(joinMsg, addLocal: true); if (ATGlobalConfig.isEntryVehicleAnimation) { if (AccountStorage().getCurrentUser()?.userProfile?.getMountains() != null) { Future.delayed(Duration(milliseconds: 500), () { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } ATGiftVapSvgaManager().play( AccountStorage() .getCurrentUser() ?.userProfile ?.getMountains() ?.sourceUrl ?? "", priority: 100, type: 1, ); }); } } if (rtmProvider?.msgUserJoinListener != null) { rtmProvider?.msgUserJoinListener!(joinMsg); } } ChatRoomRepository() .rocketStatus(currenRoom?.roomProfile?.roomProfile?.id ?? "") .then((res) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } roomRocketStatus = res; notifyListeners(); }) .catchError((e) {}); ATForegroundServiceHelper().startService(); loadRoomRedPacketList(1); if (needOpenRedenvelope) { openRedEnvelope(redPackId ?? ""); } GroupRepository() .familyUserBaseInfo(currenRoom?.roomProfile?.userProfile?.id ?? "") .then((res) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } familyBaseInfoRes = res; notifyListeners(); }); getRoomTaskClaimableCount(); getLudoGameInfo(); } ///更新房间火箭信息 void updateRoomRocketStatus(ATRoomRocketStatusRes res) { roomRocketStatus = res; notifyListeners(); } ///获取在线用户 Future getOnlineUsers() async { final int roomSessionId = _roomSessionId; final String roomId = currenRoom?.roomProfile?.roomProfile?.id ?? ""; if (roomId.isEmpty) { return; } final users = await ChatRoomRepository().roomOnlineUsers(roomId); if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } onlineUsers = users; managerUsers.clear(); for (var user in onlineUsers) { if (user.roles == ATRoomRolesType.ADMIN.name) { managerUsers.add(user); } } notifyListeners(); } Future getMicList() async { if (currenRoom == null || _isRefreshingMicList) { return; } final int roomSessionId = _roomSessionId; final String roomId = currenRoom!.roomProfile?.roomProfile?.id ?? ""; if (roomId.isEmpty) { return; } _isRefreshingMicList = true; bool isOnMic = false; try { final roomWheatList = await ChatRoomRepository().micList(roomId); if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } roomWheatMap.clear(); for (final roomWheat in _normalizeMicSnapshot(roomWheatList)) { roomWheatMap[roomWheat.micIndex!] = roomWheat; if (roomWheat.user != null && roomWheat.user!.id == AccountStorage().getCurrentUser()?.userProfile?.id) { isOnMic = true; } } notifyListeners(); await syncLocalPublishStateFromMicSnapshot(); await _applyRemoteAudioPolicy(); ATHeartbeatUtils.scheduleHeartbeat( ATHeartbeatStatus.VOICE_LIVE.name, isOnMic, roomId: roomId, ); Future.delayed(Duration(milliseconds: 1500), () { if (_isRoomSessionActive(roomSessionId, roomId)) { getOnlineUsers(); } }); } finally { _isRefreshingMicList = false; } } void getRoomTaskClaimableCount() { ChatRoomRepository() .roomTaskClaimableCount() .then((res) { roomTaskClaimableCount = res.claimableCount ?? 0; notifyListeners(); }) .catchError((e) {}); } Future> loadRoomRedPacketList( int current, ) async { var result = await ChatRoomRepository().roomRedPacketList( currenRoom?.roomProfile?.roomProfile?.id ?? "", current, ); if (current == 1) { redPacketList = result; notifyListeners(); } return result; } Future extRoom(bool isLogout, {bool navigateHome = true}) async { if (_isExitingRoom) { return _exitRoomFuture ?? Future.value(); } _nextRoomSessionId(); _isExitingRoom = true; final JoinRoomRes? leavingRoom = currenRoom; _exitRoomFuture = _extRoomInternal( leavingRoom: leavingRoom, isLogout: isLogout, navigateHome: navigateHome, ); try { await _exitRoomFuture; } finally { _isExitingRoom = false; _exitRoomFuture = null; } } Future _extRoomInternal({ required JoinRoomRes? leavingRoom, required bool isLogout, required bool navigateHome, }) async { try { _stopRoomStateResyncTimer(); _pendingMicResyncTimer?.cancel(); _pendingMicResyncTimer = null; rtmProvider?.msgAllListener = null; rtmProvider?.msgChatListener = null; rtmProvider?.msgGiftListener = null; ATLoadingManager.exhibitOperation(context: context); ATGiftVapSvgaManager().dispose(); ATHeartbeatUtils.scheduleHeartbeat(ATHeartbeatStatus.ONLINE.name, false); ATHeartbeatUtils.cancelAnchorTimer(); rtmProvider?.cleanRoomData(); playingLudoGame = null; await _disposeRtcEngine(); if (leavingRoom != null) { await rtmProvider?.quitGroup( leavingRoom.roomProfile?.roomProfile?.roomAccount ?? "", ); await AccountRepository().quitRoom( leavingRoom.roomProfile?.roomProfile?.id ?? "", ); } if (currenRoom == null || currenRoom?.roomProfile?.roomProfile?.id == leavingRoom?.roomProfile?.roomProfile?.id) { _clearData(); } ATLoadingManager.veilRoutine(); if (!isLogout && navigateHome) { ATNavigatorUtils.popUntil(context!, ModalRoute.withName(ATRoutes.home)); } } catch (e) { print('rtc退出房间出错: $e'); _clearData(); ATLoadingManager.veilRoutine(); if (!isLogout && navigateHome) { ATNavigatorUtils.popUntil(context!, ModalRoute.withName(ATRoutes.home)); } } } ///清空列表数据 void _clearData() { _stopRoomStateResyncTimer(); _pendingMicResyncTimer?.cancel(); _pendingMicResyncTimer = null; _remoteRtcUidsInChannel.clear(); _lastMicChangeTimeId = null; roomRocketStatus = null; familyBaseInfoRes = null; rtmProvider ?.onNewMessageListenerGroupMap["${currenRoom?.roomProfile?.roomProfile?.roomAccount}"] = null; roomWheatMap.clear(); onlineUsers.clear(); needUpDataUserInfo = false; ATRoomUtils.roomUsersMap.clear(); MsgItem.clearRoomUserCaches(); roomIsMute = false; playingLudoGame = null; isMinLudoGame = false; rtmProvider?.roomAllMsgList.clear(); rtmProvider?.roomChatMsgList.clear(); redPacketList.clear(); currenRoom = null; isMic = true; isMusicPlaying = false; engine = null; Provider.of(context!, listen: false).reset(); ATGiftVapSvgaManager().clearTasks(); ATRoomUtils.closeAllDialogs(); ATForegroundServiceHelper().stopService(); ATGlobalConfig.isEntryVehicleAnimation = true; ATGlobalConfig.isGiftSpecialEffects = true; ATGlobalConfig.isFloatingAnimationInGlobal = true; ATGlobalConfig.isLuckGiftSpecialEffects = true; } void muteAllRemote() { roomIsMute = !roomIsMute; engine?.muteAllRemoteAudioStreams(roomIsMute); _applyRemoteAudioPolicy(); notifyListeners(); } ///点击的位置 void clickSite(num index, {ChatVibeUserProfile? clickUser}) { if (index == -1) { if (clickUser != null) { if (clickUser.id == AccountStorage().getCurrentUser()?.userProfile?.id) { ///是自己,直接打开资料卡 showBottomInBottomDialog( context!, ATRoomUserInfoCard(userId: clickUser.id), ); } else { showBottomInBottomDialog( context!, EmptyMaiSelect(index: index, clickUser: clickUser), ); } } } else { ChatVibeUserProfile? roomWheatUser = roomWheatMap[index]?.user; showBottomInBottomDialog( context!, EmptyMaiSelect(index: index, clickUser: roomWheatUser), ); // if (roomWheatUser != null) { // ///麦上有人 // if (roomWheatUser.id == // AccountStorage().getCurrentUser()?.ChatVibeUserProfile?.id) { // ///是自己 // showBottomInBottomDialog( // context!, // EmptyMaiSelect(index: index, clickUser: roomWheatUser), // ); // } else { // showBottomInBottomDialog( // context!, // RoomUserInfoCard(userId: roomWheatUser.id), // ); // } // } else { // showBottomInBottomDialog( // context!, // EmptyMaiSelect(index: index, clickUser: roomWheatUser), // ); // } } } addSoundVoiceChangeListener(OnSoundVoiceChange onSoundVoiceChange) { _onSoundVoiceChangeList.add(onSoundVoiceChange); print('添加监听:${_onSoundVoiceChangeList.length}'); } removeSoundVoiceChangeListener(OnSoundVoiceChange onSoundVoiceChange) { _onSoundVoiceChangeList.remove(onSoundVoiceChange); print('删除监听:${_onSoundVoiceChangeList.length}'); } void shangMai(num index, {String? eventType, String? inviterId}) async { var myUser = AccountStorage().getCurrentUser()?.userProfile; if (AccountStorage().getVIP()?.name == ATVIPType.KING.name) { ///vip5无视所有规则 } else { if (currenRoom?.roomProfile?.roomSetting?.touristMike ?? false) { } else { if (isTourists()) { ATTts.show( ATAppLocalizations.of(context!)!.touristsAreNotAllowedToGoOnTheMic, ); return; } } } try { var micGoUpRes = await ChatRoomRepository().micGoUp( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, eventType: eventType, inviterId: inviterId, ); /// 设置成主播角色 engine?.renewToken(micGoUpRes.roomToken ?? ""); if (!micGoUpRes.micMute!) { if (!isMic) { engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); adjustRecordingSignalVolume(100); await engine?.muteLocalAudioStream(false); } } else { engine?.setClientRole(role: ClientRoleType.clientRoleAudience); await engine?.muteLocalAudioStream(true); } ATHeartbeatUtils.scheduleAnchorHeartbeat( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); final us = roomWheatMap[index]; if (us != null) { roomWheatMap[index] = us.copyWith( user: ChatVibeUserProfile( id: myUser?.id, account: myUser?.account, userAvatar: myUser?.userAvatar, userNickname: myUser?.userNickname, userSex: myUser?.userSex, ), ); } if ((roomWheatMap[index]?.micMute ?? false)) { ///房主上麦自动解禁麦位 if (isFz()) { jieJinMai(index); } } await syncLocalPublishStateFromMicSnapshot(); await _applyRemoteAudioPolicy(); notifyListeners(); } catch (ex) { ATTts.show('Failed to put on the microphone, $ex'); } } Future xiaMai(num index) async { try { await ChatRoomRepository().micGoDown( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, ); } finally { //自己 if (roomWheatMap[index]?.user?.id == UserManager().getCurrentUser()?.userProfile?.id) { isMic = true; } ATHeartbeatUtils.cancelAnchorTimer(); /// 设置成主持人角色 await engine?.renewToken(""); roomWheatMap[index]?.setUser = null; await syncLocalPublishStateFromMicSnapshot(); await _applyRemoteAudioPolicy(); notifyListeners(); } } ///踢人下麦 Future killXiaMai(String userId) async { try { for (final entry in roomWheatMap.entries.toList()) { final k = entry.key; final v = entry.value; if (v.user?.id == userId) { ///是否可以踢VIP有防踢 final canKill = await ChatRoomRepository().kickOffMicrophone( currenRoom?.roomProfile?.roomProfile?.id ?? "", userId, ); if (canKill) { await ChatRoomRepository().micKill( currenRoom?.roomProfile?.roomProfile?.id ?? "", k, ); Provider.of(context!, listen: false).sendMsg( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount, msg: userId, type: ATRoomMsgType.killXiaMai, ), addLocal: false, ); roomWheatMap[k]?.setUser = null; if (userId == AccountStorage().getCurrentUser()?.userProfile?.id) { isMic = true; } await syncLocalPublishStateFromMicSnapshot(); await _applyRemoteAudioPolicy(); notifyListeners(); } else { ATTts.show("cant kill the user"); } } } } catch (e) { ATTts.show("kill the user fail"); print('踢人下麦失败'); } } ///自己是否在麦上 bool isOnMai() { return roomWheatMap.values .map((userWheat) => userWheat.user?.id) .toList() .contains(AccountStorage().getCurrentUser()?.userProfile?.id); } ///自己是否在指定的麦上 bool isOnMaiInIndex(num index) { return roomWheatMap[index]?.user?.id == AccountStorage().getCurrentUser()?.userProfile?.id; } ///点击的用户在哪个麦上 num userOnMaiInIndex(String userId) { num index = -1; roomWheatMap.forEach((k, value) { if (value.user?.id == userId) { index = k; } }); return index; } ///座位上的用户是否开麦 bool userOnMaiInIndexOfMute(String userId) { bool micMute = false; roomWheatMap.forEach((k, value) { if (value.user?.id == userId) { value.micMute; } }); return micMute; } ///是否是房主 bool isFz() { return currenRoom?.entrants?.roles == ATRoomRolesType.HOMEOWNER.name; } ///是否是管理 bool isGL() { return currenRoom?.entrants?.roles == ATRoomRolesType.ADMIN.name; } ///是否是会员 bool isHY() { return currenRoom?.entrants?.roles == ATRoomRolesType.MEMBER.name; } ///是否是游客 bool isTourists() { return currenRoom?.entrants?.roles == ATRoomRolesType.TOURIST.name; } ///麦位变动 Future micChange(List? mics, {String? timeId}) async { if (mics == null || mics.isEmpty) { return; } if (currenRoom == null || _isExitingRoom) { return; } if (!_shouldAcceptMicChange(timeId)) { _scheduleMicStateResync(); return; } roomWheatMap.clear(); for (final mic in _normalizeMicSnapshot(mics)) { roomWheatMap[mic.micIndex!] = mic; } notifyListeners(); await syncLocalPublishStateFromMicSnapshot(); await _applyRemoteAudioPolicy(); _scheduleMicStateResync(); } ///找一个空麦位 -1表示没有空位 num findWheat() { for (var entry in roomWheatMap.entries) { if (entry.value.user == null && !entry.value.micLock!) { return entry.key; } } return -1; } void loadRoomInfo(String roomId) { final int roomSessionId = _roomSessionId; ChatRoomRepository().specific(roomId).then((value) { if (!_isRoomSessionActive(roomSessionId, roomId)) { return; } currenRoom = currenRoom?.copyWith( roomProfile: currenRoom?.roomProfile?.copyWith( roomCounter: value.counter, roomSetting: value.setting, roomProfile: currenRoom?.roomProfile?.roomProfile?.copyWith( roomCover: value.roomCover, roomName: value.roomName, roomDesc: value.roomDesc, ), ), ); ///如果是游客禁止上麦,已经在麦上的游客需要下麦 bool touristMike = currenRoom?.roomProfile?.roomSetting?.touristMike ?? false; if (!touristMike && isTourists()) { num index = userOnMaiInIndex( AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); if (index > -1) { xiaMai(index); } } notifyListeners(); }); } ///解锁麦位 void jieFeng(num index) async { await ChatRoomRepository().micLock( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, false, ); } ///锁麦 void fengMai(num index) async { await ChatRoomRepository().micLock( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, true, ); } ///静音麦克风 void jinMai(num index) async { await ChatRoomRepository().micMute( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, true, ); if (isOnMaiInIndex(index)) { Provider.of( context!, listen: false, ).engine?.setClientRole(role: ClientRoleType.clientRoleAudience); } var mic = roomWheatMap[index]; if (mic != null) { roomWheatMap[index] = mic.copyWith(micMute: true); notifyListeners(); } } ///解除静音麦克风 void jieJinMai(num index) async { await ChatRoomRepository().micMute( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, false, ); if (isOnMaiInIndex(index)) { if (!Provider.of(context!, listen: false).isMic) { if (roomWheatMap[index]?.user != null) { adjustRecordingSignalVolume(100); Provider.of( context!, listen: false, ).engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); } } } var mic = roomWheatMap[index]; if (mic != null) { roomWheatMap[index] = mic.copyWith(micMute: false); notifyListeners(); } } void addOnlineUser(String groupId, ChatVibeUserProfile user) { if (groupId != currenRoom?.roomProfile?.roomProfile?.roomAccount) { return; } bool isExtOnlineList = false; for (var us in onlineUsers) { if (us.id == user.id) { isExtOnlineList = true; break; } } if (!isExtOnlineList) { Provider.of(context!, listen: false).onlineUsers.add(user); notifyListeners(); } } void removOnlineUser(String groupId, String userId) { if (groupId != currenRoom?.roomProfile?.roomProfile?.roomAccount) { return; } ChatVibeUserProfile? isExtOnlineUser; for (var us in onlineUsers) { if (us.id == userId) { isExtOnlineUser = us; break; } } if (isExtOnlineUser != null) { Provider.of( context!, listen: false, ).onlineUsers.remove(isExtOnlineUser); notifyListeners(); } } void starPlayEmoji(Msg msg) { if (msg.number! > -1) { var mic = roomWheatMap[msg.number]; if (mic != null && mic.user != null) { roomWheatMap[msg.number!] = mic.copyWith( emojiPath: msg.msg, type: msg.type, number: msg.msg, ); notifyListeners(); } } } ///更新房间背景 void updateRoomBG(ATRoomThemeListRes res) { var roomTheme = RoomTheme( expireTime: res.expireTime, id: res.id, themeStatus: res.themeStatus, themeBack: res.themeBack, ); currenRoom?.roomProps?.setRoomTheme(roomTheme); notifyListeners(); } void updateRoomSetting(RoomSetting roomSetting) { currenRoom?.roomProfile?.setRoomSetting(roomSetting); notifyListeners(); } Map>> seatGlobalKeyMap = {}; void bindTargetKey(num index, GlobalKey> targetKey) { seatGlobalKeyMap[index] = targetKey; } GlobalKey>? getSeatGlobalKeyByIndex(String userId) { ///需要有人的座位 num index = userOnMaiInIndex(userId); if (index > -1) { return seatGlobalKeyMap[index]; } } ///播放音乐 void playMusic(String localPath, int cycle) { if (localPath.isEmpty) { return; } engine?.startAudioMixing( filePath: localPath, loopback: false, cycle: cycle, ); } ///停止播放音乐 void stopMusic() { Provider.of(context!, listen: false).reset(); engine?.stopAudioMixing(); } ///暂停播放音乐 void pauseMusic() { engine?.pauseAudioMixing(); } ///恢复播放音乐 void resumeMusic() { engine?.resumeAudioMixing(); } void adjustRecordingSignalVolume(int volume) { engine?.adjustRecordingSignalVolume(volume); } ///打开红包列表页面 void openRedEnvelope(String redPackId) { // if (redPackId.isEmpty) { // return; // } SmartDialog.dismiss(tag: "showRoomRedenvelopeList"); SmartDialog.show( tag: "showRoomRedenvelopeList", alignment: Alignment.center, animationType: SmartAnimationType.fade, builder: (_) { return RoomRedenvelopeListPage(); }, ); } void roomMannyGameClose() { playingLudoGame = null; notifyListeners(); } void gameLudoCreate(ATGetListGameConfigRes item) { if (playingLudoGame == null) { ChatRoomRepository() .gameLudoCreate( currenRoom?.roomProfile?.roomProfile?.id ?? "", 0, "1", currenRoom?.roomProfile?.roomProfile?.userId ?? "", item.id ?? "", ) .then((res) { playingLudoGame = res; Provider.of(context!, listen: false).sendMsg( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", msg: "${playingLudoGame?.gameInfo?.id}", type: ATRoomMsgType.roomGameCreate, ), addLocal: false, ); notifyListeners(); }); } } Future gameLudoDisband() async { if (playingLudoGame != null) { try { ATLoadingManager.exhibitOperation(); await ChatRoomRepository().gameLudoDisband( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); ATLoadingManager.veilRoutine(); playingLudoGame = null; Provider.of(context!, listen: false).sendMsg( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", msg: "${playingLudoGame?.gameInfo?.id}", type: ATRoomMsgType.roomGameClose, ), addLocal: false, ); notifyListeners(); } catch (_) { ATLoadingManager.veilRoutine(); } } } void getLudoGameInfo() { ChatRoomRepository() .gameLudo(currenRoom?.roomProfile?.roomProfile?.id ?? "") .then((res) { playingLudoGame = res; notifyListeners(); }); } void minOrShowLudoGame(bool ms) { isMinLudoGame = ms; notifyListeners(); } @override void dispose() { _stopRoomStateResyncTimer(); _pendingMicResyncTimer?.cancel(); _remoteRtcUidsInChannel.clear(); super.dispose(); } void openGameDialog() { showBottomInBottomDialog(context!, RoomGameListPage()); } void openGiftDialogToLuckyGift() { SmartDialog.show( tag: "showGiftControl", alignment: Alignment.bottomCenter, maskColor: Colors.transparent, animationType: SmartAnimationType.fade, clickMaskDismiss: true, builder: (_) { return GiftPage(initialIndex: 1); }, ); } void openGiftDialogToGift() { SmartDialog.show( tag: "showGiftControl", alignment: Alignment.bottomCenter, maskColor: Colors.transparent, animationType: SmartAnimationType.fade, clickMaskDismiss: true, builder: (_) { return GiftPage(); }, ); } }