@@ -218,32 +218,42 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
218218 _logger.info (() => 'status transitions: $currentProcessingStatuses -> $nextProcessingStatuses ' );
219219 }
220220
221+ /// RegistrationStatus can be not allowed if the signaling state
222+ /// was not yet fully initialized. For this situation we decide allow RegistrationStatus be nullable that indicate signaling was not initialized yet
223+ ///
224+ /// This scenario is particularly relevant when a call is triggered before the app
225+ /// is fully active, such as via [CallkeepDelegate.continueStartCallIntent]
226+ /// (e.g., from phone recents).
227+ ///
228+
221229 final newRegistration = change.nextState.callServiceState.registration;
222230 final previousRegistration = change.currentState.callServiceState.registration;
231+
223232 if (newRegistration != previousRegistration) {
224233 _logger.fine ('_onRegistrationChange: $newRegistration to $previousRegistration ' );
225- final newRegistrationStatus = newRegistration.status;
226- final previousRegistrationStatus = previousRegistration.status;
227234
228- if (newRegistrationStatus.isRegistered && ! previousRegistrationStatus.isRegistered) {
235+ final newRegistrationStatus = newRegistration? .status;
236+ final previousRegistrationStatus = previousRegistration? .status;
237+
238+ if (newRegistrationStatus? .isRegistered == true && previousRegistrationStatus? .isRegistered == false ) {
229239 presenceRepository.resetLastSettingsSync ();
230240 submitNotification (AppOnlineNotification ());
231241 }
232242
233- if (! newRegistrationStatus.isRegistered && previousRegistrationStatus.isRegistered) {
243+ if (newRegistrationStatus? .isRegistered == false && previousRegistrationStatus? .isRegistered == true ) {
234244 submitNotification (AppOfflineNotification ());
235245 }
236246
237- if (newRegistrationStatus.isFailed || newRegistrationStatus.isUnregistered) {
247+ if (newRegistrationStatus? .isFailed == true || newRegistrationStatus? .isUnregistered == true ) {
238248 add (const _ResetStateEvent .completeCalls ());
239249 }
240250
241- if (newRegistrationStatus.isFailed) {
251+ if (newRegistrationStatus? .isFailed == true ) {
242252 submitNotification (
243253 SipRegistrationFailedNotification (
244- knownCode: SignalingRegistrationFailedCode .values.byCode (newRegistration.code),
245- systemCode: newRegistration.code,
246- systemReason: newRegistration.reason,
254+ knownCode: SignalingRegistrationFailedCode .values.byCode (newRegistration? .code),
255+ systemCode: newRegistration? .code,
256+ systemReason: newRegistration? .reason,
247257 ),
248258 );
249259 }
@@ -676,7 +686,6 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
676686 state.copyWith (
677687 callServiceState: state.callServiceState.copyWith (
678688 signalingClientStatus: SignalingClientStatus .disconnect,
679- registration: const Registration (status: RegistrationStatus .registering),
680689 lastSignalingClientDisconnectError: null ,
681690 lastSignalingDisconnectCode: null ,
682691 ),
@@ -705,7 +714,6 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
705714
706715 CallState newState = state.copyWith (
707716 callServiceState: state.callServiceState.copyWith (
708- registration: const Registration (status: RegistrationStatus .registering),
709717 signalingClientStatus: SignalingClientStatus .disconnect,
710718 lastSignalingDisconnectCode: event.code,
711719 ),
@@ -714,9 +722,10 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
714722 bool shouldReconnect = true ;
715723
716724 if (code == SignalingDisconnectCode .appUnregisteredError) {
725+ add (const _CallSignalingEvent .registration (RegistrationStatus .unregistered));
726+
717727 newState = state.copyWith (
718728 callServiceState: state.callServiceState.copyWith (
719- registration: const Registration (status: RegistrationStatus .unregistered),
720729 signalingClientStatus: SignalingClientStatus .disconnect,
721730 lastSignalingDisconnectCode: event.code,
722731 ),
@@ -824,11 +833,7 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
824833 _CallSignalingEventNotifyRefer () => __onCallSignalingEventNotifyRefer (event, emit),
825834 _CallSignalingEventNotifyPresence () => __onCallSignalingEventNotifyPresence (event, emit),
826835 _CallSignalingEventNotifyUnknown () => __onCallSignalingEventNotifyUnknown (event, emit),
827- _CallSignalingEventRegistering () => __onCallSignalingEventRegistering (event, emit),
828- _CallSignalingEventRegistered () => __onCallSignalingEventRegistered (event, emit),
829- _CallSignalingEventRegisterationFailed () => __onCallSignalingEventRegistrationFailed (event, emit),
830- _CallSignalingEventUnregistering () => __onCallSignalingEventUnregistering (event, emit),
831- _CallSignalingEventUnregistered () => __onCallSignalingEventUnregistered (event, emit),
836+ _CallSignalingEventRegistration () => __onCallSignalingEventRegistration (event, emit),
832837 };
833838 }
834839
@@ -1164,41 +1169,12 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
11641169 _logger.fine ('_CallSignalingEventNotifyUnknown: $event ' );
11651170 }
11661171
1167- Future <void > __onCallSignalingEventRegistering (_CallSignalingEventRegistering event, Emitter <CallState > emit) async {
1168- add (const _RegistrationChange (registration: Registration (status: RegistrationStatus .registering)));
1169- }
1170-
1171- Future <void > __onCallSignalingEventRegistered (_CallSignalingEventRegistered event, Emitter <CallState > emit) async {
1172- add (const _RegistrationChange (registration: Registration (status: RegistrationStatus .registered)));
1173- }
1174-
1175- Future <void > __onCallSignalingEventRegistrationFailed (
1176- _CallSignalingEventRegisterationFailed event,
1177- Emitter <CallState > emit,
1178- ) async {
1179- add (
1180- _RegistrationChange (
1181- registration: Registration (
1182- status: RegistrationStatus .registration_failed,
1183- code: event.code,
1184- reason: event.reason,
1185- ),
1186- ),
1187- );
1188- }
1189-
1190- Future <void > __onCallSignalingEventUnregistering (
1191- _CallSignalingEventUnregistering event,
1172+ Future <void > __onCallSignalingEventRegistration (
1173+ _CallSignalingEventRegistration event,
11921174 Emitter <CallState > emit,
11931175 ) async {
1194- add (const _RegistrationChange (registration: Registration (status: RegistrationStatus .unregistering)));
1195- }
1196-
1197- Future <void > __onCallSignalingEventUnregistered (
1198- _CallSignalingEventUnregistered event,
1199- Emitter <CallState > emit,
1200- ) async {
1201- add (const _RegistrationChange (registration: Registration (status: RegistrationStatus .unregistered)));
1176+ final registration = Registration (status: event.status, code: event.code, reason: event.reason);
1177+ add (_RegistrationChange (registration: registration));
12021178 }
12031179
12041180 // processing call control events
@@ -1225,7 +1201,7 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
12251201 }
12261202
12271203 Future <void > __onCallControlEventStarted (_CallControlEventStarted event, Emitter <CallState > emit) async {
1228- if (! state.callServiceState.registration.status.isRegistered) {
1204+ if (state.callServiceState.registration? .status.isRegistered == false ) {
12291205 _logger.info ('__onCallControlEventStarted account is not registered' );
12301206 submitNotification (CallWhileUnregisteredNotification ());
12311207 return ;
@@ -1668,7 +1644,7 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
16681644 }
16691645
16701646 Future <void > __onCallPerformEventStarted (_CallPerformEventStarted event, Emitter <CallState > emit) async {
1671- if (! state.callServiceState.registration.status.isRegistered) {
1647+ if (state.callServiceState.registration? .status.isRegistered == false ) {
16721648 _logger.info ('__onCallPerformEventStarted account is not registered' );
16731649 submitNotification (CallWhileUnregisteredNotification ());
16741650
@@ -2501,15 +2477,20 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
25012477 ),
25022478 });
25032479 } else if (event is RegisteringEvent ) {
2504- add (const _CallSignalingEvent .registering ( ));
2480+ add (const _CallSignalingEvent .registration ( RegistrationStatus .registering ));
25052481 } else if (event is RegisteredEvent ) {
2506- add (const _CallSignalingEvent .registered ( ));
2482+ add (const _CallSignalingEvent .registration ( RegistrationStatus .registered ));
25072483 } else if (event is RegistrationFailedEvent ) {
2508- add (_CallSignalingEvent .registrationFailed (event.code, event.reason));
2484+ final registrationFailedEvent = _CallSignalingEvent .registration (
2485+ RegistrationStatus .registration_failed,
2486+ code: event.code,
2487+ reason: event.reason,
2488+ );
2489+ add (registrationFailedEvent);
25092490 } else if (event is UnregisteringEvent ) {
2510- add (const _CallSignalingEvent .unregistering ( ));
2491+ add (const _CallSignalingEvent .registration ( RegistrationStatus .unregistering ));
25112492 } else if (event is UnregisteredEvent ) {
2512- add (const _CallSignalingEvent .unregistered ( ));
2493+ add (const _CallSignalingEvent .registration ( RegistrationStatus .unregistered ));
25132494 } else if (event is TransferringEvent ) {
25142495 add (_CallSignalingEvent .transferring (line: event.line, callId: event.callId));
25152496 } else {
@@ -2541,15 +2522,75 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
25412522 void continueStartCallIntent (CallkeepHandle handle, String ? displayName, bool video) {
25422523 _logger.fine (() => 'continueStartCallIntent handle: $handle displayName: $displayName video: $video ' );
25432524
2544- add (
2545- CallControlEvent .started (
2525+ _continueStartCallIntent (handle, displayName, video);
2526+ }
2527+
2528+ Future <void > _continueStartCallIntent (CallkeepHandle handle, String ? displayName, bool video) async {
2529+ _logger.fine (
2530+ () => StringBuffer ()
2531+ ..write ('_continueStartCallIntent - Attempting to start call' )
2532+ ..write (' handle: $handle ' )
2533+ ..write (' displayName: $displayName ' )
2534+ ..write (' video: $video ' )
2535+ ..write (' isHandshakeActive: ${state .isHandshakeEstablished }' )
2536+ ..write (' isSignalingActive: ${state .isSignalingEstablished }' )
2537+ ..toString (),
2538+ );
2539+
2540+ try {
2541+ // Wait until both signaling and handshake are active.
2542+ // If the desired state is not reached within kSignalingClientConnectionTimeout, a TimeoutException will be thrown.
2543+ final resolvedState = await stream
2544+ .firstWhere ((state) => state.isHandshakeEstablished && state.isSignalingEstablished)
2545+ .timeout (kSignalingClientConnectionTimeout);
2546+
2547+ if (isClosed) return ;
2548+
2549+ _logger.fine (
2550+ () => StringBuffer ()
2551+ ..write ('_continueStartCallIntent - Signaling and handshake are now active for' )
2552+ ..write (' handle: $handle ' )
2553+ ..write (' displayName: $displayName ' )
2554+ ..write (' video: $video ' )
2555+ ..write (' isHandshakeActive: ${resolvedState .isHandshakeEstablished }' )
2556+ ..write (' isSignalingActive: ${resolvedState .isSignalingEstablished }' )
2557+ ..toString (),
2558+ );
2559+
2560+ final event = CallControlEvent .started (
25462561 generic: handle.isGeneric ? handle.value : null ,
25472562 number: handle.isNumber ? handle.value : null ,
25482563 email: handle.isEmail ? handle.value : null ,
25492564 displayName: displayName,
25502565 video: video,
2551- ),
2552- );
2566+ );
2567+
2568+ add (event);
2569+ } on TimeoutException {
2570+ if (isClosed) return ;
2571+
2572+ _logger.warning (
2573+ () => StringBuffer ()
2574+ ..write ('_continueStartCallIntent - Failed to start call' )
2575+ ..write (' handle: $handle ' )
2576+ ..write (' (Signaling/handshake connection timed out after ${kSignalingClientConnectionTimeout .inSeconds }s)' )
2577+ ..write (' isHandshakeActive: ${state .isHandshakeEstablished }' )
2578+ ..write (' isSignalingActive: ${state .isSignalingEstablished }' )
2579+ ..toString (),
2580+ );
2581+
2582+ submitNotification (const SignalingConnectFailedNotification ());
2583+ } catch (e, s) {
2584+ if (isClosed) return ;
2585+
2586+ final severeMessage = StringBuffer ()
2587+ ..write ('_continueStartCallIntent - An unexpected error occurred while waiting for signaling' )
2588+ ..write (' handle: $handle ' )
2589+ ..toString ();
2590+ _logger.severe (() => severeMessage, e, s);
2591+
2592+ submitNotification (ErrorMessageNotification (e.toString ()));
2593+ }
25532594 }
25542595
25552596 @override
0 commit comments