From 4b4604448d079ce71c193905c109a79ec1b73578 Mon Sep 17 00:00:00 2001 From: AlexisDanlos Date: Wed, 18 Jun 2025 18:31:13 +0200 Subject: [PATCH] feat: enhance SIM management with state tracking and UI updates --- dialer/lib/domain/services/call_service.dart | 58 +++++++++++++++---- .../presentation/features/call/call_page.dart | 55 ++++++++++++++---- 2 files changed, 92 insertions(+), 21 deletions(-) diff --git a/dialer/lib/domain/services/call_service.dart b/dialer/lib/domain/services/call_service.dart index 4a5b2dd..5b95683 100644 --- a/dialer/lib/domain/services/call_service.dart +++ b/dialer/lib/domain/services/call_service.dart @@ -11,6 +11,7 @@ class CallService { static String? currentPhoneNumber; static String? currentDisplayName; static Uint8List? currentThumbnail; + static int? currentSimSlot; // Track which SIM slot is being used static bool _isCallPageVisible = false; static Map? _pendingCall; static bool wasPhoneLocked = false; @@ -20,15 +21,38 @@ class CallService { final _callStateController = StreamController.broadcast(); final _audioStateController = StreamController>.broadcast(); + final _simStateController = StreamController.broadcast(); Map? _currentAudioState; static final GlobalKey navigatorKey = GlobalKey(); - Stream get callStateStream => _callStateController.stream; Stream> get audioStateStream => _audioStateController.stream; + Stream get simStateStream => _simStateController.stream; Map? get currentAudioState => _currentAudioState; + // Getter for current SIM slot + static int? get getCurrentSimSlot => currentSimSlot; + // Get SIM display name for the current call + static String? getCurrentSimDisplayName() { + if (currentSimSlot == null) return null; + return "SIM ${currentSimSlot! + 1}"; + } + + // Cancel pending SIM switch (used when user manually hangs up) + void cancelPendingSimSwitch() { + if (_pendingSimSwitch != null) { + print('CallService: Canceling pending SIM switch due to manual hangup'); + _pendingSimSwitch = null; + _manualHangupFlag = true; // Mark that hangup was manual + print('CallService: Manual hangup flag set to $_manualHangupFlag'); + } else { + print( + 'CallService: No pending SIM switch to cancel, but setting manual hangup flag'); + _manualHangupFlag = + true; // Still mark as manual even if no pending switch + } + } CallService() { _channel.setMethodCallHandler((call) async { @@ -73,9 +97,15 @@ class CallService { 'CallService: State changed to $state, wasPhoneLocked: $wasPhoneLocked'); _callStateController.add(state); if (state == "disconnected" || state == "disconnecting") { - // Only close call page if there's no pending SIM switch - if (_pendingSimSwitch == null) { + // Close call page if no pending SIM switch OR if it was a manual hangup + if (_pendingSimSwitch == null || _manualHangupFlag) { _closeCallPage(); + // Only reset manual hangup flag after successful page close + if (_manualHangupFlag) { + print( + 'CallService: Resetting manual hangup flag after page close'); + _manualHangupFlag = false; + } } if (wasPhoneLocked) { await _channel.invokeMethod("callEndedFromFlutter"); @@ -127,9 +157,8 @@ class CallService { case "callRemoved": wasPhoneLocked = call.arguments["wasPhoneLocked"] as bool? ?? false; print( - 'CallService: Call ended/removed, wasPhoneLocked: $wasPhoneLocked'); - // Only close call page if there's no pending SIM switch - if (_pendingSimSwitch == null) { + 'CallService: Call ended/removed, wasPhoneLocked: $wasPhoneLocked'); // Only close call page if there's no pending SIM switch AND no manual hangup in progress + if (_pendingSimSwitch == null && !_manualHangupFlag) { _closeCallPage(); } if (wasPhoneLocked) { @@ -138,6 +167,8 @@ class CallService { currentPhoneNumber = null; currentDisplayName = null; currentThumbnail = null; + currentSimSlot = null; // Reset SIM slot when call ends + _simStateController.add(null); // Notify UI that SIM is cleared _activeCallNumber = null; break; case "incomingCallFromNotification": @@ -493,6 +524,8 @@ class CallService { currentPhoneNumber = phoneNumber; currentDisplayName = displayName ?? phoneNumber; currentThumbnail = thumbnail; + currentSimSlot = simSlot; // Track the SIM slot being used + _simStateController.add(simSlot); // Notify UI of SIM change if (displayName == null || thumbnail == null) { await _fetchContactInfo(phoneNumber); } @@ -519,6 +552,7 @@ class CallService { // Pending SIM switch data static Map? _pendingSimSwitch; + static bool _manualHangupFlag = false; // Track if hangup was manual // Getter to check if there's a pending SIM switch static bool get hasPendingSimSwitch => _pendingSimSwitch != null; @@ -583,13 +617,15 @@ class CallService { print('CallService: Executing pending SIM switch redial'); // Wait a moment to ensure the previous call is fully disconnected - await Future.delayed(const Duration(milliseconds: 1000)); - - // Store the new call info for the redial + await Future.delayed(const Duration( + milliseconds: 1000)); // Store the new call info for the redial currentPhoneNumber = switchData['phoneNumber']; currentDisplayName = switchData['displayName']; - currentThumbnail = - switchData['thumbnail']; // Make the new call with the selected SIM + currentThumbnail = switchData['thumbnail']; + currentSimSlot = switchData['simSlot']; // Track the new SIM slot + _simStateController.add(switchData['simSlot']); // Notify UI of SIM change + + // Make the new call with the selected SIM final result = await _channel.invokeMethod('makeGsmCall', { 'phoneNumber': switchData['phoneNumber'], 'simSlot': switchData['simSlot'], diff --git a/dialer/lib/presentation/features/call/call_page.dart b/dialer/lib/presentation/features/call/call_page.dart index 885c7e2..2497956 100644 --- a/dialer/lib/presentation/features/call/call_page.dart +++ b/dialer/lib/presentation/features/call/call_page.dart @@ -36,6 +36,7 @@ class _CallPageState extends State { String _callStatus = "Calling..."; StreamSubscription? _callStateSubscription; StreamSubscription>? _audioStateSubscription; + StreamSubscription? _simStateSubscription; bool _isCallActive = true; // Track if call is still active bool get isNumberUnknown => widget.displayName == widget.phoneNumber; @@ -46,6 +47,7 @@ class _CallPageState extends State { _checkInitialCallState(); _listenToCallState(); _listenToAudioState(); + _listenToSimState(); _setInitialAudioState(); } @@ -54,6 +56,7 @@ class _CallPageState extends State { _callTimer?.cancel(); _callStateSubscription?.cancel(); _audioStateSubscription?.cancel(); + _simStateSubscription?.cancel(); super.dispose(); } @@ -103,15 +106,8 @@ class _CallPageState extends State { } else if (state == "disconnected" || state == "disconnecting") { _callTimer?.cancel(); _callStatus = "Call Ended"; - _isCallActive = - false; // Only navigate back if there's no pending SIM switch - if (!CallService.hasPendingSimSwitch) { - Future.delayed(const Duration(seconds: 2), () { - if (mounted && Navigator.canPop(context)) { - Navigator.of(context).pop(); - } - }); - } + _isCallActive = false; + // Let CallService handle navigation - don't navigate from here } else { _callStatus = "Calling..."; _isCallActive = true; @@ -132,6 +128,17 @@ class _CallPageState extends State { }); } + void _listenToSimState() { + _simStateSubscription = _callService.simStateStream.listen((simSlot) { + if (mounted) { + setState(() { + // UI will update automatically because we're listening to the stream + // The SIM display will show the new SIM slot + }); + } + }); + } + void _startCallTimer() { _callTimer?.cancel(); _callTimer = Timer.periodic(const Duration(seconds: 1), (timer) { @@ -274,7 +281,12 @@ class _CallPageState extends State { } try { - print('CallPage: Initiating hangUp'); + print( + 'CallPage: Initiating manual hangUp - canceling any pending SIM switch'); + + // Cancel any pending SIM switch since user is manually hanging up + _callService.cancelPendingSimSwitch(); + final result = await _callService.hangUpCall(context); print('CallPage: Hang up result: $result'); } catch (e) { @@ -386,6 +398,29 @@ class _CallPageState extends State { color: Colors.white70, ), ), + // Show SIM information if available + if (CallService.getCurrentSimDisplayName() != null) + Container( + margin: const EdgeInsets.only(top: 4.0), + padding: const EdgeInsets.symmetric( + horizontal: 8.0, vertical: 2.0), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.2), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.blue.withOpacity(0.5), + width: 1, + ), + ), + child: Text( + CallService.getCurrentSimDisplayName()!, + style: TextStyle( + fontSize: statusFontSize - 2, + color: Colors.lightBlueAccent, + fontWeight: FontWeight.w500, + ), + ), + ), ], ), ),