parent
3813d88d0c
commit
3a1dde6e19
|
|
@ -1,4 +1,8 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<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
|
<application
|
||||||
android:label="智汇享"
|
android:label="智汇享"
|
||||||
android:name="${applicationName}"
|
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">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<!-- 摄像头 -->
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>App需要您的同意,才能访问摄像头</string>
|
||||||
|
<!-- 麦克风 -->
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>App需要您的同意,才能访问麦克风</string>
|
||||||
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
|
|
|
||||||
|
|
@ -53,4 +53,18 @@ abstract class RetrofitClient {
|
||||||
Future<BaseStructureResult<String>> getMeetingToken(
|
Future<BaseStructureResult<String>> getMeetingToken(
|
||||||
@Query("roomNum") String roomNum,
|
@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;
|
break;
|
||||||
case DioExceptionType.badResponse:
|
case DioExceptionType.badResponse:
|
||||||
if (err.response == null) {
|
if (err.response == null) {
|
||||||
debugPrint('请求进入异常但是请求response');
|
debugPrint('wgs输出===:请求进入异常但是请求response');
|
||||||
} else {
|
} else {
|
||||||
Response? response = err.response;
|
Response? response = err.response;
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ class UserStore extends GetxController with RequestToolMixin {
|
||||||
userInfoEntity.value = UserInfoEntity.fromJson(userDetail);
|
userInfoEntity.value = UserInfoEntity.fromJson(userDetail);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint('$err');
|
debugPrint('wgs输出===:$err');
|
||||||
StorageService.to.remove(AppStorageKey.userInfo.value);
|
StorageService.to.remove(AppStorageKey.userInfo.value);
|
||||||
}
|
}
|
||||||
if ((token?.isNotEmpty ?? false) && userInfoEntity.value != null) {
|
if ((token?.isNotEmpty ?? false) && userInfoEntity.value != null) {
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class MyApp extends StatelessWidget {
|
||||||
getPages: AppPages.pages,
|
getPages: AppPages.pages,
|
||||||
routingCallback: (routing) {
|
routingCallback: (routing) {
|
||||||
String? currentRouter = routing?.current;
|
String? currentRouter = routing?.current;
|
||||||
debugPrint("当前路由:${routing?.current}");
|
debugPrint("wgs输出===:当前路由:${routing?.current}");
|
||||||
},
|
},
|
||||||
builder: EasyLoading.init(
|
builder: EasyLoading.init(
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
import 'package:signalr_core/signalr_core.dart';
|
import 'package:signalr_core/signalr_core.dart';
|
||||||
import 'package:wgshare/common/store/user_store.dart';
|
import 'package:wgshare/common/store/user_store.dart';
|
||||||
|
|
||||||
import '../../common/config/request_config.dart';
|
import '../../common/config/request_config.dart';
|
||||||
import '../../common/mixins/request_tool_mixin.dart';
|
import '../../common/mixins/request_tool_mixin.dart';
|
||||||
import '../../common/models/common/base_structure_result.dart';
|
import '../../common/models/common/base_structure_result.dart';
|
||||||
import '../../common/models/meeting_room_info.dart';
|
import '../../common/models/meeting_room_info.dart';
|
||||||
import '../../common/models/meeting_room_user.dart';
|
import '../../common/models/meeting_room_user.dart';
|
||||||
|
import '../../utils/permission_handler.dart';
|
||||||
import '../../utils/toast_utils.dart';
|
import '../../utils/toast_utils.dart';
|
||||||
import 'meeting_main_state.dart';
|
import 'meeting_main_state.dart';
|
||||||
|
|
||||||
|
|
@ -34,10 +34,20 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
||||||
super.onClose();
|
super.onClose();
|
||||||
state.memberNameSearchController.dispose();
|
state.memberNameSearchController.dispose();
|
||||||
stopTime();
|
stopTime();
|
||||||
leaveMeeting();
|
leaveMeetingToRtc();
|
||||||
|
leaveMeetingToSocket();
|
||||||
state.hubConnection.value?.stop();
|
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.获取会议室信息
|
/// 1.获取会议室信息
|
||||||
/// 2.获取会议室所有用户
|
/// 2.获取会议室所有用户
|
||||||
|
|
@ -54,26 +64,6 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
||||||
ToastUtils.dismiss();
|
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){
|
void changeMeetingInfoState(bool isShow){
|
||||||
state.isShowMeetingInfoFloatingLayer.value = isShow;
|
state.isShowMeetingInfoFloatingLayer.value = isShow;
|
||||||
|
|
@ -101,15 +91,6 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
||||||
state.cacheUsers.value = meetingRoomUsers;
|
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(){
|
void startTime(){
|
||||||
state.stopwatch.value.start();
|
state.stopwatch.value.start();
|
||||||
|
|
@ -136,8 +117,8 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
||||||
void searchMember(String value){
|
void searchMember(String value){
|
||||||
if(value.isNotEmpty){
|
if(value.isNotEmpty){
|
||||||
List<MeetingRoomUser> memberSearchList = [];
|
List<MeetingRoomUser> memberSearchList = [];
|
||||||
for(var i = 0; i < state.cacheUsers.length; i++){
|
for(var i = 0; i < state.cacheUsers.value.length; i++){
|
||||||
if(state.cacheUsers[i].userName.contains(value)){
|
if(state.cacheUsers.value[i].userName.contains(value)){
|
||||||
memberSearchList.add(state.cacheUsers.value[i]);
|
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
|
/// 初始化声网SDK
|
||||||
Future<void> initRtc() async {
|
Future<void> initRtc() async {
|
||||||
// 请求麦克风权限
|
// 请求麦克风、摄像头权限
|
||||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
PermissionHandler.requestCameraPermission();
|
||||||
await [Permission.microphone].request();
|
PermissionHandler.requestMicrophonePermission();
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 RtcEngine 对象
|
// 创建 RtcEngine 对象
|
||||||
state.rctEngine.value = createAgoraRtcEngine();
|
state.rctEngine.value = createAgoraRtcEngine();
|
||||||
|
|
@ -163,29 +236,58 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
||||||
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
|
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
|
||||||
));
|
));
|
||||||
|
|
||||||
joinChannelToRtc();
|
// 设置默认音频路由为听筒
|
||||||
|
state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false);
|
||||||
|
|
||||||
|
joinMeetingToRtc();
|
||||||
|
|
||||||
// 回调
|
// 回调
|
||||||
state.rctEngine.value?.registerEventHandler(
|
state.rctEngine.value?.registerEventHandler(
|
||||||
RtcEngineEventHandler(
|
RtcEngineEventHandler(
|
||||||
// 成功加入频道回调
|
|
||||||
|
// 成功加入会议室回调
|
||||||
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
|
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) {
|
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(
|
await state.rctEngine.value?.joinChannel(
|
||||||
token: state.meetingToken.value,
|
token: state.meetingToken.value,
|
||||||
channelId: state.roomNumber.value,
|
channelId: state.roomNumber.value,
|
||||||
|
|
@ -199,16 +301,30 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
|
||||||
publishCameraTrack: false,
|
publishCameraTrack: false,
|
||||||
// 发布麦克风采集的音频
|
// 发布麦克风采集的音频
|
||||||
publishMicrophoneTrack: false,
|
publishMicrophoneTrack: false,
|
||||||
// 设置用户角色为clientRoleAudience(观众)
|
// 设置用户角色为 clientRoleBroadcaster(主播)或 clientRoleAudience(观众)
|
||||||
clientRoleType: ClientRoleType.clientRoleAudience),
|
// 这里设置角色为clientRoleBroadcaster(主播)
|
||||||
|
// 主播:可以在频道内发布音视频,同时也可以订阅其他主播发布的音视频
|
||||||
|
// 观众:可以在频道内订阅音视频,不具备发布音视频权限
|
||||||
|
clientRoleType: ClientRoleType.clientRoleBroadcaster),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 离开会议室
|
/// 离开会议室
|
||||||
Future<void> leaveMeeting() async {
|
Future<void> leaveMeetingToRtc() async {
|
||||||
// 离开
|
// 离开
|
||||||
await state.rctEngine.value?.leaveChannel();
|
await state.rctEngine.value?.leaveChannel();
|
||||||
// 释放资源
|
// 释放资源
|
||||||
await state.rctEngine.value?.release();
|
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 RxList<MeetingRoomUser> cacheUsers = RxList([]);
|
||||||
|
|
||||||
|
/// 是否被允许发言
|
||||||
|
late RxBool isSpeak = false.obs;
|
||||||
|
|
||||||
/// signalR 长连接相关
|
/// signalR 长连接相关
|
||||||
late RxString serviceUrl = "http://192.168.2.9:5192/session-manage".obs;
|
late RxString serviceUrl = "http://192.168.2.9:5192/session-manage".obs;
|
||||||
late Rx<HubConnection?> hubConnection = Rx(null);
|
late Rx<HubConnection?> hubConnection = Rx(null);
|
||||||
|
|
@ -64,4 +67,6 @@ class MeetingMainState {
|
||||||
late RxBool isPublishCameraTrack = false.obs;
|
late RxBool isPublishCameraTrack = false.obs;
|
||||||
/// 是否发布麦克风采集的音频
|
/// 是否发布麦克风采集的音频
|
||||||
late RxBool isPublishMicrophoneTrack = 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),
|
SizedBox(width: 16.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Image.asset(
|
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,
|
width: 20.w,
|
||||||
height: 20.h,
|
height: 20.h,
|
||||||
),
|
),
|
||||||
onTap: (){
|
onTap: (){
|
||||||
|
if(state.communicationMode.value == 1 || state.communicationMode.value == 3){
|
||||||
logic.changeMeetingAudioState(true);
|
logic.changeMeetingAudioState(true);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -146,7 +148,7 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
// 语音
|
// 语音
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: true,
|
visible: true,
|
||||||
child: MeetingMainVoiceComponent(users: state.cacheUsers.value)
|
child: MeetingMainVoiceComponent(users: state.cacheUsers)
|
||||||
),
|
),
|
||||||
|
|
||||||
// 视频
|
// 视频
|
||||||
|
|
@ -226,7 +228,7 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Image.asset(
|
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,
|
width: 22.w,
|
||||||
height: 22.h,
|
height: 22.h,
|
||||||
),
|
),
|
||||||
|
|
@ -235,16 +237,20 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
' 静音 ',
|
' 静音 ',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12.sp,
|
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: () {
|
onTap: () {
|
||||||
|
if(state.isSpeak.value == false){
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
applySpeakPermissionBottomSheet(
|
applySpeakPermissionBottomSheet(
|
||||||
context)
|
context)
|
||||||
);
|
);
|
||||||
|
}else{
|
||||||
|
logic.doHttpCancelSpeak();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -631,11 +637,12 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
Text(
|
Text(
|
||||||
'蓝牙设备',
|
'听筒',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14.sp,
|
fontSize: 14.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: state.communicationMode.value == 1 ? FontWeight.w500 : FontWeight.w400,
|
||||||
color: ColorUtil.Color_85_117_242),
|
color: state.communicationMode.value == 1 ? ColorUtil.Color_85_117_242 : ColorUtil.Color_134_134_134
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
|
@ -645,31 +652,31 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
onTap: (){
|
onTap: (){
|
||||||
|
logic.setEnableSpeakerphone(1);
|
||||||
logic.changeMeetingAudioState(false);
|
logic.changeMeetingAudioState(false);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
audioList.add(Column(children: [
|
audioList.add(
|
||||||
Text(
|
GestureDetector(
|
||||||
'手机听筒',
|
child: Column(
|
||||||
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(
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'扬声器',
|
'扬声器',
|
||||||
style:
|
style: TextStyle(
|
||||||
TextStyle(fontSize: 14.sp, color: ColorUtil.Color_134_134_134),
|
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;
|
return audioList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -754,6 +761,7 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 44.h,
|
height: 44.h,
|
||||||
margin: const EdgeInsets.only(left: 6),
|
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,
|
height: 36.h,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
state.users[index].userName.length >= 3
|
state.users[index].userName.length > 3
|
||||||
? state.users[index].userName.substring(1,state.users[index].userName.length)
|
? state.users[index].userName.substring(state.users[index].userName.length - 2,state.users[index].userName.length)
|
||||||
: state.users[index].userName,
|
: state.users[index].userName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
|
|
@ -919,6 +932,7 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
SizedBox(width: 8.w),
|
SizedBox(width: 8.w),
|
||||||
Visibility(
|
Visibility(
|
||||||
|
visible: state.users[index].roleId == "1" || state.users[index].roleId == "3" ? true : false,
|
||||||
child: Text(
|
child: Text(
|
||||||
'主持人',
|
'主持人',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|
@ -926,7 +940,16 @@ class MeetingMainPage extends StatelessWidget {
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: ColorUtil.Color_2_177_136),
|
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,
|
pageSnapping: true,
|
||||||
onPageChanged: (index) {
|
onPageChanged: (index) {
|
||||||
// 监听事件
|
// 监听事件
|
||||||
debugPrint('index=====$index');
|
debugPrint('wgs输出===:$index');
|
||||||
},
|
},
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import 'meeting_main_voice_logic.dart';
|
||||||
import 'meeting_main_voice_state.dart';
|
import 'meeting_main_voice_state.dart';
|
||||||
|
|
||||||
class MeetingMainVoiceComponent extends StatelessWidget {
|
class MeetingMainVoiceComponent extends StatelessWidget {
|
||||||
MeetingMainVoiceComponent({Key? key, required this.users}) : super(key: key);
|
MeetingMainVoiceComponent({super.key, required this.users});
|
||||||
|
|
||||||
final List<MeetingRoomUser> users;
|
final List<MeetingRoomUser> users;
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ class MeetingMainVoiceComponent extends StatelessWidget {
|
||||||
behavior: CusBehavior(),
|
behavior: CusBehavior(),
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
gridDelegate:
|
gridDelegate:
|
||||||
SliverGridDelegateWithFixedCrossAxisCount(
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
childAspectRatio: 0.7,
|
childAspectRatio: 0.7,
|
||||||
crossAxisSpacing: 20),
|
crossAxisSpacing: 20),
|
||||||
|
|
@ -44,8 +44,8 @@ class MeetingMainVoiceComponent extends StatelessWidget {
|
||||||
height: 76.h,
|
height: 76.h,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
users[index].userName.length >= 3
|
users[index].userName.length > 3
|
||||||
? users[index].userName.substring(1,users[index].userName.length)
|
? users[index].userName.substring(users[index].userName.length - 2,users[index].userName.length)
|
||||||
: users[index].userName,
|
: users[index].userName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22.sp,
|
fontSize: 22.sp,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String
|
||||||
// placeholder: (context, url) => const CircularProgressIndicator(),
|
// placeholder: (context, url) => const CircularProgressIndicator(),
|
||||||
placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
|
placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
|
||||||
errorListener: (e) {
|
errorListener: (e) {
|
||||||
debugPrint('图片报错.............$e');
|
debugPrint('wgs输出===:图片报错.............$e');
|
||||||
},
|
},
|
||||||
errorWidget: (context, url, error) {
|
errorWidget: (context, url, error) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,10 @@ class DeviceInfo {
|
||||||
return 'error';
|
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