|
|
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|
|
|
|
import 'dart:developer';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
|
import 'package:test_sa/main.dart';
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/call/audio_call_page.dart';
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/call/video_call_page.dart';
|
|
|
|
|
@ -11,6 +12,7 @@ import 'package:test_sa/modules/cx_module/chat/call/services/callkit_service.dar
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/call/call_error_handler.dart';
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/model/call_session.dart';
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/services/signalr_service.dart';
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/chat_provider.dart';
|
|
|
|
|
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
|
|
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
|
|
|
|
|
|
@ -46,6 +48,8 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
bool _handlersRegistered = false;
|
|
|
|
|
|
|
|
|
|
// Module context (for SignalR invocations)
|
|
|
|
|
String? _userId;
|
|
|
|
|
String? _authToken;
|
|
|
|
|
String? _moduleId;
|
|
|
|
|
String? _referenceId;
|
|
|
|
|
String? _conversationId;
|
|
|
|
|
@ -79,6 +83,8 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
log(' Conversation ID: ${conversationId ?? "none"}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Store module context
|
|
|
|
|
_userId = userId;
|
|
|
|
|
_authToken = authToken;
|
|
|
|
|
_moduleId = moduleId;
|
|
|
|
|
_referenceId = referenceId;
|
|
|
|
|
_conversationId = conversationId;
|
|
|
|
|
@ -155,6 +161,15 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
|
|
|
|
|
_handlersRegistered = true;
|
|
|
|
|
log('✅ [HANDLERS] All call handlers registered', name: 'CallManager');
|
|
|
|
|
log(' - OnIncomingCallAsync', name: 'CallManager');
|
|
|
|
|
log(' - OnCallAcceptedAsync ⭐', name: 'CallManager');
|
|
|
|
|
log(' - OnCallDeclinedAsync', name: 'CallManager');
|
|
|
|
|
log(' - OnHangUpAsync ⭐', name: 'CallManager');
|
|
|
|
|
log(' - OnOfferAsync ⭐', name: 'CallManager');
|
|
|
|
|
log(' - OnAnswerOfferAsync ⭐', name: 'CallManager');
|
|
|
|
|
log(' - OnIceCandidateAsync ⭐', name: 'CallManager');
|
|
|
|
|
log(' - OnAudioToggle', name: 'CallManager');
|
|
|
|
|
log(' - OnCameraToggle', name: 'CallManager');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Start outgoing call
|
|
|
|
|
@ -168,16 +183,31 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('📞 [OUTGOING] Starting ${callType.name} call to $peerName ($peerId)', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Check if already in call
|
|
|
|
|
if (_callStatus != CallStatus.idle) {
|
|
|
|
|
log('⚠️ [OUTGOING] Already in a call - CALLER_BUSY', name: 'CallManager');
|
|
|
|
|
// CRITICAL: Verify CallManager is initialized
|
|
|
|
|
if (_userId == null || _authToken == null) {
|
|
|
|
|
log('❌ [OUTGOING] CallManager not initialized!', name: 'CallManager');
|
|
|
|
|
log(' _userId: ${_userId ?? "NULL"}', name: 'CallManager');
|
|
|
|
|
log(' _authToken: ${_authToken != null ? "SET" : "NULL"}', name: 'CallManager');
|
|
|
|
|
final context = navigatorKey.currentContext;
|
|
|
|
|
if (context != null) {
|
|
|
|
|
CallErrorHandler.showCallAlreadyInProgress(context);
|
|
|
|
|
CallErrorHandler.showGenericError(context, 'Call system not ready. Please try again.');
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CRITICAL: Verify handlers are registered
|
|
|
|
|
if (!_handlersRegistered) {
|
|
|
|
|
log('❌ [OUTGOING] Call handlers not registered!', name: 'CallManager');
|
|
|
|
|
log(' Attempting to register handlers now...', name: 'CallManager');
|
|
|
|
|
_registerCallHandlers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log('✅ [OUTGOING] CallManager initialized', name: 'CallManager');
|
|
|
|
|
log(' User ID: $_userId', name: 'CallManager');
|
|
|
|
|
log(' Employee Number: $_myEmployeeNumber', name: 'CallManager');
|
|
|
|
|
log(' Handlers registered: $_handlersRegistered', name: 'CallManager');
|
|
|
|
|
log(' SignalR state: ${_signalRService.connectionState}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Update status
|
|
|
|
|
_updateCallStatus(CallStatus.checkingPermissions);
|
|
|
|
|
log('🔍 [OUTGOING] Checking permissions...', name: 'CallManager');
|
|
|
|
|
@ -290,6 +320,51 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
log(' Caller: $callerName ($callerId)', name: 'CallManager');
|
|
|
|
|
log(' Video: $isVideoCall', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// CRITICAL FIX: Auto-initialize CallManager if not already initialized
|
|
|
|
|
if (_userId == null || _authToken == null) {
|
|
|
|
|
log('⚠️ [INCOMING] CallManager not initialized - attempting auto-initialization...', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Try to get credentials from ChatProvider
|
|
|
|
|
try {
|
|
|
|
|
final chatProvider = Provider.of<ChatProvider>(navigatorKey.currentContext!, listen: false);
|
|
|
|
|
final chatLoginResponse = chatProvider.chatLoginResponse;
|
|
|
|
|
final sender = chatProvider.sender; // Get sender for employee number
|
|
|
|
|
|
|
|
|
|
if (chatLoginResponse != null) {
|
|
|
|
|
log('🔧 [INCOMING] Found credentials in ChatProvider', name: 'CallManager');
|
|
|
|
|
log(' User ID: ${chatLoginResponse.userId}', name: 'CallManager');
|
|
|
|
|
log(' Employee Number: ${sender?.employeeNumber ?? "NOT AVAILABLE"}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
await initialize(
|
|
|
|
|
userId: chatLoginResponse.userId.toString(),
|
|
|
|
|
authToken: chatLoginResponse.token ?? '',
|
|
|
|
|
conversationId: extraData?['conversationId'] as String?,
|
|
|
|
|
moduleId: extraData?['moduleId'] as String?,
|
|
|
|
|
referenceId: extraData?['referenceId'] as String?,
|
|
|
|
|
employeeNumber: sender?.employeeNumber, // Get from sender participant
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
log('✅ [INCOMING] Auto-initialization successful', name: 'CallManager');
|
|
|
|
|
} else {
|
|
|
|
|
log('❌ [INCOMING] No credentials available in ChatProvider', name: 'CallManager');
|
|
|
|
|
// Show error to user
|
|
|
|
|
final context = navigatorKey.currentContext;
|
|
|
|
|
if (context != null) {
|
|
|
|
|
CallErrorHandler.showGenericError(context, 'Unable to receive call. Please login to chat first.');
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log('❌ [INCOMING] Auto-initialization failed: $e', name: 'CallManager');
|
|
|
|
|
// Show error to user
|
|
|
|
|
final context = navigatorKey.currentContext;
|
|
|
|
|
if (context != null) {
|
|
|
|
|
CallErrorHandler.showGenericError(context, 'Unable to receive call. Please open chat first.');
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if already in call
|
|
|
|
|
if (_callStatus != CallStatus.idle) {
|
|
|
|
|
log('⚠️ [INCOMING] Already in a call - declining with CALLER_BUSY', name: 'CallManager');
|
|
|
|
|
@ -313,6 +388,10 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
log('🔌 [INCOMING] Ensuring SignalR connection...', name: 'CallManager');
|
|
|
|
|
if (!await _signalRService.ensureConnected()) {
|
|
|
|
|
log('❌ [INCOMING] SignalR connection failed - cannot handle call', name: 'CallManager');
|
|
|
|
|
final context = navigatorKey.currentContext;
|
|
|
|
|
if (context != null) {
|
|
|
|
|
CallErrorHandler.showSignalRNotConnected(context);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
log('✅ [INCOMING] SignalR connected', name: 'CallManager');
|
|
|
|
|
@ -394,12 +473,22 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
try {
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('✅ [ACCEPT] Accepting call...', name: 'CallManager');
|
|
|
|
|
log('🔍 [ACCEPT] CODE VERSION: 2026-07-19-v3-DEBUG', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (_currentCall == null || _callStatus != CallStatus.incomingRinging) {
|
|
|
|
|
log('⚠️ [ACCEPT] No incoming call to accept', name: 'CallManager');
|
|
|
|
|
log(' Current call: ${_currentCall?.callId}', name: 'CallManager');
|
|
|
|
|
log(' Current status: ${_callStatus.name}', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log('📊 [ACCEPT] Current call state:', name: 'CallManager');
|
|
|
|
|
log(' Call ID: ${_currentCall!.callId}', name: 'CallManager');
|
|
|
|
|
log(' Peer ID: ${_currentCall!.peerId}', name: 'CallManager');
|
|
|
|
|
log(' Peer Name: ${_currentCall!.peerName}', name: 'CallManager');
|
|
|
|
|
log(' Call Type: ${_currentCall!.type.name}', name: 'CallManager');
|
|
|
|
|
log(' Direction: ${_currentCall!.direction.name}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Check permissions
|
|
|
|
|
log('🔍 [ACCEPT] Checking permissions...', name: 'CallManager');
|
|
|
|
|
if (!await _checkPermissions(_currentCall!.type)) {
|
|
|
|
|
@ -411,20 +500,31 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
|
|
|
|
|
// Ensure SignalR connected
|
|
|
|
|
log('🔌 [ACCEPT] Ensuring SignalR connection...', name: 'CallManager');
|
|
|
|
|
log(' Current SignalR state: ${_signalRService.connectionState}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (!await _signalRService.ensureConnected()) {
|
|
|
|
|
log('❌ [ACCEPT] SignalR not connected', name: 'CallManager');
|
|
|
|
|
log('❌ [ACCEPT] SignalR connection failed', name: 'CallManager');
|
|
|
|
|
log(' Final state: ${_signalRService.connectionState}', name: 'CallManager');
|
|
|
|
|
_cleanup();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
log('✅ [ACCEPT] SignalR connected', name: 'CallManager');
|
|
|
|
|
log(' SignalR state: ${_signalRService.connectionState}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
_updateCallStatus(CallStatus.connecting);
|
|
|
|
|
|
|
|
|
|
// Cancel timeout timer
|
|
|
|
|
_callTimeoutTimer?.cancel();
|
|
|
|
|
log('✅ [ACCEPT] Timeout timer cancelled', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Invoke AnswerCallAsync
|
|
|
|
|
log('📤 [ACCEPT] Invoking AnswerCallAsync...', name: 'CallManager');
|
|
|
|
|
log(' My Employee Number: ${_myEmployeeNumber ?? "NULL"}', name: 'CallManager');
|
|
|
|
|
log(' Peer ID: ${_currentCall!.peerId}', name: 'CallManager');
|
|
|
|
|
log(' Module ID: ${_moduleId ?? "0"}', name: 'CallManager');
|
|
|
|
|
log(' Reference ID: ${_referenceId ?? "0"}', name: 'CallManager');
|
|
|
|
|
log(' Conversation ID: ${_conversationId ?? ""}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
await _signalRService.invoke('AnswerCallAsync', args: [
|
|
|
|
|
_myEmployeeNumber ?? '',
|
|
|
|
|
_currentCall!.peerId,
|
|
|
|
|
@ -432,18 +532,31 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
_referenceId ?? '0',
|
|
|
|
|
_conversationId ?? '',
|
|
|
|
|
]);
|
|
|
|
|
log('✅ [ACCEPT] AnswerCallAsync invoked', name: 'CallManager');
|
|
|
|
|
log('✅ [ACCEPT] AnswerCallAsync invoked successfully', name: 'CallManager');
|
|
|
|
|
log(' Waiting for backend to process...', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Initialize WebRTC
|
|
|
|
|
log('🔧 [ACCEPT] Initializing WebRTC...', name: 'CallManager');
|
|
|
|
|
log(' Call type: ${_currentCall!.type.name}', name: 'CallManager');
|
|
|
|
|
await _initializeWebRTC();
|
|
|
|
|
log('✅ [ACCEPT] WebRTC initialized', name: 'CallManager');
|
|
|
|
|
log(' WebRTC service: ${_webrtcService != null ? "Created" : "NULL"}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Navigate to call screen
|
|
|
|
|
log('🧭 [ACCEPT] Navigating to call screen...', name: 'CallManager');
|
|
|
|
|
_navigateToCallScreen();
|
|
|
|
|
log('✅ [ACCEPT] Navigated to call screen', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
log('✅ [ACCEPT] Call accepted successfully', name: 'CallManager');
|
|
|
|
|
log('ℹ️ [ACCEPT] Now waiting for caller to send offer via OnOfferAsync...', name: 'CallManager');
|
|
|
|
|
log('ℹ️ [ACCEPT] Expected sequence:', name: 'CallManager');
|
|
|
|
|
log(' 1. Caller receives OnCallAcceptedAsync', name: 'CallManager');
|
|
|
|
|
log(' 2. Caller creates offer and sends OfferAsync', name: 'CallManager');
|
|
|
|
|
log(' 3. We receive OnOfferAsync', name: 'CallManager');
|
|
|
|
|
log(' 4. We create answer and send AnswerOfferAsync', name: 'CallManager');
|
|
|
|
|
log(' 5. Caller receives OnAnswerOfferAsync', name: 'CallManager');
|
|
|
|
|
log(' 6. ICE candidates exchange', name: 'CallManager');
|
|
|
|
|
log(' 7. Connection established', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
@ -762,8 +875,11 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
// ==================== SignalR Event Handlers ====================
|
|
|
|
|
|
|
|
|
|
void _handleIncomingCall(List<Object?>? args) {
|
|
|
|
|
log('📞 [EVENT] OnIncomingCallAsync received', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] ⚠️ OnIncomingCallAsync received', name: 'CallManager');
|
|
|
|
|
log(' Args count: ${args?.length ?? 0}', name: 'CallManager');
|
|
|
|
|
log(' Args: $args', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Note: Incoming calls are now handled by CallNotificationService
|
|
|
|
|
// This event is kept for chat screen integration
|
|
|
|
|
@ -771,30 +887,71 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleCallAccepted(List<Object?>? args) async {
|
|
|
|
|
log('📞 [EVENT] OnCallAcceptedAsync received', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] ✅ OnCallAcceptedAsync received', name: 'CallManager');
|
|
|
|
|
log(' Args count: ${args?.length ?? 0}', name: 'CallManager');
|
|
|
|
|
log(' Args: $args', name: 'CallManager');
|
|
|
|
|
log(' Current status: ${_callStatus.name}', name: 'CallManager');
|
|
|
|
|
log(' Current call: ${_currentCall?.callId}', name: 'CallManager');
|
|
|
|
|
log(' Call direction: ${_currentCall?.direction.name}', name: 'CallManager');
|
|
|
|
|
log(' Peer: ${_currentCall?.peerId}', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (_callStatus != CallStatus.outgoingRinging) {
|
|
|
|
|
log('⚠️ [EVENT] Not in outgoing ringing state, ignoring', name: 'CallManager');
|
|
|
|
|
// CRITICAL: Only the CALLER should create an offer
|
|
|
|
|
// The RECEIVER should wait for OnOfferAsync
|
|
|
|
|
if (_currentCall?.direction != CallDirection.outgoing) {
|
|
|
|
|
log('⚠️ [EVENT] This is an INCOMING call - receiver should NOT create offer', name: 'CallManager');
|
|
|
|
|
log(' Current direction: ${_currentCall?.direction.name}', name: 'CallManager');
|
|
|
|
|
log(' ✅ Receiver will wait for OnOfferAsync from caller', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXED: Accept event in both connecting and outgoingRinging states
|
|
|
|
|
// Backend might send OnCallAcceptedAsync very quickly before status changes to outgoingRinging
|
|
|
|
|
if (_callStatus != CallStatus.connecting && _callStatus != CallStatus.outgoingRinging) {
|
|
|
|
|
log('⚠️ [EVENT] Not in valid state for creating offer (current: ${_callStatus.name})', name: 'CallManager');
|
|
|
|
|
log(' Expected: connecting OR outgoingRinging', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we're still in connecting, update to outgoingRinging first
|
|
|
|
|
if (_callStatus == CallStatus.connecting) {
|
|
|
|
|
log('🔄 [EVENT] Status was connecting, updating to outgoingRinging first', name: 'CallManager');
|
|
|
|
|
_updateCallStatus(CallStatus.outgoingRinging);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_callTimeoutTimer?.cancel();
|
|
|
|
|
_updateCallStatus(CallStatus.connecting);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Create and send offer
|
|
|
|
|
log('🔧 [EVENT] Creating SDP offer...', name: 'CallManager');
|
|
|
|
|
// Ensure WebRTC is initialized before creating offer
|
|
|
|
|
if (_webrtcService == null) {
|
|
|
|
|
log('⚠️ [EVENT] WebRTC not initialized yet, waiting...', name: 'CallManager');
|
|
|
|
|
// Wait a bit for WebRTC to initialize (it should be initializing in parallel)
|
|
|
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
|
|
|
|
|
|
|
|
|
if (_webrtcService == null) {
|
|
|
|
|
log('❌ [EVENT] WebRTC still not initialized after wait', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CALLER SIDE: Create and send offer
|
|
|
|
|
log('🔧 [EVENT] [CALLER] Creating SDP offer...', name: 'CallManager');
|
|
|
|
|
final offer = await _webrtcService!.createOffer();
|
|
|
|
|
log('✅ [EVENT] SDP offer created', name: 'CallManager');
|
|
|
|
|
log('✅ [EVENT] [CALLER] SDP offer created (length: ${offer.sdp?.length ?? 0})', name: 'CallManager');
|
|
|
|
|
log(' Offer SDP type: ${offer.type}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
log('📤 [EVENT] [CALLER] Sending offer via OfferAsync...', name: 'CallManager');
|
|
|
|
|
log(' To peer: ${_currentCall!.peerId}', name: 'CallManager');
|
|
|
|
|
log(' Call ID: ${_currentCall!.callId}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
log('📤 [EVENT] Sending offer via OfferAsync...', name: 'CallManager');
|
|
|
|
|
await _signalRService.invoke('OfferAsync', args: [
|
|
|
|
|
_currentCall!.peerId,
|
|
|
|
|
offer.sdp ?? '',
|
|
|
|
|
_currentCall!.callId,
|
|
|
|
|
]);
|
|
|
|
|
log('✅ [EVENT] Offer sent successfully', name: 'CallManager');
|
|
|
|
|
log('✅ [EVENT] [CALLER] Offer sent successfully via OfferAsync', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
log('❌ [EVENT] Error handling call accepted: $e',
|
|
|
|
|
@ -804,8 +961,11 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleCallDeclined(List<Object?>? args) {
|
|
|
|
|
log('📞 [EVENT] OnCallDeclinedAsync received', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] ❌ OnCallDeclinedAsync received', name: 'CallManager');
|
|
|
|
|
log(' Args count: ${args?.length ?? 0}', name: 'CallManager');
|
|
|
|
|
log(' Args: $args', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
// Extract decline reason if available
|
|
|
|
|
String? reason;
|
|
|
|
|
@ -828,13 +988,24 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleHangUp(List<Object?>? args) {
|
|
|
|
|
log('📞 [EVENT] OnHangUpAsync received', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] 🔴 OnHangUpAsync received', name: 'CallManager');
|
|
|
|
|
log(' Args count: ${args?.length ?? 0}', name: 'CallManager');
|
|
|
|
|
log(' Args: $args', name: 'CallManager');
|
|
|
|
|
log(' Current call status: ${_callStatus.name}', name: 'CallManager');
|
|
|
|
|
log(' Current call ID: ${_currentCall?.callId}', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
_cleanup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleOffer(List<Object?>? args) async {
|
|
|
|
|
log('📞 [EVENT] OnOfferAsync received', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] 📥 OnOfferAsync received', name: 'CallManager');
|
|
|
|
|
log(' Args count: ${args?.length ?? 0}', name: 'CallManager');
|
|
|
|
|
log(' Current status: ${_callStatus.name}', name: 'CallManager');
|
|
|
|
|
log(' WebRTC initialized: ${_webrtcService != null}', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (args == null || args.isEmpty) {
|
|
|
|
|
log('⚠️ [EVENT] No offer data received', name: 'CallManager');
|
|
|
|
|
@ -843,11 +1014,13 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final offerSdp = args[0] as String?;
|
|
|
|
|
if (offerSdp == null) {
|
|
|
|
|
log('⚠️ [EVENT] Offer SDP is null', name: 'CallManager');
|
|
|
|
|
if (offerSdp == null || offerSdp.isEmpty) {
|
|
|
|
|
log('⚠️ [EVENT] Offer SDP is null or empty', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log(' Offer SDP length: ${offerSdp.length}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (_webrtcService == null) {
|
|
|
|
|
log('⚠️ [EVENT] WebRTC service not initialized', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
@ -855,15 +1028,19 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
|
|
|
|
|
log('🔧 [EVENT] Creating SDP answer...', name: 'CallManager');
|
|
|
|
|
final answer = await _webrtcService!.createAnswer(offerSdp);
|
|
|
|
|
log('✅ [EVENT] SDP answer created', name: 'CallManager');
|
|
|
|
|
log('✅ [EVENT] SDP answer created (length: ${answer.sdp?.length ?? 0})', name: 'CallManager');
|
|
|
|
|
log(' Answer SDP type: ${answer.type}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
log('📤 [EVENT] Sending answer via AnswerOfferAsync...', name: 'CallManager');
|
|
|
|
|
log(' To peer: ${_currentCall!.peerId}', name: 'CallManager');
|
|
|
|
|
log(' Call ID: ${_currentCall!.callId}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
await _signalRService.invoke('AnswerOfferAsync', args: [
|
|
|
|
|
_currentCall!.peerId,
|
|
|
|
|
answer.sdp ?? '',
|
|
|
|
|
_currentCall!.callId,
|
|
|
|
|
]);
|
|
|
|
|
log('✅ [EVENT] Answer sent successfully', name: 'CallManager');
|
|
|
|
|
log('✅ [EVENT] Answer sent successfully via AnswerOfferAsync', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
log('❌ [EVENT] Error handling offer: $e',
|
|
|
|
|
@ -872,7 +1049,12 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleAnswer(List<Object?>? args) async {
|
|
|
|
|
log('📞 [EVENT] OnAnswerOfferAsync received', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] 📥 OnAnswerOfferAsync received', name: 'CallManager');
|
|
|
|
|
log(' Args count: ${args?.length ?? 0}', name: 'CallManager');
|
|
|
|
|
log(' Current status: ${_callStatus.name}', name: 'CallManager');
|
|
|
|
|
log(' WebRTC initialized: ${_webrtcService != null}', name: 'CallManager');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (args == null || args.isEmpty) {
|
|
|
|
|
log('⚠️ [EVENT] No answer data received', name: 'CallManager');
|
|
|
|
|
@ -881,11 +1063,13 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final answerSdp = args[0] as String?;
|
|
|
|
|
if (answerSdp == null) {
|
|
|
|
|
log('⚠️ [EVENT] Answer SDP is null', name: 'CallManager');
|
|
|
|
|
if (answerSdp == null || answerSdp.isEmpty) {
|
|
|
|
|
log('⚠️ [EVENT] Answer SDP is null or empty', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log(' Answer SDP length: ${answerSdp.length}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (_webrtcService == null) {
|
|
|
|
|
log('⚠️ [EVENT] WebRTC service not initialized', name: 'CallManager');
|
|
|
|
|
return;
|
|
|
|
|
@ -894,6 +1078,7 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
log('🔧 [EVENT] Setting remote answer...', name: 'CallManager');
|
|
|
|
|
await _webrtcService!.setRemoteAnswer(answerSdp);
|
|
|
|
|
log('✅ [EVENT] Remote answer set successfully', name: 'CallManager');
|
|
|
|
|
log(' Now waiting for ICE connection to establish...', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
log('❌ [EVENT] Error handling answer: $e',
|
|
|
|
|
@ -902,7 +1087,7 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleIceCandidate(List<Object?>? args) async {
|
|
|
|
|
log('📞 [EVENT] OnIceCandidateAsync received', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] 🧊 OnIceCandidateAsync received', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
if (args == null || args.isEmpty) {
|
|
|
|
|
log('⚠️ [EVENT] No ICE candidate data received', name: 'CallManager');
|
|
|
|
|
@ -929,6 +1114,7 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
log('🧊 [EVENT] Adding remote ICE candidate...', name: 'CallManager');
|
|
|
|
|
log(' Candidate: ${candidate.candidate?.substring(0, 50)}...', name: 'CallManager');
|
|
|
|
|
await _webrtcService!.addIceCandidate(candidate);
|
|
|
|
|
log('✅ [EVENT] Remote ICE candidate added', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
@ -939,7 +1125,7 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleAudioToggle(List<Object?>? args) {
|
|
|
|
|
log('📞 [EVENT] OnAudioToggle received', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] 🎤 OnAudioToggle received', name: 'CallManager');
|
|
|
|
|
log(' Peer muted: $_isPeerMuted -> ${!_isPeerMuted}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
_isPeerMuted = !_isPeerMuted;
|
|
|
|
|
@ -947,7 +1133,7 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleCameraToggle(List<Object?>? args) {
|
|
|
|
|
log('📞 [EVENT] OnCameraToggle received', name: 'CallManager');
|
|
|
|
|
log('📞 [EVENT] 📹 OnCameraToggle received', name: 'CallManager');
|
|
|
|
|
log(' Peer camera: $_isPeerCameraOn -> ${!_isPeerCameraOn}', name: 'CallManager');
|
|
|
|
|
|
|
|
|
|
_isPeerCameraOn = !_isPeerCameraOn;
|
|
|
|
|
@ -992,4 +1178,3 @@ class CallManager extends ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|