Browse Source

feat(call): 添加通话权限检查和玫瑰不足处理

- 在创建RTC频道时添加麦克风和摄像头权限检查
- 实现语音通话需要麦克风权限,视频通话需要摄像头和麦克风权限
- 添加toUserId参数到createOneOnOneRtcChannel方法
- 处理玫瑰不足情况(E0002错误码)并显示充值弹框
- 修复音频通话类型参数错误(2为视频,3为音频)
- 在RtcChannelData模型中添加success和code字段
- 添加权限被永久拒绝时跳转系统设置功能
master
Jolie 2 months ago
parent
commit
87c11464ad
2 changed files with 90 additions and 12 deletions
  1. 94
      lib/controller/message/call_controller.dart
  2. 8
      lib/model/rtc/rtc_channel_data.dart

94
lib/controller/message/call_controller.dart

@ -133,7 +133,10 @@ class CallController extends GetxController {
/// RTC频道
/// [type] 12
Future<RtcChannelData?> createOneOnOneRtcChannel({required int type}) async {
Future<RtcChannelData?> createOneOnOneRtcChannel({
required int type,
required String toUserId,
}) async {
if (isCreatingChannel.value) {
print('⚠️ 正在创建频道,请稍候');
return null;
@ -145,12 +148,34 @@ class CallController extends GetxController {
return null;
}
//
final hasPermission = await _ensureCallPermissions(type);
if (!hasPermission) {
print('❌ [CallController] 权限检查失败,无法创建通话频道');
return null;
}
isCreatingChannel.value = true;
final response = await _networkService.rtcApi.createOneOnOneRtcChannel({
'type': type,
'toUserId': toUserId,
});
if (response.data.isSuccess && response.data.data != null) {
if (!response.data.data!.success && response.data.data!.code == 'E0002') {
// toast
SmartDialog.showToast('玫瑰不足请充值');
Get.log('❌ 送礼失败: ${response.data.data}');
// 使 addPostFrameCallback toast
WidgetsBinding.instance.addPostFrameCallback((_) {
SmartDialog.show(
alignment: Alignment.bottomCenter,
maskColor: Colors.black.withOpacity(0.5),
builder: (_) => const LiveRechargePopup(),
);
});
return null;
}
rtcChannel.value = response.data.data;
print('✅ 创建一对一RTC频道成功: ${response.data.data?.channelId}');
isCreatingChannel.value = false;
@ -166,16 +191,6 @@ class CallController extends GetxController {
}
}
///
Future<RtcChannelData?> createAudioChannel() {
return createOneOnOneRtcChannel(type: 2);
}
///
Future<RtcChannelData?> createVideoChannel() {
return createOneOnOneRtcChannel(type: 3);
}
///
/// [toUserId] ID
Future<List<ChatAudioProductModel>?> listChatAudioProduct(String toUserId) async {
@ -237,7 +252,10 @@ class CallController extends GetxController {
// RTC
final type = callType == CallType.video ? 3 : 2; // 12
final channelData = await createOneOnOneRtcChannel(type: type);
final channelData = await createOneOnOneRtcChannel(
type: type,
toUserId: targetUserId,
);
_callUid = channelData?.uid;
_callChannelId = channelData?.channelId;
if (channelData == null) {
@ -372,6 +390,19 @@ class CallController extends GetxController {
SmartDialog.showToast(connectResponse.data.message);
return false;
}
if (!connectResponse.data.data!['success'] && connectResponse.data.data!['code'] == 'E0002') {
// toast
SmartDialog.showToast('玫瑰不足请充值');
// 使 addPostFrameCallback toast
WidgetsBinding.instance.addPostFrameCallback((_) {
SmartDialog.show(
alignment: Alignment.bottomCenter,
maskColor: Colors.black.withOpacity(0.5),
builder: (_) => const LiveRechargePopup(),
);
});
return false;
}
print('✅ [CallController] 已调用连接一对一RTC频道接口,channelId: $channelId');
//
@ -639,6 +670,45 @@ class CallController extends GetxController {
print('✅ [CallController] 已加入 RTC 频道: $channelName');
}
///
/// [type] 2=3=
Future<bool> _ensureCallPermissions(int type) async {
if (type == 2) {
//
final micStatus = await Permission.microphone.request();
if (micStatus.isGranted) {
return true;
}
if (micStatus.isPermanentlyDenied) {
SmartDialog.showToast('请在系统设置中开启麦克风权限');
await openAppSettings();
} else {
SmartDialog.showToast('请允许麦克风权限以进行语音通话');
}
return false;
} else if (type == 3) {
//
final statuses = await [Permission.camera, Permission.microphone].request();
final allGranted = statuses.values.every((status) => status.isGranted);
if (allGranted) {
return true;
}
final permanentlyDenied = statuses.values.any(
(status) => status.isPermanentlyDenied,
);
if (permanentlyDenied) {
SmartDialog.showToast('请在系统设置中开启摄像头和麦克风权限');
await openAppSettings();
} else {
SmartDialog.showToast('请允许摄像头和麦克风权限以进行视频通话');
}
return false;
}
return false;
}
Future<bool> _ensureRtcPermissions() async {
final statuses = await [Permission.camera, Permission.microphone].request();
final allGranted = statuses.values.every((status) => status.isGranted);

8
lib/model/rtc/rtc_channel_data.dart

@ -3,11 +3,15 @@ class RtcChannelData {
final String channelId;
final String token;
final int uid;
final bool success;
final String code;
RtcChannelData({
required this.channelId,
required this.token,
required this.uid,
required this.success,
required this.code,
});
factory RtcChannelData.fromJson(Map<String, dynamic> json) {
@ -15,6 +19,8 @@ class RtcChannelData {
channelId: json['channelId']?.toString() ?? '',
token: json['token']?.toString() ?? '',
uid: json['uid'] ?? 0,
success: json['success'] as bool? ?? false,
code: json['code']?.toString() ?? '',
);
}
@ -23,6 +29,8 @@ class RtcChannelData {
'channelId': channelId,
'token': token,
'uid': uid,
'success': success,
'code': code,
};
}

Loading…
Cancel
Save