chatapp3-flutter/lib/services/audio/rtc_manager.dart
2026-04-15 14:44:27 +08:00

1150 lines
36 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: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<OnSoundVoiceChange> _onSoundVoiceChangeList = [];
///麦位
Map<num, MicRes> roomWheatMap = {};
RtcEngine? engine;
BuildContext? context;
RtmProvider? rtmProvider;
SCIsFollowRoomRes? isFollowRoomRes;
SCRoomRocketStatusRes? roomRocketStatus;
///房间是否静音
bool roomIsMute = false;
bool closeFullGame = false;
///禁音开关 默认关闭
bool isMic = true;
///在线用户列表
List<SocialChatUserProfile> onlineUsers = [];
///房间管理员
List<SocialChatUserProfile> managerUsers = [];
///音乐是否正在播放
bool isMusicPlaying = false;
///房间红包列表
List<SCRoomRedPacketListRes> redPacketList = [];
num roomTaskClaimableCount = 0;
initializeRealTimeCommunicationManager(BuildContext context) {
this.context = context;
}
Future<void> 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}');
},
onLocalAudioStateChanged:
(
RtcConnection connection,
LocalAudioStreamState state,
LocalAudioStreamReason reason,
) {},
onAudioRoutingChanged: (routing) {},
onAudioMixingStateChanged: (
AudioMixingStateType state,
AudioMixingReasonType reason,
) {
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) {
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<RtcEngine> _initAgoraRtcEngine() async {
RtcEngine? engine;
while (engine == null) {
engine = createAgoraRtcEngine();
await engine.initialize(
RtcEngineContext(appId: SCGlobalConfig.agoraRtcAppid),
);
}
return engine;
}
void initializeAudioVolumeIndicationCallback(
RtcConnection connection,
List<AudioVolumeInfo> 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<SocialChatUserProfileManager>(
context,
listen: false,
).fetchUserProfileData();
retrieveMicrophoneList();
SCFloatIchart().remove();
SCNavigatorUtils.push(
context,
'${VoiceRoomRoute.voiceRoom}?id=$roomId',
transition: TransitionType.custom,
transitionDuration: kOpenRoomTransitionDuration,
transitionBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> 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<RtmProvider>(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<SocialChatRoomManager>(
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<List<SCRoomRedPacketListRes>> 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().dispose();
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<void> 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().clearTasks();
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<RtmProvider>(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<MicRes>? 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<bool> 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<RtcProvider>(
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<RtcProvider>(context!, listen: false).isMic) {
if (roomWheatMap[index]?.user != null) {
adjustRecordingSignalVolume(100);
Provider.of<RtcProvider>(
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, 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<RtcProvider>(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<RtcProvider>(
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<num, GlobalKey<State<StatefulWidget>>> seatGlobalKeyMap = {};
void bindTargetKey(num index, GlobalKey<State<StatefulWidget>> targetKey) {
seatGlobalKeyMap[index] = targetKey;
}
GlobalKey<State<StatefulWidget>>? 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);
}
}