1.用户加入会议室

2.发言相关
This commit is contained in:
fuenmao 2024-11-28 09:51:54 +08:00
parent 3813d88d0c
commit 3a1dde6e19
16 changed files with 495 additions and 113 deletions

View File

@ -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}"

View File

@ -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>

View File

@ -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,
);
} }

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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);
}
} }

View File

@ -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;
/// 13
late RxInt communicationMode = 1.obs;
} }

View File

@ -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),
),
) )
], ],
), ),

View File

@ -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(

View File

@ -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,

View File

@ -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(

View File

@ -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;
}
} }

View File

@ -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: 电话权限被拒绝");
}
}
}

View File

@ -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: 电话权限被拒绝");
}
}
}

View File

@ -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("权限被拒绝,可能会导致相关功能不可用!");
}
}
}