working on background call handling

ui_ux_rollout_merge_audio_video_call
WaseemAbbasi22 3 weeks ago
parent b8595cf796
commit d5a21a05ca

@ -0,0 +1,237 @@
import 'dart:developer';
import 'package:test_sa/core/di/service_locator.dart';
import 'package:test_sa/modules/cx_module/chat/services/call_manager.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:test_sa/modules/cx_module/chat/model/chat_login_response_model.dart';
import 'package:test_sa/modules/cx_module/chat/model/chat_participant_model.dart';
import 'package:test_sa/core/storage/auth_storage.dart';
class CallCoordinator {
static final CallCoordinator _instance = CallCoordinator._internal();
factory CallCoordinator() => _instance;
CallCoordinator._internal();
// Services from DI
CallManager get _callManager => getIt<CallManager>();
SignalRService get _signalRService => getIt<SignalRService>();
bool _isInitializing = false;
// Cache for silent login (used for incoming calls in background)
static ChatLoginResponse? _cachedLoginResponse;
static ChatParticipantModel? _cachedParticipants;
static String? _cachedMyEmployeeNumber;
Future<bool> initializeForOutgoingCall({
required ChatProvider chatProvider,
required String conversationId,
required String moduleId,
required String referenceId,
}) async {
log('═══════════════════════════════════════════', name: 'CallCoordinator');
log('📞 [OUTGOING] Initializing communication for outgoing call', name: 'CallCoordinator');
try {
// Step 1: Ensure chat login credentials exist
log('🔐 [OUTGOING] Step 1: Checking chat login credentials...', name: 'CallCoordinator');
if (chatProvider.chatLoginResponse == null) {
log('❌ [OUTGOING] Chat login credentials not found', name: 'CallCoordinator');
log(' User must open chat screen first to initialize credentials', name: 'CallCoordinator');
return false;
}
log('✅ [OUTGOING] Chat login credentials available', name: 'CallCoordinator');
// Step 2: Ensure participants loaded
log('👥 [OUTGOING] Step 2: Checking participants...', name: 'CallCoordinator');
if (chatProvider.sender == null || chatProvider.recipient == null) {
log('❌ [OUTGOING] Participants not loaded (sender: ${chatProvider.sender != null}, recipient: ${chatProvider.recipient != null})', name: 'CallCoordinator');
return false;
}
log('✅ [OUTGOING] Participants loaded', name: 'CallCoordinator');
// Step 3: Initialize CallManager with credentials
log('🔧 [OUTGOING] Step 3: Initializing CallManager...', name: 'CallCoordinator');
final userId = chatProvider.chatLoginResponse!.userId.toString();
final authToken = chatProvider.chatLoginResponse!.token ?? '';
// Use employeeNumber from sender participant in ChatParticipantModel
final employeeNumber = chatProvider.sender?.employeeNumber;
log(' Sender userId: ${chatProvider.sender?.userId}', name: 'CallCoordinator');
log(' Sender userName: ${chatProvider.sender?.userName}', name: 'CallCoordinator');
log(' Sender employeeNumber: ${chatProvider.sender?.employeeNumber}', name: 'CallCoordinator');
log(' Using employeeNumber for CallUserAsync: $employeeNumber', name: 'CallCoordinator');
await _callManager.initialize(
userId: userId,
authToken: authToken,
conversationId: conversationId,
moduleId: moduleId,
referenceId: referenceId,
employeeNumber: employeeNumber,
);
log('✅ [OUTGOING] Communication initialized successfully', name: 'CallCoordinator');
log('═══════════════════════════════════════════', name: 'CallCoordinator');
return true;
} catch (e, stackTrace) {
log('❌ [OUTGOING] Initialization failed: $e',
name: 'CallCoordinator', error: e, stackTrace: stackTrace);
log('═══════════════════════════════════════════', name: 'CallCoordinator');
return false;
}
}
Future<bool> initializeForIncomingCall({
required String callerEmployeeNumber,
String? conversationId,
String? moduleId,
String? referenceId,
}) async {
log('═══════════════════════════════════════════', name: 'CallCoordinator');
log('📞 [INCOMING] Initializing communication for incoming call', name: 'CallCoordinator');
log(' Caller: $callerEmployeeNumber', name: 'CallCoordinator');
log(' Module ID: $moduleId', name: 'CallCoordinator');
// Prevent concurrent initialization
if (_isInitializing) {
log('⏳ [INCOMING] Already initializing, waiting...', name: 'CallCoordinator');
await Future.delayed(const Duration(milliseconds: 500));
return _signalRService.isConnected;
}
_isInitializing = true;
try {
String? userId;
String? authToken;
String? myEmployeeNumber;
// STEP 1: Try to read credentials from SharedPreferences (CRITICAL for background/terminated)
log('🔐 [INCOMING] Step 1: Reading credentials from SharedPreferences...', name: 'CallCoordinator');
final storedCredentials = await AuthStorage.getCredentials();
if (storedCredentials != null) {
// SUCCESS: Found stored credentials
userId = storedCredentials.userId;
authToken = storedCredentials.accessToken;
myEmployeeNumber = storedCredentials.employeeNumber;
log('✅ [INCOMING] Using stored credentials from SharedPreferences', name: 'CallCoordinator');
log(' User ID: $userId', name: 'CallCoordinator');
log(' Employee Number: ${myEmployeeNumber ?? "none"}', name: 'CallCoordinator');
log(' Token length: ${authToken.length} chars', name: 'CallCoordinator');
} else {
// Fall back to memory cache (for foreground calls)
log('⚠️ [INCOMING] No stored credentials in SharedPreferences', name: 'CallCoordinator');
log(' Checking memory cache...', name: 'CallCoordinator');
if (_cachedLoginResponse != null && _cachedMyEmployeeNumber != null) {
userId = _cachedLoginResponse!.userId.toString();
authToken = _cachedLoginResponse!.token ?? '';
myEmployeeNumber = _cachedMyEmployeeNumber;
log('✅ [INCOMING] Using cached credentials from memory', name: 'CallCoordinator');
log(' User ID: $userId', name: 'CallCoordinator');
log(' Employee Number: $myEmployeeNumber', name: 'CallCoordinator');
} else {
log('❌ [INCOMING] No credentials available anywhere', name: 'CallCoordinator');
log(' Cannot initialize SignalR without credentials', name: 'CallCoordinator');
log(' User must be logged in first', name: 'CallCoordinator');
log('═══════════════════════════════════════════', name: 'CallCoordinator');
return false;
}
}
// STEP 2: Initialize SignalR with stored credentials (if not already connected)
if (!_signalRService.isConnected) {
log('🔌 [INCOMING] Step 2: Initializing SignalR connection...', name: 'CallCoordinator');
final signalRConnected = await _signalRService.initialize(
userId: userId!,
authToken: authToken!,
conversationId: conversationId,
);
if (!signalRConnected) {
log('❌ [INCOMING] SignalR connection failed', name: 'CallCoordinator');
log('═══════════════════════════════════════════', name: 'CallCoordinator');
return false;
}
log('✅ [INCOMING] SignalR connected successfully', name: 'CallCoordinator');
log(' Connection ID: ${_signalRService.connectionId}', name: 'CallCoordinator');
} else {
log('✅ [INCOMING] SignalR already connected', name: 'CallCoordinator');
log(' Connection ID: ${_signalRService.connectionId}', name: 'CallCoordinator');
}
// STEP 3: Initialize CallManager
log('🔧 [INCOMING] Step 3: Initializing CallManager...', name: 'CallCoordinator');
await _callManager.initialize(
userId: userId!,
authToken: authToken!,
conversationId: conversationId,
moduleId: moduleId ?? '1002', // Default to ATOMS
referenceId: referenceId ?? '0',
employeeNumber: myEmployeeNumber,
);
log('✅ [INCOMING] CallManager initialized successfully', name: 'CallCoordinator');
log('✅ [INCOMING] Communication initialization complete', name: 'CallCoordinator');
log(' ✅ SignalR: Connected', name: 'CallCoordinator');
log(' ✅ CallManager: Ready', name: 'CallCoordinator');
log(' ✅ Ready to receive incoming call', name: 'CallCoordinator');
log('═══════════════════════════════════════════', name: 'CallCoordinator');
return true;
} catch (e, stackTrace) {
log('❌ [INCOMING] Initialization failed: $e',
name: 'CallCoordinator', error: e, stackTrace: stackTrace);
log('═══════════════════════════════════════════', name: 'CallCoordinator');
return false;
} finally {
_isInitializing = false;
}
}
/// Cache chat credentials for future incoming calls
/// Should be called after successful chat login (from ChatProvider)
void cacheCredentials({
required ChatLoginResponse loginResponse,
required ChatParticipantModel participants,
required String myEmployeeNumber,
}) {
log('💾 [COORDINATOR] Caching credentials for future incoming calls', name: 'CallCoordinator');
log(' User ID: ${loginResponse.userId}', name: 'CallCoordinator');
log(' Employee Number: $myEmployeeNumber', name: 'CallCoordinator');
_cachedLoginResponse = loginResponse;
_cachedParticipants = participants;
_cachedMyEmployeeNumber = myEmployeeNumber;
log('✅ [COORDINATOR] Credentials cached successfully', name: 'CallCoordinator');
}
/// Ensure communication is ready
/// Used before any call operation
bool isCommunicationReady() {
final signalRReady = _signalRService.isConnected;
log('🔍 [COORDINATOR] Communication readiness check:', name: 'CallCoordinator');
log(' SignalR: ${signalRReady ? "✅ Connected" : "❌ Not connected"}', name: 'CallCoordinator');
log(' CallManager: ✅ Ready', name: 'CallCoordinator');
return signalRReady;
}
/// Reset communication (for logout)
Future<void> reset() async {
log('🔄 [COORDINATOR] Resetting communication...', name: 'CallCoordinator');
_isInitializing = false;
_cachedLoginResponse = null;
_cachedParticipants = null;
_cachedMyEmployeeNumber = null;
// Services will be reset via service_locator.resetServices()
log('✅ [COORDINATOR] Reset complete', name: 'CallCoordinator');
}
}
Loading…
Cancel
Save