From a6d39e5d5df215d5f249e7c5acd58f7d8ffb4d5f Mon Sep 17 00:00:00 2001 From: Florian Griffon Date: Tue, 8 Apr 2025 21:39:39 +0300 Subject: [PATCH] feat: fetch contact in makeGsmCall to show it in call --- dialer/lib/services/call_service.dart | 152 ++++++++++++-------------- 1 file changed, 70 insertions(+), 82 deletions(-) diff --git a/dialer/lib/services/call_service.dart b/dialer/lib/services/call_service.dart index e03c652..c7e41a6 100644 --- a/dialer/lib/services/call_service.dart +++ b/dialer/lib/services/call_service.dart @@ -2,124 +2,115 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../features/call/call_page.dart'; import '../features/call/incoming_call_page.dart'; +import '../services/contact_service.dart'; // Import your ContactService class CallService { static const MethodChannel _channel = MethodChannel('call_service'); static String? currentPhoneNumber; + static String? currentDisplayName; // Store display name + static Uint8List? currentThumbnail; // Store thumbnail static bool _isCallPageVisible = false; - static Map? _pendingCall; - static bool wasPhoneLocked = false; + final ContactService _contactService = ContactService(); // Instantiate ContactService static final GlobalKey navigatorKey = GlobalKey(); CallService() { _channel.setMethodCallHandler((call) async { - print('CallService: Handling method call: ${call.method}'); + final context = navigatorKey.currentContext; + print('CallService: Received method ${call.method} with args ${call.arguments}'); + if (context == null) { + print('CallService: Navigator context is null, cannot navigate'); + return; + } + switch (call.method) { case "callAdded": final phoneNumber = call.arguments["callId"] as String; final state = call.arguments["state"] as String; currentPhoneNumber = phoneNumber.replaceFirst('tel:', ''); + // Fetch contact info using ContactService if not already set + await _fetchContactInfo(currentPhoneNumber!); print('CallService: Call added, number: $currentPhoneNumber, state: $state'); if (state == "ringing") { - _handleIncomingCall(phoneNumber); + _navigateToIncomingCallPage(context); } else { - _navigateToCallPage(); + _navigateToCallPage(context); } break; case "callStateChanged": final state = call.arguments["state"] as String; - wasPhoneLocked = call.arguments["wasPhoneLocked"] as bool? ?? false; - print('CallService: State changed to $state, wasPhoneLocked: $wasPhoneLocked'); + print('CallService: State changed to $state'); if (state == "disconnected" || state == "disconnecting") { - _closeCallPage(); - if (wasPhoneLocked) { - _channel.invokeMethod("callEndedFromFlutter"); - } + _closeCallPage(context); } else if (state == "active" || state == "dialing") { - _navigateToCallPage(); + _navigateToCallPage(context); } else if (state == "ringing") { - final phoneNumber = call.arguments["callId"] as String; - _handleIncomingCall(phoneNumber.replaceFirst('tel:', '')); + _navigateToIncomingCallPage(context); } break; case "callEnded": case "callRemoved": - wasPhoneLocked = call.arguments["wasPhoneLocked"] as bool? ?? false; - print('CallService: Call ended/removed, wasPhoneLocked: $wasPhoneLocked'); - _closeCallPage(); - if (wasPhoneLocked) { - _channel.invokeMethod("callEndedFromFlutter"); - } + print('CallService: Call ended/removed'); + _closeCallPage(context); currentPhoneNumber = null; - break; - case "incomingCallFromNotification": - final phoneNumber = call.arguments["phoneNumber"] as String; - wasPhoneLocked = call.arguments["wasPhoneLocked"] as bool? ?? false; - currentPhoneNumber = phoneNumber; - print('CallService: Incoming call from notification: $phoneNumber, wasPhoneLocked: $wasPhoneLocked'); - _handleIncomingCall(phoneNumber); + currentDisplayName = null; + currentThumbnail = null; break; } }); } - void _handleIncomingCall(String phoneNumber) { - final context = navigatorKey.currentContext; - if (context == null) { - print('CallService: Context is null, queuing incoming call: $phoneNumber'); - _pendingCall = {"phoneNumber": phoneNumber}; - Future.delayed(Duration(milliseconds: 500), () => _checkPendingCall()); - } else { - _navigateToIncomingCallPage(context); - } - } - - void _checkPendingCall() { - if (_pendingCall != null) { - final context = navigatorKey.currentContext; - if (context != null) { - print('CallService: Processing queued call: ${_pendingCall!["phoneNumber"]}'); - currentPhoneNumber = _pendingCall!["phoneNumber"]; - _navigateToIncomingCallPage(context); - _pendingCall = null; - } else { - print('CallService: Context still null, retrying...'); - Future.delayed(Duration(milliseconds: 500), () => _checkPendingCall()); + Future _fetchContactInfo(String phoneNumber) async { + if (currentDisplayName != null && currentThumbnail != null) return; // Already set + try { + final contacts = await _contactService.fetchContacts(); // Use ContactService + for (var contact in contacts) { + for (var phone in contact.phones) { + if (_normalizePhoneNumber(phone.number) == _normalizePhoneNumber(phoneNumber)) { + currentDisplayName = contact.displayName; + currentThumbnail = contact.thumbnail; + return; + } + } } + // If no match found, use phone number as fallback + currentDisplayName ??= phoneNumber; + currentThumbnail ??= null; + } catch (e) { + print('CallService: Error fetching contact info: $e'); + currentDisplayName = phoneNumber; + currentThumbnail = null; } } - void _navigateToCallPage() { - final context = navigatorKey.currentContext; - if (context == null) { - print('CallService: Cannot navigate to CallPage, context is null'); - return; - } - if (_isCallPageVisible) { + String _normalizePhoneNumber(String number) { + return number.replaceAll(RegExp(r'[\s\-\(\)]'), ''); + } + + void _navigateToCallPage(BuildContext context) { + if (_isCallPageVisible && ModalRoute.of(context)?.settings.name == '/call') { print('CallService: CallPage already visible, skipping navigation'); return; } print('CallService: Navigating to CallPage'); - Navigator.push( + Navigator.pushReplacement( context, MaterialPageRoute( settings: const RouteSettings(name: '/call'), builder: (context) => CallPage( - displayName: currentPhoneNumber!, + displayName: currentDisplayName ?? currentPhoneNumber!, phoneNumber: currentPhoneNumber!, - thumbnail: null, + thumbnail: currentThumbnail, ), ), ).then((_) { _isCallPageVisible = false; - print('CallService: CallPage popped, _isCallPageVisible set to false'); }); _isCallPageVisible = true; } void _navigateToIncomingCallPage(BuildContext context) { - if (_isCallPageVisible) { + if (_isCallPageVisible && ModalRoute.of(context)?.settings.name == '/incoming_call') { print('CallService: IncomingCallPage already visible, skipping navigation'); return; } @@ -129,35 +120,30 @@ class CallService { MaterialPageRoute( settings: const RouteSettings(name: '/incoming_call'), builder: (context) => IncomingCallPage( - displayName: currentPhoneNumber!, + displayName: currentDisplayName ?? currentPhoneNumber!, phoneNumber: currentPhoneNumber!, - thumbnail: null, + thumbnail: currentThumbnail, ), ), ).then((_) { _isCallPageVisible = false; - print('CallService: IncomingCallPage popped, _isCallPageVisible set to false'); }); _isCallPageVisible = true; } - void _closeCallPage() { - final context = navigatorKey.currentContext; - if (context == null) { - print('CallService: Cannot close page, context is null'); + void _closeCallPage(BuildContext context) { + if (!_isCallPageVisible) { + print('CallService: CallPage not visible, skipping pop'); return; } - print('CallService: Closing call page, _isCallPageVisible: $_isCallPageVisible'); if (Navigator.canPop(context)) { - print('CallService: Popping call page'); + print('CallService: Popping CallPage'); Navigator.pop(context); _isCallPageVisible = false; - } else { - print('CallService: No page to pop'); } } - Future> makeGsmCall( + Future makeGsmCall( BuildContext context, { required String phoneNumber, String? displayName, @@ -165,43 +151,45 @@ class CallService { }) async { try { currentPhoneNumber = phoneNumber; + currentDisplayName = displayName ?? phoneNumber; // Use provided or fetch later + currentThumbnail = thumbnail; // Use provided or fetch later print('CallService: Making GSM call to $phoneNumber'); final result = await _channel.invokeMethod('makeGsmCall', {"phoneNumber": phoneNumber}); print('CallService: makeGsmCall result: $result'); - final resultMap = Map.from(result as Map); - if (resultMap["status"] != "calling") { + if (result["status"] != "calling") { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Failed to initiate call")), ); + return; } - return resultMap; + // Fetch contact info if not provided + await _fetchContactInfo(phoneNumber); + _navigateToCallPage(context); // Navigate immediately after call initiation } catch (e) { print("CallService: Error making call: $e"); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Error making call: $e")), ); - return {"status": "error", "message": e.toString()}; + rethrow; } } - Future> hangUpCall(BuildContext context) async { + Future hangUpCall(BuildContext context) async { try { print('CallService: Hanging up call'); final result = await _channel.invokeMethod('hangUpCall'); print('CallService: hangUpCall result: $result'); - final resultMap = Map.from(result as Map); - if (resultMap["status"] != "ended") { + if (result["status"] != "ended") { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Failed to end call")), ); } - return resultMap; } catch (e) { print("CallService: Error hanging up call: $e"); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Error hanging up call: $e")), ); - return {"status": "error", "message": e.toString()}; + rethrow; } } } \ No newline at end of file