Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle edge cases for incorrect call flow execution #214

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/app/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const kSignalingClientConnectionTimeout = Duration(seconds: 10);
const kSignalingClientReconnectDelay = Duration(seconds: 3);
const kSignalingClientFastReconnectDelay = Duration(seconds: 1);

const kPeerConnectionRetrieveTimeout = Duration(seconds: 5);

const kCompatibilityVerifyRepeatDelay = Duration(seconds: 2);

const kDebounceDuration = Duration(milliseconds: 275);
Expand Down
59 changes: 40 additions & 19 deletions lib/features/call/bloc/call_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,13 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
} else {
try {
_logger.finer(() => 'Retrieve peerConnection completer with callId: $callId - await');
final peerConnection = await peerConnectionCompleter.future;
// Timeout to handle scenarios where `complete` is never called.
// This can occur, for example, when a push is received for an incorrect account
// after a forced logout.
final peerConnection = await peerConnectionCompleter.future.timeout(
kPeerConnectionRetrieveTimeout,
onTimeout: () => throw TimeoutException('Timeout while retrieving peer connection for callId: $callId'),
);
_logger.finer(() => 'Retrieve peerConnection completer with callId: $callId - value');
return peerConnection;
} catch (e, stackTrace) {
Expand Down Expand Up @@ -435,6 +441,10 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
_logger.warning('__onResetStateEventCompleteCall: ${event.callId}');

try {
emit(state.copyWithMappedActiveCall(event.callId, (activeCall) {
return activeCall.copyWith(status: CallProcessingStatus.disconnecting);
}));

await state.performOnActiveCall(event.callId, (activeCall) async {
await (await _peerConnectionRetrieve(activeCall.callId))?.close();
await activeCall.localStream?.dispose();
Expand Down Expand Up @@ -1538,7 +1548,10 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
) async {
// Condition occur when the user interacts with a push notification before signaling is properly initialized.
// In this case, the CallKeep method "reportNewIncomingCall" may return callIdAlreadyTerminated.
if (state.retrieveActiveCall(event.callId)?.line == _kUndefinedLine) return;
if (state.retrieveActiveCall(event.callId)?.line == _kUndefinedLine) {
add(_ResetStateEvent.completeCall(event.callId));
return;
}

if (state.retrieveActiveCall(event.callId)?.wasHungUp == true) {
// TODO: There's an issue where the user might have already ended the call, but the active call screen remains visible.
Expand Down Expand Up @@ -1826,38 +1839,46 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
_CallScreenEventDidPush event,
Emitter<CallState> emit,
) async {
final hasActiveCalls = state.activeCalls.isNotEmpty;
var newState = state.copyWith(minimized: false);

newState = newState.copyWithMappedActiveCalls((activeCall) {
final transfer = activeCall.transfer;
if (transfer != null && transfer is BlindTransferInitiated) {
return activeCall.copyWith(
transfer: null,
);
} else {
return activeCall;
}
});
if (hasActiveCalls) {
newState = newState.copyWithMappedActiveCalls((activeCall) {
final transfer = activeCall.transfer;
if (transfer != null && transfer is BlindTransferInitiated) {
return activeCall.copyWith(
transfer: null,
);
} else {
return activeCall;
}
});

emit(newState);
emit(newState);

await callkeep.reportUpdateCall(
state.activeCalls.current.callId,
proximityEnabled: state.shouldListenToProximity,
);
await callkeep.reportUpdateCall(
state.activeCalls.current.callId,
proximityEnabled: state.shouldListenToProximity,
);
} else {
_logger.warning('__onCallScreenEventDidPush: activeCalls is empty');
}
}

Future<void> __onCallScreenEventDidPop(
_CallScreenEventDidPop event,
Emitter<CallState> emit,
) async {
emit(state.copyWith(minimized: state.activeCalls.isEmpty ? null : true));
final hasActiveCalls = state.activeCalls.isNotEmpty;
emit(state.copyWith(minimized: hasActiveCalls ? true : null));

if (state.activeCalls.isNotEmpty) {
if (hasActiveCalls) {
await callkeep.reportUpdateCall(
state.activeCalls.current.callId,
proximityEnabled: state.shouldListenToProximity,
);
} else {
_logger.warning('__onCallScreenEventDidPop: activeCalls is empty');
}
}

Expand Down
103 changes: 75 additions & 28 deletions lib/features/call/bloc/call_bloc.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12047,7 +12047,9 @@ mixin _$CallState {
bool? get minimized => throw _privateConstructorUsedError;
bool? get speaker => throw _privateConstructorUsedError;

@JsonKey(ignore: true)
/// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CallStateCopyWith<CallState> get copyWith =>
throw _privateConstructorUsedError;
}
Expand Down Expand Up @@ -12080,6 +12082,8 @@ class _$CallStateCopyWithImpl<$Res, $Val extends CallState>
// ignore: unused_field
final $Res Function($Val) _then;

/// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Expand Down Expand Up @@ -12168,6 +12172,8 @@ class __$$CallStateImplCopyWithImpl<$Res>
_$CallStateImpl _value, $Res Function(_$CallStateImpl) _then)
: super(_value, _then);

/// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Expand Down Expand Up @@ -12346,7 +12352,9 @@ class _$CallStateImpl extends _CallState with DiagnosticableTreeMixin {
minimized,
speaker);

@JsonKey(ignore: true)
/// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CallStateImplCopyWith<_$CallStateImpl> get copyWith =>
Expand Down Expand Up @@ -12387,8 +12395,11 @@ abstract class _CallState extends CallState {
bool? get minimized;
@override
bool? get speaker;

/// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(ignore: true)
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CallStateImplCopyWith<_$CallStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Expand All @@ -12400,6 +12411,7 @@ mixin _$ActiveCall {
String get callId => throw _privateConstructorUsedError;
CallkeepHandle get handle => throw _privateConstructorUsedError;
String? get displayName => throw _privateConstructorUsedError;
CallProcessingStatus? get status => throw _privateConstructorUsedError;

/// If the call is result of a refer request, the id should be provided.
String? get fromReferId => throw _privateConstructorUsedError;
Expand All @@ -12416,7 +12428,9 @@ mixin _$ActiveCall {
MediaStream? get localStream => throw _privateConstructorUsedError;
MediaStream? get remoteStream => throw _privateConstructorUsedError;

@JsonKey(ignore: true)
/// Create a copy of ActiveCall
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ActiveCallCopyWith<ActiveCall> get copyWith =>
throw _privateConstructorUsedError;
}
Expand All @@ -12433,6 +12447,7 @@ abstract class $ActiveCallCopyWith<$Res> {
String callId,
CallkeepHandle handle,
String? displayName,
CallProcessingStatus? status,
String? fromReferId,
bool video,
bool? frontCamera,
Expand Down Expand Up @@ -12460,6 +12475,8 @@ class _$ActiveCallCopyWithImpl<$Res, $Val extends ActiveCall>
// ignore: unused_field
final $Res Function($Val) _then;

/// Create a copy of ActiveCall
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Expand All @@ -12468,6 +12485,7 @@ class _$ActiveCallCopyWithImpl<$Res, $Val extends ActiveCall>
Object? callId = null,
Object? handle = null,
Object? displayName = freezed,
Object? status = freezed,
Object? fromReferId = freezed,
Object? video = null,
Object? frontCamera = freezed,
Expand Down Expand Up @@ -12503,6 +12521,10 @@ class _$ActiveCallCopyWithImpl<$Res, $Val extends ActiveCall>
? _value.displayName
: displayName // ignore: cast_nullable_to_non_nullable
as String?,
status: freezed == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as CallProcessingStatus?,
fromReferId: freezed == fromReferId
? _value.fromReferId
: fromReferId // ignore: cast_nullable_to_non_nullable
Expand Down Expand Up @@ -12555,6 +12577,8 @@ class _$ActiveCallCopyWithImpl<$Res, $Val extends ActiveCall>
) as $Val);
}

/// Create a copy of ActiveCall
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TransferCopyWith<$Res>? get transfer {
Expand Down Expand Up @@ -12582,6 +12606,7 @@ abstract class _$$ActiveCallImplCopyWith<$Res>
String callId,
CallkeepHandle handle,
String? displayName,
CallProcessingStatus? status,
String? fromReferId,
bool video,
bool? frontCamera,
Expand All @@ -12608,6 +12633,8 @@ class __$$ActiveCallImplCopyWithImpl<$Res>
_$ActiveCallImpl _value, $Res Function(_$ActiveCallImpl) _then)
: super(_value, _then);

/// Create a copy of ActiveCall
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Expand All @@ -12616,6 +12643,7 @@ class __$$ActiveCallImplCopyWithImpl<$Res>
Object? callId = null,
Object? handle = null,
Object? displayName = freezed,
Object? status = freezed,
Object? fromReferId = freezed,
Object? video = null,
Object? frontCamera = freezed,
Expand Down Expand Up @@ -12651,6 +12679,10 @@ class __$$ActiveCallImplCopyWithImpl<$Res>
? _value.displayName
: displayName // ignore: cast_nullable_to_non_nullable
as String?,
status: freezed == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as CallProcessingStatus?,
fromReferId: freezed == fromReferId
? _value.fromReferId
: fromReferId // ignore: cast_nullable_to_non_nullable
Expand Down Expand Up @@ -12713,6 +12745,7 @@ class _$ActiveCallImpl extends _ActiveCall with DiagnosticableTreeMixin {
required this.callId,
required this.handle,
this.displayName,
this.status,
this.fromReferId,
required this.video,
this.frontCamera = true,
Expand All @@ -12738,6 +12771,8 @@ class _$ActiveCallImpl extends _ActiveCall with DiagnosticableTreeMixin {
final CallkeepHandle handle;
@override
final String? displayName;
@override
final CallProcessingStatus? status;

/// If the call is result of a refer request, the id should be provided.
@override
Expand Down Expand Up @@ -12773,7 +12808,7 @@ class _$ActiveCallImpl extends _ActiveCall with DiagnosticableTreeMixin {

@override
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
return 'ActiveCall(direction: $direction, line: $line, callId: $callId, handle: $handle, displayName: $displayName, fromReferId: $fromReferId, video: $video, frontCamera: $frontCamera, held: $held, muted: $muted, updating: $updating, createdTime: $createdTime, acceptedTime: $acceptedTime, hungUpTime: $hungUpTime, transfer: $transfer, failure: $failure, localStream: $localStream, remoteStream: $remoteStream)';
return 'ActiveCall(direction: $direction, line: $line, callId: $callId, handle: $handle, displayName: $displayName, status: $status, fromReferId: $fromReferId, video: $video, frontCamera: $frontCamera, held: $held, muted: $muted, updating: $updating, createdTime: $createdTime, acceptedTime: $acceptedTime, hungUpTime: $hungUpTime, transfer: $transfer, failure: $failure, localStream: $localStream, remoteStream: $remoteStream)';
}

@override
Expand All @@ -12786,6 +12821,7 @@ class _$ActiveCallImpl extends _ActiveCall with DiagnosticableTreeMixin {
..add(DiagnosticsProperty('callId', callId))
..add(DiagnosticsProperty('handle', handle))
..add(DiagnosticsProperty('displayName', displayName))
..add(DiagnosticsProperty('status', status))
..add(DiagnosticsProperty('fromReferId', fromReferId))
..add(DiagnosticsProperty('video', video))
..add(DiagnosticsProperty('frontCamera', frontCamera))
Expand Down Expand Up @@ -12813,6 +12849,7 @@ class _$ActiveCallImpl extends _ActiveCall with DiagnosticableTreeMixin {
(identical(other.handle, handle) || other.handle == handle) &&
(identical(other.displayName, displayName) ||
other.displayName == displayName) &&
(identical(other.status, status) || other.status == status) &&
(identical(other.fromReferId, fromReferId) ||
other.fromReferId == fromReferId) &&
(identical(other.video, video) || other.video == video) &&
Expand All @@ -12838,28 +12875,32 @@ class _$ActiveCallImpl extends _ActiveCall with DiagnosticableTreeMixin {
}

@override
int get hashCode => Object.hash(
runtimeType,
direction,
line,
callId,
handle,
displayName,
fromReferId,
video,
frontCamera,
held,
muted,
updating,
createdTime,
acceptedTime,
hungUpTime,
transfer,
const DeepCollectionEquality().hash(failure),
localStream,
remoteStream);

@JsonKey(ignore: true)
int get hashCode => Object.hashAll([
runtimeType,
direction,
line,
callId,
handle,
displayName,
status,
fromReferId,
video,
frontCamera,
held,
muted,
updating,
createdTime,
acceptedTime,
hungUpTime,
transfer,
const DeepCollectionEquality().hash(failure),
localStream,
remoteStream
]);

/// Create a copy of ActiveCall
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ActiveCallImplCopyWith<_$ActiveCallImpl> get copyWith =>
Expand All @@ -12873,6 +12914,7 @@ abstract class _ActiveCall extends ActiveCall {
required final String callId,
required final CallkeepHandle handle,
final String? displayName,
final CallProcessingStatus? status,
final String? fromReferId,
required final bool video,
final bool? frontCamera,
Expand All @@ -12899,8 +12941,10 @@ abstract class _ActiveCall extends ActiveCall {
@override
String? get displayName;
@override
CallProcessingStatus? get status;

/// If the call is result of a refer request, the id should be provided.
@override
String? get fromReferId;
@override
bool get video;
Expand All @@ -12926,8 +12970,11 @@ abstract class _ActiveCall extends ActiveCall {
MediaStream? get localStream;
@override
MediaStream? get remoteStream;

/// Create a copy of ActiveCall
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(ignore: true)
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ActiveCallImplCopyWith<_$ActiveCallImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Loading