|
|
|
@ -133,7 +133,10 @@ class CallController extends GetxController { |
|
|
|
|
|
|
|
/// 创建一对一RTC频道 |
|
|
|
/// [type] 1为音频,2为视频 |
|
|
|
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; // 1为音频,2为视频 |
|
|
|
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); |
|
|
|
|