import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:fluro/fluro.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:yumi/app_localizations.dart'; import 'package:yumi/app/constants/sc_room_msg_type.dart'; import 'package:yumi/shared/tools/sc_permission_utils.dart'; import 'package:yumi/shared/tools/sc_room_utils.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart'; import 'package:yumi/services/room/rc_room_manager.dart'; import 'package:yumi/services/audio/rtm_manager.dart'; import 'package:yumi/services/auth/user_profile_manager.dart'; import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart'; import 'package:provider/provider.dart'; import 'package:yumi/ui_kit/components/sc_tts.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/app/routes/sc_routes.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/shared/tools/sc_lk_dialog_util.dart'; import 'package:yumi/shared/tools/sc_loading_manager.dart'; import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart'; import 'package:yumi/shared/business_logic/models/res/join_room_res.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; import 'package:yumi/shared/business_logic/models/res/mic_res.dart'; import 'package:yumi/shared/business_logic/models/res/room_res.dart'; import 'package:yumi/modules/room/voice_room_route.dart'; import 'package:yumi/ui_kit/widgets/room/empty_mai_select.dart'; import 'package:yumi/ui_kit/widgets/room/room_user_info_card.dart'; import '../../shared/tools/sc_heartbeat_utils.dart'; import '../../shared/data_sources/models/enum/sc_heartbeat_status.dart'; import '../../shared/data_sources/models/enum/sc_room_info_event_type.dart'; import '../../shared/data_sources/models/enum/sc_room_roles_type.dart'; import '../../shared/business_logic/models/res/sc_is_follow_room_res.dart'; import '../../shared/business_logic/models/res/sc_room_red_packet_list_res.dart'; import '../../shared/business_logic/models/res/sc_room_rocket_status_res.dart'; import '../../shared/business_logic/models/res/sc_room_theme_list_res.dart'; import '../../shared/tools/sc_room_profile_cache.dart'; import '../../ui_kit/components/sc_float_ichart.dart'; typedef OnSoundVoiceChange = Function(num index, int volum); typedef RtcProvider = RealTimeCommunicationManager; class RealTimeCommunicationManager extends ChangeNotifier { bool needUpDataUserInfo = false; ///当前所在房间 JoinRoomRes? currenRoom; /// 声音音量变化监听 final List _onSoundVoiceChangeList = []; ///麦位 Map roomWheatMap = {}; RtcEngine? engine; BuildContext? context; RtmProvider? rtmProvider; SCIsFollowRoomRes? isFollowRoomRes; SCRoomRocketStatusRes? roomRocketStatus; ///房间是否静音 bool roomIsMute = false; bool closeFullGame = false; ///禁音开关 默认关闭 bool isMic = true; ///在线用户列表 List onlineUsers = []; ///房间管理员 List managerUsers = []; ///音乐是否正在播放 bool isMusicPlaying = false; ///房间红包列表 List redPacketList = []; num roomTaskClaimableCount = 0; initializeRealTimeCommunicationManager(BuildContext context) { this.context = context; } Future joinAgoraVoiceChannel() async { try { engine = await _initAgoraRtcEngine(); engine?.setAudioProfile( profile: AudioProfileType.audioProfileSpeechStandard, scenario: AudioScenarioType.audioScenarioGameStreaming, ); engine?.enableAudioVolumeIndication( interval: 500, smooth: 3, reportVad: true, ); await engine?.disableVideo(); await engine?.setLocalPublishFallbackOption( StreamFallbackOptions.streamFallbackOptionAudioOnly, ); engine?.registerEventHandler( RtcEngineEventHandler( onError: (ErrorCodeType err, String msg) { print('rtc错误${err}'); }, onAudioMixingStateChanged: ( AudioMixingStateType state, AudioMixingReasonType reason, ) { switch (state) { case AudioMixingStateType.audioMixingStatePlaying: isMusicPlaying = true; break; case AudioMixingStateType.audioMixingStateStopped: case AudioMixingStateType.audioMixingStateFailed: isMusicPlaying = false; break; default: break; } }, onJoinChannelSuccess: (RtcConnection connection, int elapsed) { print('rtc 自己加入 ${connection.channelId} ${connection.localUid}'); }, onUserJoined: (connection, remoteUid, elapsed) { print('rtc用户 $remoteUid 加入了频道'); }, // 监听远端用户离开 onUserOffline: (connection, remoteUid, reason) { print('rtc用户 $remoteUid 离开了频道 (原因: ${reason})'); }, onTokenPrivilegeWillExpire: ( RtcConnection connection, String token, ) async { var rtcToken = await SCAccountRepository().getRtcToken( currenRoom?.roomProfile?.roomProfile?.id ?? "", AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); engine?.renewToken(rtcToken.rtcToken ?? ""); }, onAudioVolumeIndication: initializeAudioVolumeIndicationCallback, ), ); var rtcToken = await SCAccountRepository().getRtcToken( currenRoom?.roomProfile?.roomProfile?.id ?? "", AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); await engine?.joinChannel( token: rtcToken.rtcToken ?? "", channelId: currenRoom?.roomProfile?.roomProfile?.id ?? "", 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, ), ); engine?.muteAllRemoteAudioStreams(roomIsMute); } catch (e) { SCTts.show("Join room fail"); exitCurrentVoiceRoomSession(false); print('加入失败:${e.runtimeType},${e.toString()}'); } } Future _initAgoraRtcEngine() async { RtcEngine? engine; while (engine == null) { engine = createAgoraRtcEngine(); await engine.initialize( RtcEngineContext(appId: SCGlobalConfig.agoraRtcAppid), ); } return engine; } void initializeAudioVolumeIndicationCallback( 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(k, voloum); } } } }); } } } ///关注/取消房间 void followCurrentVoiceRoom() async { var result = await SCAccountRepository().followRoom( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); if (isFollowRoomRes != null) { isFollowRoomRes = isFollowRoomRes!.copyWith( followRoom: !isFollowRoomRes!.followRoom!, ); } else { isFollowRoomRes = SCIsFollowRoomRes(followRoom: result); } SmartDialog.dismiss(tag: "unFollowDialog"); notifyListeners(); } int startTime = 0; String? _preferNonEmpty(String? primary, String? fallback) { if ((primary ?? "").trim().isNotEmpty) { return primary; } if ((fallback ?? "").trim().isNotEmpty) { return fallback; } return primary ?? fallback; } void updateCurrentRoomBasicInfo({ String? roomCover, String? roomName, String? roomDesc, }) { final currentRoomProfile = currenRoom?.roomProfile?.roomProfile; if (currenRoom == null || currentRoomProfile == null) { return; } currenRoom = currenRoom?.copyWith( roomProfile: currenRoom?.roomProfile?.copyWith( roomProfile: currentRoomProfile.copyWith( roomCover: _preferNonEmpty(roomCover, currentRoomProfile.roomCover), roomName: _preferNonEmpty(roomName, currentRoomProfile.roomName), roomDesc: _preferNonEmpty(roomDesc, currentRoomProfile.roomDesc), ), ), ); SCRoomProfileCache.saveRoomProfile( roomId: currentRoomProfile.id ?? "", roomCover: roomCover, roomName: roomName, roomDesc: roomDesc, ); notifyListeners(); } void joinVoiceRoomSession( BuildContext context, String roomId, { String? pwd, bool clearRoomData = false, bool needOpenRedenvelope = false, String redPackId = "", }) async { int nextTime = DateTime.now().millisecondsSinceEpoch; if (nextTime - startTime < 1000) { //频繁点击 return; } startTime = nextTime; if (clearRoomData) { _clearData(); } bool hasPermission = await SCPermissionUtils.checkMicrophonePermission(); if (!hasPermission) { SCTts.show('Microphone permission is denied.'); throw ArgumentError('Microphone permission is denied.'); } if (roomId == currenRoom?.roomProfile?.roomProfile?.id) { ///最小化进入房间,或者进入的是同一个房间 final loaded = await loadRoomInfo( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); if (!loaded) { return; } if (!context.mounted) { return; } Provider.of( context, listen: false, ).fetchUserProfileData(); retrieveMicrophoneList(); SCFloatIchart().remove(); SCNavigatorUtils.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 { SCFloatIchart().remove(); if (currenRoom != null) { await exitCurrentVoiceRoomSession(false); } if (!context.mounted) { return; } rtmProvider = Provider.of(context, listen: false); SCLoadingManager.show(context: context); try { currenRoom = await SCAccountRepository().entryRoom( roomId, pwd: pwd, redPackId: redPackId, needOpenRedenvelope: needOpenRedenvelope, ); await initializeRoomSession( needOpenRedenvelope: needOpenRedenvelope, redPackId: redPackId, ); notifyListeners(); } catch (e) { await resetLocalRoomState(fallbackRtmProvider: rtmProvider); rethrow; } finally { SCLoadingManager.hide(); } } } ///初始化房间相关 Future initializeRoomSession({ bool needOpenRedenvelope = false, String? redPackId, }) async { if ((currenRoom?.roomProfile?.roomProfile?.event == SCRoomInfoEventType.WAITING_CONFIRMED.name || currenRoom?.roomProfile?.roomProfile?.event == SCRoomInfoEventType.ID_CHANGE.name) && currenRoom?.entrants?.roles == SCRoomRolesType.HOMEOWNER.name) { ///需要去修改房间信息,创建群聊 SCNavigatorUtils.push( context!, "${VoiceRoomRoute.roomEdit}?need=true", replace: false, ); return Future; } SCRoomUtils.roomSCGlobalConfig( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); if (currenRoom?.roomProfile?.roomProfile?.id != AccountStorage().getCurrentUser()?.userProfile?.id) { SCChatRoomRepository() .isFollowRoom(currenRoom?.roomProfile?.roomProfile?.id ?? "") .then((value) { isFollowRoomRes = value; notifyListeners(); }); } SCNavigatorUtils.push(context!, VoiceRoomRoute.voiceRoom, replace: false); var joinResult = await rtmProvider?.joinRoomGroup( currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", "", ); SCChatRoomRepository() .rocketClaim(currenRoom?.roomProfile?.roomProfile?.id ?? "") .catchError((e) { return true; // 错误已处理 }); rtmProvider?.addMsg( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", msg: SCAppLocalizations.of(context!)!.systemRoomTips, type: SCRoomMsgType.systemTips, ), ); rtmProvider?.addMsg( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", msg: currenRoom?.roomProfile?.roomProfile?.roomDesc, type: SCRoomMsgType.systemTips, ), ); ///获取麦位 retrieveMicrophoneList(); Provider.of( context!, listen: false, ).fetchContributionLevelData( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); await joinAgoraVoiceChannel(); if (joinResult?.code == 0) { Msg joinMsg = Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "", user: AccountStorage().getCurrentUser()?.userProfile, msg: "", role: currenRoom?.entrants?.roles ?? "", type: SCRoomMsgType.joinRoom, ); rtmProvider?.dispatchMessage(joinMsg, addLocal: true); if (SCGlobalConfig.isEntryVehicleAnimation) { if (AccountStorage().getCurrentUser()?.userProfile?.getMountains() != null) { Future.delayed(Duration(milliseconds: 550), () { SCGiftVapSvgaManager().play( AccountStorage() .getCurrentUser() ?.userProfile ?.getMountains() ?.sourceUrl ?? "", priority: 100, type: 1, ); }); } } if (rtmProvider?.msgUserJoinListener != null) { rtmProvider?.msgUserJoinListener!(joinMsg); } } SCChatRoomRepository() .rocketStatus(currenRoom?.roomProfile?.roomProfile?.id ?? "") .then((res) { roomRocketStatus = res; notifyListeners(); }) .catchError((e) {}); loadRoomRedPacketList(1); fetchRoomTaskClaimableCount(); } ///更新房间火箭信息 void updateRoomRocketConfigurationStatus(SCRoomRocketStatusRes res) { roomRocketStatus = res; notifyListeners(); } ///获取在线用户 Future fetchOnlineUsersList() async { onlineUsers = await SCChatRoomRepository().roomOnlineUsers( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); managerUsers.clear(); for (var user in onlineUsers) { if (user.roles == SCRoomRolesType.ADMIN.name) { managerUsers.add(user); } } notifyListeners(); } Future retrieveMicrophoneList() async { bool isOnMic = false; var roomWheatList = await SCChatRoomRepository().micList( currenRoom!.roomProfile?.roomProfile?.id ?? "", ); for (var roomWheat in roomWheatList) { roomWheatMap[roomWheat.micIndex!] = roomWheat; if (roomWheat.user != null && roomWheat.user!.id == AccountStorage().getCurrentUser()?.userProfile?.id) { isOnMic = true; ///自己在麦上 SCHeartbeatUtils.scheduleAnchorHeartbeat( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); } } notifyListeners(); SCHeartbeatUtils.scheduleHeartbeat( SCHeartbeatStatus.VOICE_LIVE.name, isOnMic, roomId: currenRoom?.roomProfile?.roomProfile?.id, ); Future.delayed(Duration(milliseconds: 1500), () { fetchOnlineUsersList(); }); } void fetchRoomTaskClaimableCount() { SCChatRoomRepository() .roomTaskClaimableCount() .then((res) { roomTaskClaimableCount = res.claimableCount ?? 0; notifyListeners(); }) .catchError((e) {}); } Future> loadRoomRedPacketList( int current, ) async { var result = await SCChatRoomRepository().roomRedPacketList( currenRoom?.roomProfile?.roomProfile?.id ?? "", current, ); if (current == 1) { redPacketList = result; notifyListeners(); } return result; } Future exitCurrentVoiceRoomSession(bool isLogout) async { try { rtmProvider?.msgAllListener = null; rtmProvider?.msgChatListener = null; rtmProvider?.msgGiftListener = null; SCLoadingManager.show(context: context); SCGiftVapSvgaManager().stopPlayback(); SCHeartbeatUtils.scheduleHeartbeat(SCHeartbeatStatus.ONLINE.name, false); SCHeartbeatUtils.cancelAnchorTimer(); rtmProvider?.cleanRoomData(); await engine?.leaveChannel(); await Future.delayed(Duration(milliseconds: 100)); await engine?.release(); await rtmProvider?.quitGroup( currenRoom!.roomProfile?.roomProfile?.roomAccount ?? "", ); await SCAccountRepository().quitRoom( currenRoom!.roomProfile?.roomProfile?.id ?? "", ); _clearData(); SCLoadingManager.hide(); if (!isLogout) { SCNavigatorUtils.popUntil(context!, ModalRoute.withName(SCRoutes.home)); } } catch (e) { print('rtc退出房间出错: $e'); _clearData(); SCLoadingManager.hide(); if (!isLogout) { SCNavigatorUtils.popUntil(context!, ModalRoute.withName(SCRoutes.home)); } } } Future resetLocalRoomState({RtmProvider? fallbackRtmProvider}) async { rtmProvider ??= fallbackRtmProvider; try { SCHeartbeatUtils.cancelTimer(); SCHeartbeatUtils.cancelAnchorTimer(); final groupId = currenRoom?.roomProfile?.roomProfile?.roomAccount ?? ""; if (groupId.isNotEmpty) { await rtmProvider?.quitGroup(groupId); } await engine?.leaveChannel(); await Future.delayed(const Duration(milliseconds: 100)); await engine?.release(); } catch (e) { print('rtc清理本地房间状态出错: $e'); } finally { engine = null; rtmProvider?.cleanRoomData(); _clearData(); notifyListeners(); } } ///清空列表数据 void _clearData() { roomRocketStatus = null; rtmProvider ?.onNewMessageListenerGroupMap["${currenRoom?.roomProfile?.roomProfile?.roomAccount}"] = null; roomWheatMap.clear(); onlineUsers.clear(); needUpDataUserInfo = false; SCRoomUtils.roomUsersMap.clear(); roomIsMute = false; rtmProvider?.roomAllMsgList.clear(); rtmProvider?.roomChatMsgList.clear(); redPacketList.clear(); currenRoom = null; isMic = true; isMusicPlaying = false; DataPersistence.setLastTimeRoomId(""); SCGiftVapSvgaManager().stopPlayback(); SCRoomUtils.closeAllDialogs(); SCGlobalConfig.isEntryVehicleAnimation = true; SCGlobalConfig.isGiftSpecialEffects = true; SCGlobalConfig.isFloatingAnimationInGlobal = true; SCGlobalConfig.isLuckGiftSpecialEffects = true; } void toggleRemoteAudioMuteForAllUsers() { roomIsMute = !roomIsMute; engine?.muteAllRemoteAudioStreams(roomIsMute); notifyListeners(); } ///点击的位置 void clickSite(num index, {SocialChatUserProfile? clickUser}) { if (index == -1) { if (clickUser != null) { if (clickUser.id == AccountStorage().getCurrentUser()?.userProfile?.id) { ///是自己,直接打开资料卡 showBottomInCenterDialog( context!, RoomUserInfoCard(userId: clickUser.id), ); } else { showBottomInBottomDialog( context!, EmptyMaiSelect(index: index, clickUser: clickUser), ); } } } else { SocialChatUserProfile? roomWheatUser = roomWheatMap[index]?.user; showBottomInBottomDialog( context!, EmptyMaiSelect(index: index, clickUser: roomWheatUser), ); // if (roomWheatUser != null) { // ///麦上有人 // if (roomWheatUser.id == // AccountStorage().getCurrentUser()?.userProfile?.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 (currenRoom?.roomProfile?.roomSetting?.touristMike ?? false) { } else { if (isTourists()) { SCTts.show( SCAppLocalizations.of(context!)!.touristsAreNotAllowedToGoOnTheMic, ); return; } } try { var micGoUpRes = await SCChatRoomRepository().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); } SCHeartbeatUtils.scheduleAnchorHeartbeat( currenRoom?.roomProfile?.roomProfile?.id ?? "", ); var us = roomWheatMap[index]; us?.copyWith( user: SocialChatUserProfile( id: myUser?.id, account: myUser?.account, userAvatar: myUser?.userAvatar, userNickname: myUser?.userNickname, userSex: myUser?.userSex, ), ); roomWheatMap[index] = us!; if (us.micMute!) { ///房主上麦自动解禁麦位 if (isFz()) { jieJinMai(index); } } notifyListeners(); } catch (ex) { SCTts.show('Failed to put on the microphone, $ex'); } } xiaMai(num index) async { SCChatRoomRepository() .micGoDown(currenRoom?.roomProfile?.roomProfile?.id ?? "", index) .whenComplete(() { //自己 if (roomWheatMap[index]?.user?.id == AccountStorage().getCurrentUser()?.userProfile?.id) { isMic = true; engine?.muteLocalAudioStream(true); } SCHeartbeatUtils.cancelAnchorTimer(); /// 设置成主持人角色 engine?.renewToken(""); engine?.setClientRole(role: ClientRoleType.clientRoleAudience); roomWheatMap[index]?.setUser = null; notifyListeners(); }); } ///踢人下麦 killXiaMai(String userId) { try { roomWheatMap.forEach((k, v) async { if (v.user?.id == userId) { var canKill = await SCChatRoomRepository().kickOffMicrophone( currenRoom?.roomProfile?.roomProfile?.id ?? "", userId, ); if (canKill) { await SCChatRoomRepository().micKill( currenRoom?.roomProfile?.roomProfile?.id ?? "", k, ); Provider.of(context!, listen: false).dispatchMessage( Msg( groupId: currenRoom?.roomProfile?.roomProfile?.roomAccount, msg: userId, type: SCRoomMsgType.killXiaMai, ), addLocal: false, ); roomWheatMap[k]?.setUser = null; notifyListeners(); } else { SCTts.show("cant kill the user"); } } }); } catch (e) { SCTts.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 == SCRoomRolesType.HOMEOWNER.name; } ///是否是管理 bool isGL() { return currenRoom?.entrants?.roles == SCRoomRolesType.ADMIN.name; } ///是否是会员 bool isHY() { return currenRoom?.entrants?.roles == SCRoomRolesType.MEMBER.name; } ///是否是游客 bool isTourists() { return currenRoom?.entrants?.roles == SCRoomRolesType.TOURIST.name; } ///麦位变动 void micChange(List? mics) { if (mics == null || mics.isEmpty) { return; } roomWheatMap.clear(); for (var mic in mics) { roomWheatMap[mic.micIndex!] = mic; if (mic.user?.id == AccountStorage().getCurrentUser()?.userProfile?.id) { if (mic.micMute!) { ///麦克风静音 engine?.muteLocalAudioStream(true); } else { // if (!isMic) { // engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); // } // engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); ///这个用户需要禁麦/解禁麦克风 engine?.muteLocalAudioStream(false); } } } notifyListeners(); ///判断自己是不是在麦上 num index = userOnMaiInIndex( AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); if (index == -1) { engine?.muteLocalAudioStream(true); } } ///找一个空麦位 -1表示没有空位 num findWheat() { for (var entry in roomWheatMap.entries) { if (entry.value.user == null && !entry.value.micLock!) { return entry.key; } } return -1; } Future loadRoomInfo(String roomId) async { try { debugPrint("[Room Cover Sync] loadRoomInfo start roomId=$roomId"); final value = await SCChatRoomRepository().specific(roomId); final currentRoomProfile = currenRoom?.roomProfile?.roomProfile; debugPrint( "[Room Cover Sync] loadRoomInfo fetched roomId=${value.id ?? roomId} roomCover=${value.roomCover ?? ""} roomName=${value.roomName ?? ""}", ); currenRoom = currenRoom?.copyWith( roomProfile: currenRoom?.roomProfile?.copyWith( roomCounter: value.counter ?? currenRoom?.roomProfile?.roomCounter, roomSetting: value.setting ?? currenRoom?.roomProfile?.roomSetting, roomProfile: currentRoomProfile?.copyWith( roomCover: _preferNonEmpty( value.roomCover, currentRoomProfile.roomCover, ), roomName: _preferNonEmpty( value.roomName, currentRoomProfile.roomName, ), roomDesc: _preferNonEmpty( value.roomDesc, currentRoomProfile.roomDesc, ), ), ), ); debugPrint( "[Room Cover Sync] loadRoomInfo applied roomId=$roomId roomCover=${currenRoom?.roomProfile?.roomProfile?.roomCover ?? ""}", ); ///如果是游客禁止上麦,已经在麦上的游客需要下麦 bool touristMike = currenRoom?.roomProfile?.roomSetting?.touristMike ?? false; if (!touristMike && isTourists()) { num index = userOnMaiInIndex( AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); if (index > -1) { xiaMai(index); } } notifyListeners(); return true; } catch (e) { await resetLocalRoomState(fallbackRtmProvider: rtmProvider); SCTts.show("Room not exists"); return false; } } ///解锁麦位 void jieFeng(num index) async { await SCChatRoomRepository().micLock( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, false, ); } ///锁麦 void fengMai(num index) async { await SCChatRoomRepository().micLock( currenRoom?.roomProfile?.roomProfile?.id ?? "", index, true, ); } ///静音麦克风 void jinMai(num index) async { await SCChatRoomRepository().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 SCChatRoomRepository().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); Provider.of( context!, listen: false, ).engine?.muteLocalAudioStream(false); } } } var mic = roomWheatMap[index]; if (mic != null) { roomWheatMap[index] = mic.copyWith(micMute: false); notifyListeners(); } } void addOnlineUser(String groupId, SocialChatUserProfile 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; } SocialChatUserProfile? 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(SCRoomThemeListRes 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]; } return null; } ///播放音乐 void startAudioMixing(String localPath, int cycle) { if (localPath.isEmpty) { return; } engine?.startAudioMixing( filePath: localPath, loopback: false, cycle: cycle, ); } void adjustRecordingSignalVolume(int volume) { engine?.adjustRecordingSignalVolume(volume); } }