WGShare.Mobile.Flutter/wgshare/lib/pages/metting/meeting_main_logic.dart

359 lines
13 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 'dart:async';
import 'dart:convert';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:signalr_core/signalr_core.dart';
import 'package:wgshare/common/store/user_store.dart';
import '../../common/config/request_config.dart';
import '../../common/mixins/request_tool_mixin.dart';
import '../../common/models/common/base_structure_result.dart';
import '../../common/models/meeting_room_info.dart';
import '../../common/models/meeting_room_msg.dart';
import '../../common/models/meeting_room_user.dart';
import '../../utils/permission_handler.dart';
import '../../utils/toast_utils.dart';
import 'meeting_main_state.dart';
class MeetingMainLogic extends GetxController with RequestToolMixin{
final MeetingMainState state = MeetingMainState();
@override
void onInit() {
super.onInit();
// 接收参数
var data = Get.arguments;
state.roomNumber.value = data["roomNumber"];
doHttpGetMeetingToken();
}
@override
void onClose() {
super.onClose();
state.memberNameSearchController.dispose();
state.sendMsgController.dispose();
stopTime();
leaveMeetingToRtc();
leaveMeetingToSocket();
state.hubConnection.value?.stop();
}
/// 获取会议室Token
Future<void> doHttpGetMeetingToken() async {
BaseStructureResult<String> res = await getClient().getMeetingToken(state.roomNumber.value);
state.meetingToken.value = res.data!;
initRtc();
signalRSocket();
}
/// 合并请求
/// 1.获取会议室信息
/// 2.获取会议室所有用户
Future<void> mergeFetch() async {
ToastUtils.showLoading();
var results = await Future.wait([
getClient().getMeetingRoomInfo(state.roomNumber.value),
getClient().getMeetingRoomAllUser(state.roomNumber.value)]);
getMeetingRoomInfo(results[0].data as MeetingRoomInfo);
getMeetingRoomAllUser(results[1].data as List<MeetingRoomUser>);
ToastUtils.dismiss();
}
/// 改变会议信息浮层显示状态
void changeMeetingInfoState(bool isShow){
state.isShowMeetingInfoFloatingLayer.value = isShow;
}
/// 改变音频选择浮层显示状态
void changeMeetingAudioState(bool isShow){
state.isShowMeetingAudioFloatingLayer.value = isShow;
}
/// 改变当前页面状态
void changePageState(int pageState){
state.pageState.value = pageState;
}
/// 获取会议室信息
void getMeetingRoomInfo(MeetingRoomInfo meetingRoomInfo) async {
state.meetingRoomInfo.value = meetingRoomInfo;
startTime();
}
/// 获取会议室所有用户
void getMeetingRoomAllUser(List<MeetingRoomUser> meetingRoomUsers) async {
state.users.value = meetingRoomUsers;
state.cacheUsers.value = meetingRoomUsers;
}
/// 启动计时
void startTime(){
state.stopwatch.value.start();
state.timer.value = Timer.periodic(const Duration(milliseconds: 100), upTime);
}
/// 更新计时
void upTime(Timer t) {
if(state.stopwatch.value.isRunning){
state.duration.value =
"${state.stopwatch.value.elapsed.inHours.toString().padLeft(2, "0")}"
":${(state.stopwatch.value.elapsed.inMinutes % 60).toString().padLeft(2, "0")}"
":${(state.stopwatch.value.elapsed.inSeconds % 60).toString().padLeft(2, "0")}";
}
}
/// 停止计时
void stopTime() {
state.timer.value?.cancel();
state.stopwatch.value.stop();
}
/// 搜索成员
void searchMember(String value){
if(value.isNotEmpty){
List<MeetingRoomUser> memberSearchList = [];
for(var i = 0; i < state.cacheUsers.value.length; i++){
if(state.cacheUsers.value[i].userName.contains(value)){
memberSearchList.add(state.cacheUsers.value[i]);
}
}
state.users.value = memberSearchList;
}else{
state.users.value = state.cacheUsers.value;
}
}
/// 申请发言
Future<void> doHttpApplySpeak() async {
BaseStructureResult res = await getClient().applySpeak(state.roomNumber.value);
}
/// 结束发言
Future<void> doHttpCancelSpeak() async {
BaseStructureResult res = await getClient().cancelSpeak(state.meetingRoomInfo.value!.id, state.meetingRoomInfo.value!.roomNum, UserStore.to.userInfoEntity.value!.uid);
}
/// --------------------------signalR Socket相关
/// Socket长连接
Future<void> signalRSocket() async {
state.hubConnection.value = HubConnectionBuilder().withUrl('${RequestConfig().baseUrl}/session-manage',
HttpConnectionOptions(
transport: HttpTransportType.longPolling,
accessTokenFactory: () async => await Future.value(UserStore.to.token),
logging: (level, message) => debugPrint("wgs输出===SignalR Socket-$message"),
)).build();
await state.hubConnection.value?.start();
joinMeetingToSocket();
/// 远端用户或主播加入会议室回调
state.hubConnection.value?.on("UserJoined", (user){
var jsonStr = const Utf8Decoder().convert(json.encode(user).runes.toList());
var listDynamic = jsonDecode(jsonStr);
List<MeetingRoomUser> meetingRoomUsers = (listDynamic as List<dynamic>).map((e) => MeetingRoomUser.fromJson((e as Map<String,dynamic>))).toList();
state.cacheUsers.value.addAll(meetingRoomUsers);
state.users.value = state.cacheUsers.value;
debugPrint("wgs输出===Socket-远端用户或主播加入会议室:$jsonStr");
});
/// 远端用户或主播离开会议室回调
state.hubConnection.value?.on("UserLeave", (uid){
var jsonStr = const Utf8Decoder().convert(json.encode(uid).runes.toList());
List listDynamic = jsonDecode(jsonStr);
for(String uidStr in listDynamic){
for(var j = 0; j < state.cacheUsers.value.length; j++){
if(state.cacheUsers.value[j].uid == uidStr){
state.cacheUsers.value.removeAt(j);
}
}
}
state.users.value = state.cacheUsers.value;
debugPrint("wgs输出===Socket-远端用户或主播离开会议室:$jsonStr");
});
/// 申请/结束发言-管理员同意或拒绝回调
state.hubConnection.value?.on("ManagerRefresh", (e){
var jsonStr = const Utf8Decoder().convert(json.encode(e?[0]).runes.toList());
var listDynamic = jsonDecode(jsonStr);
MeetingRoomUser meetingRoomUser = MeetingRoomUser.fromJson(listDynamic);
if(meetingRoomUser.isRoomManager == true){
debugPrint("wgs输出===Socket-申请发言:${e?[0]}--${e?[1]}");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.roleId = meetingRoomUser.roleId;
mru.enableMicr = true;
mru.isRoomManager = meetingRoomUser.isRoomManager;
}
}
state.isSpeak.value = true;
setEnableLocalAudio(true);
}else{
debugPrint("wgs输出===Socket-停止发言:${e?[0]}--${e?[1]}");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.roleId = meetingRoomUser.roleId;
mru.enableMicr = false;
mru.isRoomManager = meetingRoomUser.isRoomManager;
}
}
state.isSpeak.value = false;
setEnableLocalAudio(false);
}
update();
});
/// 会议室接收消息
state.hubConnection.value?.on("ReceiveMessage", (e){
var jsonStr = const Utf8Decoder().convert(json.encode(e).runes.toList());
List list = json.decode(jsonStr);
MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg(list[0],list[1],list[2],0);
state.meetingRoomMsgs.value.add(meetingRoomMsg);
update();
Future.delayed(const Duration(milliseconds: 100), () {
state.chatController.jumpTo(state.chatController.position.maxScrollExtent);
});
debugPrint("wgs输出===Socket-会议室接收消息:$jsonStr");
});
}
/// 加入会议室
Future<void> joinMeetingToSocket() async {
await state.hubConnection.value?.invoke("joinChannel", args: [state.roomNumber.value, false, false, false]);
mergeFetch();
}
/// 离开会议室
Future<void> leaveMeetingToSocket() async {
await state.hubConnection.value?.invoke("levelChannel", args: [state.roomNumber.value]);
}
/// 会议室发送消息
Future<void> sendMsg(String msg) async {
MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg(
UserStore.to.userInfoEntity.value!.uid,
UserStore.to.userInfoEntity.value!.userName,
msg,1);
state.meetingRoomMsgs.value.add(meetingRoomMsg);
update();
Future.delayed(const Duration(milliseconds: 100), () {
state.chatController.jumpTo(state.chatController.position.maxScrollExtent);
});
await state.hubConnection.value?.invoke("sendChannelMsg", args: [state.roomNumber.value, msg]);
}
/// --------------------------声网SDK相关
/// 初始化声网SDK
Future<void> initRtc() async {
// 请求麦克风、摄像头权限
PermissionHandler.requestCameraPermission();
PermissionHandler.requestMicrophonePermission();
// 创建 RtcEngine 对象
state.rctEngine.value = createAgoraRtcEngine();
// 初始化 RtcEngine设置频道场景为 channelProfileLiveBroadcasting直播场景
await state.rctEngine.value?.initialize(RtcEngineContext(
appId: state.appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
// 设置默认音频路由为听筒
state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false);
joinMeetingToRtc();
// 回调
state.rctEngine.value?.registerEventHandler(
RtcEngineEventHandler(
// 成功加入会议室回调
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
debugPrint("wgs输出===RTC-自己加入会议室ID${connection.localUid}");
},
// 成功离开会议室回调
onLeaveChannel: (RtcConnection connection, RtcStats stats){
debugPrint("wgs输出===RTC-自己离开会议室ID${connection.localUid}");
},
// 远端用户或主播加入当前会议室回调-主播角色才能接收该回调
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
debugPrint("wgs输出===RTC-远端用户或主播加入会议室用户或主机的ID$remoteUid");
},
// 远端用户或主播离开当前会议室回调-主播角色才能接收该回调
onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) async {
debugPrint("wgs输出===RTC-远端用户或主播离开会议室用户或主机的ID$remoteUid");
},
// 音频路由发生变化回调
onAudioRoutingChanged: (int routing){
debugPrint("wgs输出===RTC-音频路由切换:$routing");
state.communicationMode.value = routing;
if(routing == 1){
debugPrint("wgs输出===RTC-音频路由切换为听筒");
}else if(routing == 3){
debugPrint("wgs输出===RTC-音频路由切换为扬声器");
}else{
debugPrint("wgs输出===RTC-音频路由切换为外接设备");
}
},
// 音频采集开关回调
onLocalAudioStateChanged: (RtcConnection connection, LocalAudioStreamState state, LocalAudioStreamReason reason){
debugPrint("wgs输出===RTC-音频采集开关:$state");
}
),
);
}
/// 加入会议室
Future<void> joinMeetingToRtc() async {
await state.rctEngine.value?.joinChannel(
token: state.meetingToken.value,
channelId: state.roomNumber.value,
uid: 0,
options: const ChannelMediaOptions(
// 自动订阅所有视频流
autoSubscribeVideo: true,
// 自动订阅所有音频流
autoSubscribeAudio: true,
// 发布摄像头采集的视频
publishCameraTrack: false,
// 发布麦克风采集的音频
publishMicrophoneTrack: false,
// 设置用户角色为 clientRoleBroadcaster主播或 clientRoleAudience观众
// 这里设置角色为clientRoleBroadcaster主播
// 主播:可以在频道内发布音视频,同时也可以订阅其他主播发布的音视频
// 观众:可以在频道内订阅音视频,不具备发布音视频权限
clientRoleType: ClientRoleType.clientRoleBroadcaster),
);
}
/// 离开会议室
Future<void> leaveMeetingToRtc() async {
// 离开
await state.rctEngine.value?.leaveChannel();
// 释放资源
await state.rctEngine.value?.release();
}
/// 设置音频输出路由(没有外接设备时生效)
Future<void> setEnableSpeakerphone(int mode) async {
state.communicationMode.value = mode;
await state.rctEngine.value?.setEnableSpeakerphone(mode == 1 ? false : true);
}
/// 设置是否打开本地音频采集
Future<void> setEnableLocalAudio(bool enabled) async {
await state.rctEngine.value?.enableLocalAudio(enabled);
}
}