parent
3813d88d0c
commit
3a1dde6e19
|
|
@ -1,4 +1,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
<application
|
||||
android:label="智汇享"
|
||||
android:name="${applicationName}"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- 摄像头 -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>App需要您的同意,才能访问摄像头</string>
|
||||
<!-- 麦克风 -->
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>App需要您的同意,才能访问麦克风</string>
|
||||
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
|
|
|||
|
|
@ -53,4 +53,18 @@ abstract class RetrofitClient {
|
|||
Future<BaseStructureResult<String>> getMeetingToken(
|
||||
@Query("roomNum") String roomNum,
|
||||
);
|
||||
|
||||
/// 申请发言
|
||||
@GET("/room/apply-speak")
|
||||
Future<BaseStructureResult> applySpeak(
|
||||
@Query("roomNum") String roomNum,
|
||||
);
|
||||
|
||||
/// 结束发言
|
||||
@DELETE("/room/manager")
|
||||
Future<BaseStructureResult> cancelSpeak(
|
||||
@Field("roomId") String roomId,
|
||||
@Field("roomNum") String roomNum,
|
||||
@Field("userId") String userId,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class TheError extends Interceptor {
|
|||
break;
|
||||
case DioExceptionType.badResponse:
|
||||
if (err.response == null) {
|
||||
debugPrint('请求进入异常但是请求response');
|
||||
debugPrint('wgs输出===:请求进入异常但是请求response');
|
||||
} else {
|
||||
Response? response = err.response;
|
||||
if (response != null) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class UserStore extends GetxController with RequestToolMixin {
|
|||
userInfoEntity.value = UserInfoEntity.fromJson(userDetail);
|
||||
}
|
||||
} catch (err) {
|
||||
debugPrint('$err');
|
||||
debugPrint('wgs输出===:$err');
|
||||
StorageService.to.remove(AppStorageKey.userInfo.value);
|
||||
}
|
||||
if ((token?.isNotEmpty ?? false) && userInfoEntity.value != null) {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class MyApp extends StatelessWidget {
|
|||
getPages: AppPages.pages,
|
||||
routingCallback: (routing) {
|
||||
String? currentRouter = routing?.current;
|
||||
debugPrint("当前路由:${routing?.current}");
|
||||
debugPrint("wgs输出===:当前路由:${routing?.current}");
|
||||
},
|
||||
builder: EasyLoading.init(
|
||||
builder: (context, child) {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
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:permission_handler/permission_handler.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_user.dart';
|
||||
import '../../utils/permission_handler.dart';
|
||||
import '../../utils/toast_utils.dart';
|
||||
import 'meeting_main_state.dart';
|
||||
|
||||
|
|
@ -34,10 +34,20 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
|||
super.onClose();
|
||||
state.memberNameSearchController.dispose();
|
||||
stopTime();
|
||||
leaveMeeting();
|
||||
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.获取会议室所有用户
|
||||
|
|
@ -54,26 +64,6 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
|||
ToastUtils.dismiss();
|
||||
}
|
||||
|
||||
/// 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("SignalR Socket:$message"),
|
||||
)).build();
|
||||
|
||||
await state.hubConnection.value?.start();
|
||||
|
||||
joinChannelToSocket();
|
||||
}
|
||||
|
||||
/// 加入会议室
|
||||
Future<void> joinChannelToSocket() async {
|
||||
await state.hubConnection.value?.invoke("joinChannel", args: [state.roomNumber.value, false, false, false]);
|
||||
mergeFetch();
|
||||
}
|
||||
|
||||
/// 改变会议信息浮层显示状态
|
||||
void changeMeetingInfoState(bool isShow){
|
||||
state.isShowMeetingInfoFloatingLayer.value = isShow;
|
||||
|
|
@ -101,15 +91,6 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
|||
state.cacheUsers.value = meetingRoomUsers;
|
||||
}
|
||||
|
||||
/// 获取会议室Token
|
||||
Future<void> doHttpGetMeetingToken() async {
|
||||
BaseStructureResult<String> res = await getClient().getMeetingToken(state.roomNumber.value);
|
||||
state.meetingToken.value = res.data!;
|
||||
|
||||
initRtc();
|
||||
signalRSocket();
|
||||
}
|
||||
|
||||
/// 启动计时
|
||||
void startTime(){
|
||||
state.stopwatch.value.start();
|
||||
|
|
@ -136,8 +117,8 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
|||
void searchMember(String value){
|
||||
if(value.isNotEmpty){
|
||||
List<MeetingRoomUser> memberSearchList = [];
|
||||
for(var i = 0; i < state.cacheUsers.length; i++){
|
||||
if(state.cacheUsers[i].userName.contains(value)){
|
||||
for(var i = 0; i < state.cacheUsers.value.length; i++){
|
||||
if(state.cacheUsers.value[i].userName.contains(value)){
|
||||
memberSearchList.add(state.cacheUsers.value[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -147,12 +128,104 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
|||
}
|
||||
}
|
||||
|
||||
/// 申请发言
|
||||
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 = json.encode(user);
|
||||
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 = json.encode(uid);
|
||||
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 = json.encode(e?[0]);
|
||||
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);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/// 加入会议室
|
||||
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]);
|
||||
}
|
||||
|
||||
/// --------------------------声网SDK相关
|
||||
/// 初始化声网SDK
|
||||
Future<void> initRtc() async {
|
||||
// 请求麦克风权限
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
await [Permission.microphone].request();
|
||||
}
|
||||
// 请求麦克风、摄像头权限
|
||||
PermissionHandler.requestCameraPermission();
|
||||
PermissionHandler.requestMicrophonePermission();
|
||||
|
||||
// 创建 RtcEngine 对象
|
||||
state.rctEngine.value = createAgoraRtcEngine();
|
||||
|
|
@ -163,29 +236,58 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
|||
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
|
||||
));
|
||||
|
||||
joinChannelToRtc();
|
||||
// 设置默认音频路由为听筒
|
||||
state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false);
|
||||
|
||||
joinMeetingToRtc();
|
||||
|
||||
// 回调
|
||||
state.rctEngine.value?.registerEventHandler(
|
||||
RtcEngineEventHandler(
|
||||
// 成功加入频道回调
|
||||
|
||||
// 成功加入会议室回调
|
||||
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
|
||||
debugPrint("自己加入会议室,ID:${connection.localUid}");
|
||||
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("远端用户或主播加入会议室,用户或主机的ID:$remoteUid");
|
||||
debugPrint("wgs输出===:RTC-远端用户或主播加入会议室,用户或主机的ID:$remoteUid");
|
||||
},
|
||||
// 远端用户或主播离开当前频道回调
|
||||
onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) {
|
||||
debugPrint("远端用户或主播离开会议室,用户或主机的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> joinChannelToRtc() async {
|
||||
Future<void> joinMeetingToRtc() async {
|
||||
await state.rctEngine.value?.joinChannel(
|
||||
token: state.meetingToken.value,
|
||||
channelId: state.roomNumber.value,
|
||||
|
|
@ -199,16 +301,30 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
|||
publishCameraTrack: false,
|
||||
// 发布麦克风采集的音频
|
||||
publishMicrophoneTrack: false,
|
||||
// 设置用户角色为clientRoleAudience(观众)
|
||||
clientRoleType: ClientRoleType.clientRoleAudience),
|
||||
// 设置用户角色为 clientRoleBroadcaster(主播)或 clientRoleAudience(观众)
|
||||
// 这里设置角色为clientRoleBroadcaster(主播)
|
||||
// 主播:可以在频道内发布音视频,同时也可以订阅其他主播发布的音视频
|
||||
// 观众:可以在频道内订阅音视频,不具备发布音视频权限
|
||||
clientRoleType: ClientRoleType.clientRoleBroadcaster),
|
||||
);
|
||||
}
|
||||
|
||||
/// 离开会议室
|
||||
Future<void> leaveMeeting() async {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ class MeetingMainState {
|
|||
/// 搜索用户时,缓存会议室所有用户原始数据
|
||||
late RxList<MeetingRoomUser> cacheUsers = RxList([]);
|
||||
|
||||
/// 是否被允许发言
|
||||
late RxBool isSpeak = false.obs;
|
||||
|
||||
/// signalR 长连接相关
|
||||
late RxString serviceUrl = "http://192.168.2.9:5192/session-manage".obs;
|
||||
late Rx<HubConnection?> hubConnection = Rx(null);
|
||||
|
|
@ -64,4 +67,6 @@ class MeetingMainState {
|
|||
late RxBool isPublishCameraTrack = false.obs;
|
||||
/// 是否发布麦克风采集的音频
|
||||
late RxBool isPublishMicrophoneTrack = false.obs;
|
||||
/// 当前音频路由,1:听筒,3:扬声器
|
||||
late RxInt communicationMode = 1.obs;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,12 +60,14 @@ class MeetingMainPage extends StatelessWidget {
|
|||
SizedBox(width: 16.w),
|
||||
GestureDetector(
|
||||
child: Image.asset(
|
||||
'assets/images/meeting_main_audio.png',
|
||||
state.communicationMode.value == 1 ? 'assets/images/index_copy.png' : state.communicationMode.value == 3 ? 'assets/images/meeting_main_camera_open.png' : 'assets/images/meeting_main_audio.png',
|
||||
width: 20.w,
|
||||
height: 20.h,
|
||||
),
|
||||
onTap: (){
|
||||
if(state.communicationMode.value == 1 || state.communicationMode.value == 3){
|
||||
logic.changeMeetingAudioState(true);
|
||||
}
|
||||
},
|
||||
),
|
||||
Row(
|
||||
|
|
@ -146,7 +148,7 @@ class MeetingMainPage extends StatelessWidget {
|
|||
// 语音
|
||||
Visibility(
|
||||
visible: true,
|
||||
child: MeetingMainVoiceComponent(users: state.cacheUsers.value)
|
||||
child: MeetingMainVoiceComponent(users: state.cacheUsers)
|
||||
),
|
||||
|
||||
// 视频
|
||||
|
|
@ -226,7 +228,7 @@ class MeetingMainPage extends StatelessWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/meeting_main_microphone_default.png',
|
||||
state.isSpeak.value == true ? 'assets/images/meeting_main_microphone_mute.png' : 'assets/images/meeting_main_microphone_default.png',
|
||||
width: 22.w,
|
||||
height: 22.h,
|
||||
),
|
||||
|
|
@ -235,16 +237,20 @@ class MeetingMainPage extends StatelessWidget {
|
|||
' 静音 ',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: ColorUtil.Color_202_202_202),
|
||||
color: state.isSpeak.value == true ? ColorUtil.Color_85_117_242 : ColorUtil.Color_202_202_202),
|
||||
)
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
if(state.isSpeak.value == false){
|
||||
Get.bottomSheet(
|
||||
isScrollControlled: true,
|
||||
applySpeakPermissionBottomSheet(
|
||||
context)
|
||||
);
|
||||
}else{
|
||||
logic.doHttpCancelSpeak();
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
|
|
@ -631,11 +637,12 @@ class MeetingMainPage extends StatelessWidget {
|
|||
GestureDetector(
|
||||
child: Column(children: [
|
||||
Text(
|
||||
'蓝牙设备',
|
||||
'听筒',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: ColorUtil.Color_85_117_242),
|
||||
fontWeight: state.communicationMode.value == 1 ? FontWeight.w500 : FontWeight.w400,
|
||||
color: state.communicationMode.value == 1 ? ColorUtil.Color_85_117_242 : ColorUtil.Color_134_134_134
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
|
|
@ -645,31 +652,31 @@ class MeetingMainPage extends StatelessWidget {
|
|||
)
|
||||
]),
|
||||
onTap: (){
|
||||
logic.setEnableSpeakerphone(1);
|
||||
logic.changeMeetingAudioState(false);
|
||||
},
|
||||
)
|
||||
);
|
||||
audioList.add(Column(children: [
|
||||
Text(
|
||||
'手机听筒',
|
||||
style: TextStyle(fontSize: 14.sp, color: ColorUtil.Color_134_134_134),
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 1.h,
|
||||
color: ColorUtil.Color_49_47_47,
|
||||
margin: const EdgeInsets.only(top: 14, bottom: 14),
|
||||
)
|
||||
]));
|
||||
audioList.add(Column(
|
||||
audioList.add(
|
||||
GestureDetector(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'扬声器',
|
||||
style:
|
||||
TextStyle(fontSize: 14.sp, color: ColorUtil.Color_134_134_134),
|
||||
)
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: state.communicationMode.value == 3 ? FontWeight.w500 : FontWeight.w400,
|
||||
color: state.communicationMode.value == 3 ? ColorUtil.Color_85_117_242 : ColorUtil.Color_134_134_134
|
||||
),
|
||||
),
|
||||
],
|
||||
));
|
||||
),
|
||||
onTap: (){
|
||||
logic.setEnableSpeakerphone(3);
|
||||
logic.changeMeetingAudioState(false);
|
||||
},
|
||||
)
|
||||
);
|
||||
return audioList;
|
||||
}
|
||||
|
||||
|
|
@ -754,6 +761,7 @@ class MeetingMainPage extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
child: Container(
|
||||
height: 44.h,
|
||||
margin: const EdgeInsets.only(left: 6),
|
||||
|
|
@ -770,6 +778,11 @@ class MeetingMainPage extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
onTap: (){
|
||||
logic.doHttpApplySpeak();
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
@ -901,8 +914,8 @@ class MeetingMainPage extends StatelessWidget {
|
|||
height: 36.h,
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
state.users[index].userName.length >= 3
|
||||
? state.users[index].userName.substring(1,state.users[index].userName.length)
|
||||
state.users[index].userName.length > 3
|
||||
? state.users[index].userName.substring(state.users[index].userName.length - 2,state.users[index].userName.length)
|
||||
: state.users[index].userName,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
|
|
@ -919,6 +932,7 @@ class MeetingMainPage extends StatelessWidget {
|
|||
),
|
||||
SizedBox(width: 8.w),
|
||||
Visibility(
|
||||
visible: state.users[index].roleId == "1" || state.users[index].roleId == "3" ? true : false,
|
||||
child: Text(
|
||||
'主持人',
|
||||
style: TextStyle(
|
||||
|
|
@ -926,7 +940,16 @@ class MeetingMainPage extends StatelessWidget {
|
|||
fontWeight: FontWeight.w500,
|
||||
color: ColorUtil.Color_2_177_136),
|
||||
),
|
||||
visible: state.users[index].isRoomManager,
|
||||
),
|
||||
Visibility(
|
||||
visible: state.users[index].roleId == "2" && state.users[index].isRoomManager == true ? true : false,
|
||||
child: Text(
|
||||
'发言人',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: ColorUtil.Color_2_177_136),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class MeetingMainVideoComponent extends StatelessWidget {
|
|||
pageSnapping: true,
|
||||
onPageChanged: (index) {
|
||||
// 监听事件
|
||||
debugPrint('index=====$index');
|
||||
debugPrint('wgs输出===:$index');
|
||||
},
|
||||
children: <Widget>[
|
||||
Container(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'meeting_main_voice_logic.dart';
|
|||
import 'meeting_main_voice_state.dart';
|
||||
|
||||
class MeetingMainVoiceComponent extends StatelessWidget {
|
||||
MeetingMainVoiceComponent({Key? key, required this.users}) : super(key: key);
|
||||
MeetingMainVoiceComponent({super.key, required this.users});
|
||||
|
||||
final List<MeetingRoomUser> users;
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ class MeetingMainVoiceComponent extends StatelessWidget {
|
|||
behavior: CusBehavior(),
|
||||
child: GridView.builder(
|
||||
gridDelegate:
|
||||
SliverGridDelegateWithFixedCrossAxisCount(
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 0.7,
|
||||
crossAxisSpacing: 20),
|
||||
|
|
@ -44,8 +44,8 @@ class MeetingMainVoiceComponent extends StatelessWidget {
|
|||
height: 76.h,
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
users[index].userName.length >= 3
|
||||
? users[index].userName.substring(1,users[index].userName.length)
|
||||
users[index].userName.length > 3
|
||||
? users[index].userName.substring(users[index].userName.length - 2,users[index].userName.length)
|
||||
: users[index].userName,
|
||||
style: TextStyle(
|
||||
fontSize: 22.sp,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String
|
|||
// placeholder: (context, url) => const CircularProgressIndicator(),
|
||||
placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
|
||||
errorListener: (e) {
|
||||
debugPrint('图片报错.............$e');
|
||||
debugPrint('wgs输出===:图片报错.............$e');
|
||||
},
|
||||
errorWidget: (context, url, error) {
|
||||
return GestureDetector(
|
||||
|
|
|
|||
|
|
@ -17,4 +17,10 @@ class DeviceInfo {
|
|||
return 'error';
|
||||
}
|
||||
}
|
||||
|
||||
static Future<int> getAndroidSdkVersion() async {
|
||||
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||
AndroidDeviceInfo deviceInfo = await deviceInfoPlugin.androidInfo;
|
||||
return deviceInfo.version.sdkInt;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
import 'dart:ffi';
|
||||
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import '../device_info.dart';
|
||||
|
||||
class AndroidPermissionHandler {
|
||||
Future<void> requestAllPermissions() async {
|
||||
await requestLocationPermission();
|
||||
await requestBluetoothPermission();
|
||||
await requestCameraPermission();
|
||||
await requestStoragePermission();
|
||||
await requestNotificationPermission();
|
||||
await requestPhonePermission();
|
||||
}
|
||||
|
||||
Future<void> requestLocationPermission() async {
|
||||
PermissionStatus status;
|
||||
if (Platform.isAndroid && await DeviceInfo.getAndroidSdkVersion() >= 29) {
|
||||
status = await Permission.locationAlways.request();
|
||||
} else {
|
||||
status = await Permission.locationWhenInUse.request();
|
||||
}
|
||||
|
||||
if (status.isGranted) {
|
||||
print("Android: 位置权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("Android: 位置权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("Android: 位置权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestBluetoothPermission() async {
|
||||
PermissionStatus status;
|
||||
if (Platform.isAndroid && await DeviceInfo.getAndroidSdkVersion() >= 31) {
|
||||
status = (await [
|
||||
Permission.bluetoothScan,
|
||||
Permission.bluetoothConnect,
|
||||
Permission.bluetoothAdvertise,
|
||||
].request()) as PermissionStatus;
|
||||
} else {
|
||||
status = await Permission.bluetooth.request();
|
||||
}
|
||||
|
||||
if (status.isGranted) {
|
||||
print("Android: 蓝牙权限已授予");
|
||||
} else {
|
||||
print("Android: 蓝牙权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestCameraPermission() async {
|
||||
PermissionStatus status = await Permission.camera.request();
|
||||
if (status.isGranted) {
|
||||
print("Android: 相机权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("Android: 相机权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("Android: 相机权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestStoragePermission() async {
|
||||
PermissionStatus status = await Permission.storage.request();
|
||||
if (status.isGranted) {
|
||||
print("Android: 存储权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("Android: 存储权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("Android: 存储权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestNotificationPermission() async {
|
||||
if (Platform.isAndroid && await DeviceInfo.getAndroidSdkVersion() >= 33) {
|
||||
PermissionStatus status = await Permission.notification.request();
|
||||
if (status.isGranted) {
|
||||
print("Android: 通知权限已授予");
|
||||
} else {
|
||||
print("Android: 通知权限被拒绝");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestPhonePermission() async {
|
||||
PermissionStatus status = await Permission.phone.request();
|
||||
if (status.isGranted) {
|
||||
print("Android: 电话权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("Android: 电话权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("Android: 电话权限被拒绝");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class IosPermissionHandler {
|
||||
Future<void> requestAllPermissions() async {
|
||||
await requestLocationPermission();
|
||||
await requestBluetoothPermission();
|
||||
await requestCameraPermission();
|
||||
await requestStoragePermission();
|
||||
await requestNotificationPermission();
|
||||
await requestPhonePermission();
|
||||
}
|
||||
|
||||
Future<void> requestLocationPermission() async {
|
||||
PermissionStatus status = await Permission.location.request();
|
||||
if (status.isGranted) {
|
||||
print("iOS: 位置权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("iOS: 位置权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("iOS: 位置权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestBluetoothPermission() async {
|
||||
PermissionStatus status = await Permission.bluetooth.request();
|
||||
if (status.isGranted) {
|
||||
print("iOS: 蓝牙权限已授予");
|
||||
} else {
|
||||
print("iOS: 蓝牙权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestCameraPermission() async {
|
||||
PermissionStatus status = await Permission.camera.request();
|
||||
if (status.isGranted) {
|
||||
print("iOS: 相机权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("iOS: 相机权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("iOS: 相机权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestStoragePermission() async {
|
||||
PermissionStatus status = await Permission.photos.request();
|
||||
if (status.isGranted) {
|
||||
print("iOS: 存储权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("iOS: 存储权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("iOS: 存储权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestNotificationPermission() async {
|
||||
PermissionStatus status = await Permission.notification.request();
|
||||
if (status.isGranted) {
|
||||
print("iOS: 通知权限已授予");
|
||||
} else {
|
||||
print("iOS: 通知权限被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestPhonePermission() async {
|
||||
PermissionStatus status = await Permission.phone.request();
|
||||
if (status.isGranted) {
|
||||
print("iOS: 电话权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("iOS: 电话权限被永久拒绝,请前往设置开启");
|
||||
} else {
|
||||
print("iOS: 电话权限被拒绝");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:wgshare/utils/toast_utils.dart';
|
||||
|
||||
class PermissionHandler {
|
||||
|
||||
/// 摄像头权限
|
||||
static Future<void> requestCameraPermission() async {
|
||||
PermissionStatus status = await Permission.camera.request();
|
||||
if (status.isGranted) {
|
||||
debugPrint("wgs输出===:权限-摄像头权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
debugPrint("wgs输出===:权限-摄像头权限被永久拒绝,请前往设置开启");
|
||||
ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
|
||||
openAppSettings();
|
||||
} else {
|
||||
debugPrint("wgs输出===:权限-摄像头权限被拒绝");
|
||||
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
|
||||
}
|
||||
}
|
||||
|
||||
/// 麦克风权限
|
||||
static Future<void> requestMicrophonePermission() async {
|
||||
PermissionStatus status = await Permission.microphone.request();
|
||||
if (status.isGranted) {
|
||||
debugPrint("wgs输出===:权限-麦克风权限已授予");
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
debugPrint("wgs输出===:权限-麦克风权限被永久拒绝,请前往设置开启");
|
||||
ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
|
||||
openAppSettings();
|
||||
} else {
|
||||
debugPrint("wgs输出===:权限-麦克风权限被拒绝");
|
||||
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue