1732 lines
52 KiB
Dart
1732 lines
52 KiB
Dart
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<OnSoundVoiceChange> _onSoundVoiceChangeList = [];
|
||
|
||
///麦位
|
||
Map<num, MicRes> roomWheatMap = {};
|
||
final Set<int> _remoteRtcUidsInChannel = <int>{};
|
||
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<ChatVibeUserProfile> onlineUsers = [];
|
||
|
||
///房间管理员
|
||
List<ChatVibeUserProfile> managerUsers = [];
|
||
|
||
///音乐是否正在播放
|
||
bool isMusicPlaying = false;
|
||
|
||
///房间红包列表
|
||
List<ATRoomRedPacketListRes> redPacketList = [];
|
||
|
||
num roomTaskClaimableCount = 0;
|
||
bool _isJoiningRoom = false;
|
||
bool _isExitingRoom = false;
|
||
int _roomSessionId = 0;
|
||
Future<void>? _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<void> 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<AudioManager>(
|
||
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<AudioVolumeInfo> 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<RtcEngine> _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<int> _buildAllowedSpeakerUids() {
|
||
final Set<int> allowed = <int>{};
|
||
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<void> _applyRemoteAudioPolicy() async {
|
||
if (engine == null) {
|
||
return;
|
||
}
|
||
final Set<int> 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<MicRes> _normalizeMicSnapshot(List<MicRes> mics) {
|
||
final Set<String> seenUserIds = <String>{};
|
||
final List<MicRes> normalized = <MicRes>[];
|
||
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<void> _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<void> _disposeRtcEngine() async {
|
||
final currentEngine = engine;
|
||
engine = null;
|
||
if (currentEngine == null) {
|
||
_remoteRtcUidsInChannel.clear();
|
||
return;
|
||
}
|
||
await _shutdownRtcEngine(currentEngine);
|
||
_remoteRtcUidsInChannel.clear();
|
||
}
|
||
|
||
Future<void> 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<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.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<ChatVibeUserProfileManager>(
|
||
context,
|
||
listen: false,
|
||
).getMyUserInfo();
|
||
getMicList();
|
||
ATFloatIchart().remove();
|
||
ATNavigatorUtils.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 {
|
||
ATFloatIchart().remove();
|
||
if (currenRoom != null) {
|
||
// 切房时先退出当前房间,但不跳回 Home,避免 context 失效导致后续 join 偶发失败
|
||
await extRoom(false, navigateHome: false);
|
||
}
|
||
rtmProvider = Provider.of<RtmProvider>(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<RtmProvider>(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<ChatVibeRoomManager>(
|
||
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<List<ATRoomRedPacketListRes>> loadRoomRedPacketList(
|
||
int current,
|
||
) async {
|
||
var result = await ChatRoomRepository().roomRedPacketList(
|
||
currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||
current,
|
||
);
|
||
if (current == 1) {
|
||
redPacketList = result;
|
||
notifyListeners();
|
||
}
|
||
return result;
|
||
}
|
||
|
||
Future<void> 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<void> _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<AudioManager>(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;
|
||
if (roomWheatUser != null &&
|
||
roomWheatUser.id !=
|
||
AccountStorage().getCurrentUser()?.userProfile?.id &&
|
||
!isFz() &&
|
||
!isGL()) {
|
||
showBottomInBottomDialog(
|
||
context!,
|
||
ATRoomUserInfoCard(userId: roomWheatUser.id),
|
||
);
|
||
return;
|
||
}
|
||
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<void> 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<void> 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<RtmProvider>(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<void> micChange(List<MicRes>? 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<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 ChatRoomRepository().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, 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<RtcProvider>(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<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(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<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];
|
||
}
|
||
}
|
||
|
||
///播放音乐
|
||
void playMusic(String localPath, int cycle) {
|
||
if (localPath.isEmpty) {
|
||
return;
|
||
}
|
||
engine?.startAudioMixing(
|
||
filePath: localPath,
|
||
loopback: false,
|
||
cycle: cycle,
|
||
);
|
||
}
|
||
|
||
///停止播放音乐
|
||
void stopMusic() {
|
||
Provider.of<AudioManager>(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<RtmProvider>(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<RtmProvider>(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();
|
||
},
|
||
);
|
||
}
|
||
}
|