From ea69ae69ada0c23fd91be897b493d6d243228f7e Mon Sep 17 00:00:00 2001 From: Victor Uvarov Date: Tue, 22 Oct 2024 18:37:15 -0700 Subject: [PATCH 01/35] make sure the session terminates (#485) Co-authored-by: Victor Uvarov --- lib/src/sip_ua_helper.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 71de9291..a27d44ae 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -554,7 +554,7 @@ class Call { assert(_session != null, 'ERROR(hangup): rtc session is invalid!'); if (peerConnection != null) { for (MediaStream? stream in peerConnection!.getLocalStreams()) { - if (stream == null) return; + if (stream == null) continue; logger.d( 'Stopping local stream with tracks: ${stream.getTracks().length}'); for (MediaStreamTrack track in stream.getTracks()) { @@ -563,7 +563,7 @@ class Call { } } for (MediaStream? stream in peerConnection!.getRemoteStreams()) { - if (stream == null) return; + if (stream == null) continue; logger.d( 'Stopping remote stream with tracks: ${stream.getTracks().length}'); for (MediaStreamTrack track in stream.getTracks()) { From 23c25959d4d2103e09fd277a90c5cb2ced812c3f Mon Sep 17 00:00:00 2001 From: Mikael Wills <63661422+mikaelwills@users.noreply.github.com> Date: Mon, 2 Dec 2024 01:18:14 +0000 Subject: [PATCH 02/35] Hold and video upgrade fixed (#503) Co-authored-by: Mikael Wills --- example/lib/src/callscreen.dart | 13 +- example/lib/src/register.dart | 26 +++- example/test/widget_test.dart | 2 +- lib/src/rtc_session.dart | 249 ++++++++++++++++++++++++-------- lib/src/sip_ua_helper.dart | 4 +- 5 files changed, 222 insertions(+), 72 deletions(-) diff --git a/example/lib/src/callscreen.dart b/example/lib/src/callscreen.dart index 976c0307..4bfba3dc 100644 --- a/example/lib/src/callscreen.dart +++ b/example/lib/src/callscreen.dart @@ -181,7 +181,10 @@ class _MyCallScreenWidget extends State if (_localRenderer != null) { _localRenderer!.srcObject = stream; } - if (!kIsWeb && !WebRTC.platformIsDesktop) { + + if (!kIsWeb && + !WebRTC.platformIsDesktop && + event.stream?.getAudioTracks().isNotEmpty == true) { event.stream?.getAudioTracks().first.enableSpeakerphone(false); } _localStream = stream; @@ -334,10 +337,14 @@ class _MyCallScreenWidget extends State call!.voiceOnly = false; }); helper!.renegotiate( - call: call!, voiceOnly: false, done: (incomingMessage) {}); + call: call!, + voiceOnly: false, + done: (IncomingMessage? incomingMessage) {}); } else { helper!.renegotiate( - call: call!, voiceOnly: true, done: (incomingMessage) {}); + call: call!, + voiceOnly: true, + done: (IncomingMessage? incomingMessage) {}); } } diff --git a/example/lib/src/register.dart b/example/lib/src/register.dart index a9f0bc98..f315bf3d 100644 --- a/example/lib/src/register.dart +++ b/example/lib/src/register.dart @@ -156,6 +156,27 @@ class _MyRegisterWidget extends State appBar: AppBar( title: Text("SIP Account"), ), + bottomNavigationBar: Padding( + padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: SizedBox( + height: 40, + child: ElevatedButton( + child: Text('Register'), + onPressed: () => _handleSave(context), + ), + ), + ), + ], + ), + ], + ), + ), body: ListView( padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 20), children: [ @@ -281,11 +302,6 @@ class _MyRegisterWidget extends State ], ), ], - const SizedBox(height: 20), - ElevatedButton( - child: Text('Register'), - onPressed: () => _handleSave(context), - ), ], ), ); diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 4e8e53f4..900418a1 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:dart_sip_ua_example/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 9fc9da52..d9937d07 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -301,7 +301,8 @@ class RTCSession extends EventManager implements Owner { // Set anonymous property. bool anonymous = options['anonymous'] ?? false; Map requestParams = { - 'from_tag': _from_tag + 'from_tag': _from_tag, + 'to_display_name': options['to_display_name'] ?? '', }; _ua.contact!.anonymous = anonymous; _ua.contact!.outbound = true; @@ -314,7 +315,7 @@ class RTCSession extends EventManager implements Owner { requestParams['from_display_name'] = options['from_display_name'] ?? ''; requestParams['from_uri'] = URI.parse(options['from_uri']); extraHeaders - .add('P-Preferred-Identity: ${_ua.configuration.uri.toString()}'); + .add('P-Preferred-Identity: ${_ua.configuration.uri.toString()}'); } if (anonymous) { @@ -1032,7 +1033,7 @@ class RTCSession extends EventManager implements Owner { /** * Hold */ - bool hold([Map? options, Function? done]) { + bool hold([Map? options, Function(IncomingMessage?)? done]) { logger.d('hold()'); options = options ?? {}; @@ -1056,7 +1057,7 @@ class RTCSession extends EventManager implements Owner { handlers.on(EventSucceeded(), (EventSucceeded event) { if (done != null) { - done(); + done(event.response); } }); handlers.on(EventCallFailed(), (EventCallFailed event) { @@ -1083,7 +1084,8 @@ class RTCSession extends EventManager implements Owner { return true; } - bool unhold([Map? options, Function? done]) { + bool unhold( + [Map? options, Function(IncomingMessage?)? done]) { logger.d('unhold()'); options = options ?? {}; @@ -1106,7 +1108,7 @@ class RTCSession extends EventManager implements Owner { EventManager handlers = EventManager(); handlers.on(EventSucceeded(), (EventSucceeded event) { if (done != null) { - done(); + done(event.response); } }); handlers.on(EventCallFailed(), (EventCallFailed event) { @@ -1135,8 +1137,8 @@ class RTCSession extends EventManager implements Owner { bool renegotiate( {Map? options, - Function(IncomingMessage)? done, - bool useUpdate = false}) { + bool useUpdate = false, + Function(IncomingMessage?)? done}) { logger.d('renegotiate()'); options = options ?? {}; @@ -1155,6 +1157,15 @@ class RTCSession extends EventManager implements Owner { return false; } + bool? upgradeToVideo; + try { + upgradeToVideo = (options['mediaConstraints']?['video'] != false || + options['mediaConstraints']?['mandatory']?['video'] != null) && + rtcOfferConstraints?['offerToReceiveVideo'] == null; + } catch (e) { + print('Failed to determine upgrade to video: $e'); + } + if (!_isReadyToReOffer()) { return false; } @@ -1175,7 +1186,7 @@ class RTCSession extends EventManager implements Owner { _setLocalMediaStatus(); - if (useUpdate) { + if (options['useUpdate'] != null) { _sendUpdate({ 'sdpOffer': true, 'eventHandlers': handlers, @@ -1183,13 +1194,21 @@ class RTCSession extends EventManager implements Owner { 'extraHeaders': options['extraHeaders'] }); } else { - _sendReinvite({ - 'eventHandlers': handlers, - 'sdpSemantics': sdpSemantics, - 'rtcOfferConstraints': rtcOfferConstraints, - 'mediaConstraints': mediaConstraints, - 'extraHeaders': options['extraHeaders'] - }); + if (upgradeToVideo ?? false) { + _sendVideoUpgradeReinvite({ + 'eventHandlers': handlers, + 'sdpSemantics': sdpSemantics, + 'rtcOfferConstraints': rtcOfferConstraints, + 'mediaConstraints': mediaConstraints, + 'extraHeaders': options['extraHeaders'] + }); + } else { + _sendReinvite({ + 'eventHandlers': handlers, + 'rtcOfferConstraints': rtcOfferConstraints, + 'extraHeaders': options['extraHeaders'] + }); + } } return true; @@ -1592,19 +1611,6 @@ class RTCSession extends EventManager implements Owner { 'optional': [], }; offerConstraints['mandatory']['IceRestart'] = true; - - EventManager handlers = EventManager(); - handlers.on(EventSucceeded(), (EventSucceeded event) async { - logger.d('ICE Restart was successful'); - }); - handlers.on(EventCallFailed(), (EventCallFailed event) { - terminate({ - 'cause': DartSIP_C.CausesType.WEBRTC_ERROR, - 'status_code': 500, - 'reason_phrase': 'Media Renegotiation Failed' - }); - }); - offerConstraints['eventHandlers'] = handlers; renegotiate(options: offerConstraints); } @@ -1670,7 +1676,7 @@ class RTCSession extends EventManager implements Owner { modifiers = constraints['offerModifiers'] ?? Function(RTCSessionDescription)>[]; - constraints.remove('offerModifiers'); + constraints['offerModifiers'] = null; if (type != 'offer' && type != 'answer') { completer.completeError(Exceptions.TypeError( @@ -2013,17 +2019,16 @@ class RTCSession extends EventManager implements Owner { bool upgradeToVideo = false; if (sdp != null) { List mediaList = sdp['media']; - for (Map m in mediaList) { - if (holdMediaTypes.indexOf(m['type']) == -1) { - continue; - } - - if (m['type'] == 'video') { - upgradeToVideo = true; - } - - String direction = m['direction'] ?? sdp['direction'] ?? 'sendrecv'; + // Loop media list items for video upgrade + for (Map media in mediaList) { + if (holdMediaTypes.indexOf(media['type']) == -1) continue; + if (media['type'] == 'video') upgradeToVideo = true; + } + // Loop media list items for hold + for (Map media in mediaList) { + if (holdMediaTypes.indexOf(media['type']) == -1) continue; + String direction = media['direction'] ?? sdp['direction'] ?? 'sendrecv'; if (direction == 'sendonly' || direction == 'inactive') { hold = true; } @@ -2046,19 +2051,34 @@ class RTCSession extends EventManager implements Owner { 'facingMode': 'user', } }; - MediaStream localStream = - await navigator.mediaDevices.getUserMedia(mediaConstraints); - if (localStream.getVideoTracks().isEmpty) { - logger.w( - 'Remote wants to upgrade to video but failed to get local video'); - } - for (MediaStreamTrack track in localStream.getTracks()) { - if (track.kind == 'video') { - _connection!.addTrack(track, localStream); + bool hasCamera = false; + try { + List devices = + await navigator.mediaDevices.enumerateDevices(); + for (MediaDeviceInfo device in devices) { + if (device.kind == 'videoinput') hasCamera = true; } + } catch (e) { + logger.w('Failed to enumerate devices: $e'); + } + if (hasCamera) { + MediaStream localStream = + await navigator.mediaDevices.getUserMedia(mediaConstraints); + if (localStream.getVideoTracks().isEmpty) { + logger.w( + 'Remote wants to upgrade to video but failed to get local video'); + } + for (MediaStreamTrack track in localStream.getTracks()) { + if (track.kind == 'video') { + _connection!.addTrack(track, localStream); + } + } + emit(EventStream( + session: this, originator: 'local', stream: localStream)); + } else { + logger.w( + 'Remote wants to upgrade to video but no camera available to send'); } - emit( - EventStream(session: this, originator: 'local', stream: localStream)); } logger.d('emit "sdp"'); @@ -2485,6 +2505,9 @@ class RTCSession extends EventManager implements Owner { emit(EventSetRemoteDescriptionFailed(exception: error)); } } else if (utils.test2XX(status_code)) { + // 2XX + _status = C.STATUS_CONFIRMED; + if (response.body == null || response.body!.isEmpty) { _acceptAndTerminate(response, 400, DartSIP_C.CausesType.MISSING_SDP); _failed('remote', null, null, response, 400, @@ -2506,8 +2529,6 @@ class RTCSession extends EventManager implements Owner { return; } - _status = C.STATUS_CONFIRMED; - logger.d('emit "sdp"'); emit(EventSdp(originator: 'remote', type: 'answer', sdp: response.body)); @@ -2567,6 +2588,113 @@ class RTCSession extends EventManager implements Owner { options = options ?? {}; + List extraHeaders = options['extraHeaders'] != null + ? utils.cloneArray(options['extraHeaders']) + : []; + EventManager eventHandlers = options['eventHandlers'] ?? EventManager(); + Map? rtcOfferConstraints = + options['rtcOfferConstraints'] ?? _rtcOfferConstraints; + + bool succeeded = false; + + extraHeaders.add('Contact: $_contact'); + extraHeaders.add('Content-Type: application/sdp'); + + // Session Timers. + if (_sessionTimers.running) { + extraHeaders.add( + 'Session-Expires: ${_sessionTimers.currentExpires};refresher=${_sessionTimers.refresher ? 'uac' : 'uas'}'); + } + + void onFailed([dynamic response]) { + eventHandlers.emit(EventCallFailed(session: this, response: response)); + } + + void onSucceeded(IncomingResponse? response) async { + if (_status == C.STATUS_TERMINATED) { + return; + } + + sendRequest(SipMethod.ACK); + + // If it is a 2XX retransmission exit now. + if (succeeded != null) { + return; + } + + // Handle Session Timers. + _handleSessionTimersInIncomingResponse(response); + + // Must have SDP answer. + if (response!.body == null || response.body!.isEmpty) { + onFailed(); + return; + } else if (response.getHeader('Content-Type') != 'application/sdp') { + onFailed(); + return; + } + + logger.d('emit "sdp"'); + emit(EventSdp(originator: 'remote', type: 'answer', sdp: response.body)); + + RTCSessionDescription answer = + RTCSessionDescription(response.body, 'answer'); + + try { + await _connection!.setRemoteDescription(answer); + eventHandlers.emit(EventSucceeded(response: response)); + } catch (error) { + onFailed(); + logger.e( + 'emit "peerconnection:setremotedescriptionfailed" [error:${error.toString()}]'); + emit(EventSetRemoteDescriptionFailed(exception: error)); + } + } + + try { + RTCSessionDescription desc = + await _createLocalDescription('offer', rtcOfferConstraints); + String? sdp = _mangleOffer(desc.sdp); + logger.d('emit "sdp"'); + emit(EventSdp(originator: 'local', type: 'offer', sdp: sdp)); + + EventManager handlers = EventManager(); + handlers.on(EventOnSuccessResponse(), (EventOnSuccessResponse event) { + onSucceeded(event.response as IncomingResponse?); + succeeded = true; + }); + handlers.on(EventOnErrorResponse(), (EventOnErrorResponse event) { + onFailed(event.response); + }); + handlers.on(EventOnTransportError(), (EventOnTransportError event) { + onTransportError(); // Do nothing because session ends. + }); + handlers.on(EventOnRequestTimeout(), (EventOnRequestTimeout event) { + onRequestTimeout(); // Do nothing because session ends. + }); + handlers.on(EventOnDialogError(), (EventOnDialogError event) { + onDialogError(); // Do nothing because session ends. + }); + + sendRequest(SipMethod.INVITE, { + 'extraHeaders': extraHeaders, + 'body': sdp, + 'eventHandlers': handlers + }); + } catch (e, s) { + logger.e(e.toString(), error: e, stackTrace: s); + onFailed(); + } + } + + /** + * Send Re-INVITE + */ + void _sendVideoUpgradeReinvite([Map? options]) async { + logger.d('sendVideoUpgradeReinvite()'); + + options = options ?? {}; + List extraHeaders = options['extraHeaders'] != null ? utils.cloneArray(options['extraHeaders']) : []; @@ -2577,26 +2705,22 @@ class RTCSession extends EventManager implements Owner { Map mediaConstraints = options['mediaConstraints'] ?? {}; + mediaConstraints['audio'] = false; + dynamic sdpSemantics = options['pcConfig']?['sdpSemantics'] ?? 'unified-plan'; - bool hasVideo = (options['mediaConstraints']?['video'] ?? false) != false; - try { MediaStream localStream = await navigator.mediaDevices.getUserMedia(mediaConstraints); _localMediaStreamLocallyGenerated = true; - _localMediaStream = localStream; - - emit( - EventStream(session: this, originator: 'local', stream: localStream)); switch (sdpSemantics) { case 'unified-plan': localStream.getTracks().forEach((MediaStreamTrack track) { - if (track.kind == 'video' && hasVideo) { + if (track.kind == 'video') _connection!.addTrack(track, localStream); - } + _localMediaStream?.addTrack(track); }); break; case 'plan-b': @@ -2606,6 +2730,9 @@ class RTCSession extends EventManager implements Owner { logger.e('Unkown sdp semantics $sdpSemantics'); throw Exceptions.NotReadyError('Unkown sdp semantics $sdpSemantics'); } + + emit(EventStream( + session: this, originator: 'local', stream: _localMediaStream)); } catch (error) { if (_status == C.STATUS_TERMINATED) { throw Exceptions.InvalidStateError('terminated'); diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index a27d44ae..3e6dc480 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -123,7 +123,7 @@ class SIPUAHelper extends EventManager { required bool voiceOnly, Map? options, bool useUpdate = false, - Function(IncomingMessage)? done, + Function(IncomingMessage?)? done, }) async { Map finalOptions = options ?? buildCallOptions(voiceOnly); call.renegotiate(options: finalOptions, useUpdate: useUpdate, done: done); @@ -600,7 +600,7 @@ class Call { void renegotiate({ required Map? options, bool useUpdate = false, - Function(IncomingMessage)? done, + Function(IncomingMessage?)? done, }) { assert(_session != null, 'ERROR(renegotiate): rtc session is invalid!'); _session.renegotiate(options: options, useUpdate: useUpdate, done: done); From 0eb206e974d23c41d7537a0e46d9cc4b7b5c396f Mon Sep 17 00:00:00 2001 From: Emmanuel Schmidbauer Date: Sun, 1 Dec 2024 20:18:41 -0500 Subject: [PATCH 03/35] sip_ua_helper: add sendOptions (#476) --- lib/src/sip_ua_helper.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 3e6dc480..1ed2ae1f 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -14,6 +14,7 @@ import 'event_manager/event_manager.dart'; import 'event_manager/subscriber_events.dart'; import 'logger.dart'; import 'message.dart'; +import 'options.dart'; import 'rtc_session.dart'; import 'rtc_session/refer_subscriber.dart'; import 'sip_message.dart'; @@ -413,6 +414,10 @@ class SIPUAHelper extends EventManager { return _ua!.sendMessage(target, body, options, params); } + Options sendOptions(String target, String body, Map? params) { + return _ua!.sendOptions(target, body, params); + } + void subscribe(String target, String event, String contentType) { Subscriber s = _ua!.subscribe(target, event, contentType); From d7047176e0b84794bfe7b78fa6b9e0bff0edcc19 Mon Sep 17 00:00:00 2001 From: HVaidehi <87645715+HVaidehi@users.noreply.github.com> Date: Mon, 2 Dec 2024 06:53:28 +0530 Subject: [PATCH 04/35] Update callscreen.dart (#427) IOS video call issue on acceptance fixed Co-authored-by: CloudWebRTC --- example/lib/src/callscreen.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/lib/src/callscreen.dart b/example/lib/src/callscreen.dart index 4bfba3dc..a6b6ebe9 100644 --- a/example/lib/src/callscreen.dart +++ b/example/lib/src/callscreen.dart @@ -230,6 +230,7 @@ class _MyCallScreenWidget extends State 'minFrameRate': '30', }, 'facingMode': 'user', + 'optional': [], } : false }; @@ -242,6 +243,9 @@ class _MyCallScreenWidget extends State await navigator.mediaDevices.getUserMedia(mediaConstraints); mediaStream.addTrack(userStream.getAudioTracks()[0], addToNative: true); } else { + if (!remoteHasVideo) { + mediaConstraints['video'] = false; + } mediaStream = await navigator.mediaDevices.getUserMedia(mediaConstraints); } From 27d7ba9b6a1a5196b8f3202460243441b724986d Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Wed, 18 Dec 2024 13:54:03 +0000 Subject: [PATCH 05/35] Dependency updates --- example/ios/Podfile | 2 +- example/pubspec.yaml | 4 ++-- pubspec.yaml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index c9339a03..ed164703 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 1e834d69..6c4c2372 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -25,14 +25,14 @@ dependencies: path: ../ shared_preferences: ^2.2.0 permission_handler: ^11.1.0 - flutter_webrtc: ^0.10.4 + flutter_webrtc: ^0.12.4 provider: 6.1.2 logger: ^2.4.0 dev_dependencies: flutter_test: sdk: flutter - lints: ^3.0.0 + lints: ^4.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/pubspec.yaml b/pubspec.yaml index 1ebd9cd5..6535e6de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,8 +9,8 @@ environment: dependencies: collection: ^1.18.0 crypto: ^3.0.3 - flutter_webrtc: ^0.10.4 - intl: ^0.19.0 + flutter_webrtc: ^0.12.4 + intl: ^0.20.1 logger: ^2.0.2+1 path: ^1.6.4 random_string: ^2.3.1 @@ -22,5 +22,5 @@ dependencies: dev_dependencies: import_sorter: ^4.6.0 - lints: ^3.0.0 + lints: ^4.0.0 test: any From 523904e3abfa29e68b499d69dc8d84e1526b9c5f Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Wed, 18 Dec 2024 14:20:15 +0000 Subject: [PATCH 06/35] Build check fixes --- lib/sip_ua.dart | 2 -- lib/src/config.dart | 1 - lib/src/rtc_session.dart | 4 +--- lib/src/rtc_session/dtmf.dart | 1 - lib/src/sip_ua_helper.dart | 8 ++++---- lib/src/transports/tcp_socket.dart | 2 +- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/sip_ua.dart b/lib/sip_ua.dart index c7f2efe7..397defee 100644 --- a/lib/sip_ua.dart +++ b/lib/sip_ua.dart @@ -3,5 +3,3 @@ export 'src/sip_message.dart'; export 'src/sip_ua_helper.dart'; export 'src/transport_type.dart'; export 'src/uri.dart'; - - diff --git a/lib/src/config.dart b/lib/src/config.dart index d9849d85..4ece468f 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -7,7 +7,6 @@ import 'exceptions.dart' as Exceptions; import 'grammar.dart'; import 'logger.dart'; import 'transports/web_socket.dart'; -import 'uri.dart'; import 'utils.dart' as Utils; // Default settings. diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index d9937d07..26e2dc33 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -22,11 +22,9 @@ import 'rtc_session/info.dart' as RTCSession_Info; import 'rtc_session/info.dart'; import 'rtc_session/refer_notifier.dart'; import 'rtc_session/refer_subscriber.dart'; -import 'sip_message.dart'; import 'timers.dart'; import 'transactions/transaction_base.dart'; import 'ua.dart'; -import 'uri.dart'; import 'utils.dart' as utils; class C { @@ -3064,7 +3062,7 @@ class RTCSession extends EventManager implements Owner { } /// SDP offers may contain text media channels. e.g. Older clients using linphone. - /// + /// /// WebRTC does not support text media channels, so remove them. String? _sdpOfferToWebRTC(String? sdpInput) { if (sdpInput == null) { diff --git a/lib/src/rtc_session/dtmf.dart b/lib/src/rtc_session/dtmf.dart index df8269e3..e1fe8f16 100644 --- a/lib/src/rtc_session/dtmf.dart +++ b/lib/src/rtc_session/dtmf.dart @@ -7,7 +7,6 @@ import '../event_manager/internal_events.dart'; import '../exceptions.dart' as Exceptions; import '../logger.dart'; import '../rtc_session.dart' as rtc; -import '../sip_message.dart'; import '../utils.dart' as Utils; class C { diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 1ed2ae1f..fba7de89 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -17,7 +17,6 @@ import 'message.dart'; import 'options.dart'; import 'rtc_session.dart'; import 'rtc_session/refer_subscriber.dart'; -import 'sip_message.dart'; import 'stack_trace_nj.dart'; import 'subscriber.dart'; import 'transports/web_socket.dart'; @@ -180,9 +179,9 @@ class SIPUAHelper extends EventManager { _settings.instance_id = uaSettings.instanceId; _settings.registrar_server = uaSettings.registrarServer; _settings.contact_uri = uaSettings.contact_uri; - _settings.connection_recovery_max_interval = + _settings.connection_recovery_max_interval = uaSettings.connectionRecoveryMaxInterval; - _settings.connection_recovery_min_interval = + _settings.connection_recovery_min_interval = uaSettings.connectionRecoveryMinInterval; _settings.terminateOnAudioMediaPortZero = uaSettings.terminateOnMediaPortZero; @@ -414,7 +413,8 @@ class SIPUAHelper extends EventManager { return _ua!.sendMessage(target, body, options, params); } - Options sendOptions(String target, String body, Map? params) { + Options sendOptions( + String target, String body, Map? params) { return _ua!.sendOptions(target, body, params); } diff --git a/lib/src/transports/tcp_socket.dart b/lib/src/transports/tcp_socket.dart index 1b5b1e6e..85aa9ecc 100644 --- a/lib/src/transports/tcp_socket.dart +++ b/lib/src/transports/tcp_socket.dart @@ -179,7 +179,7 @@ class SIPUATcpSocket extends SIPUASocketInterface { @override String? get url { - if (_host == null || _port == null) { + if (_host == null || _port == null) { return null; } return '$_host:$_port'; From adf4aa1cfc01ab60f8f89780137d4bc77a2b303d Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Wed, 18 Dec 2024 14:32:32 +0000 Subject: [PATCH 07/35] Push to master checks --- .github/workflows/pushMaster.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/pushMaster.yaml diff --git a/.github/workflows/pushMaster.yaml b/.github/workflows/pushMaster.yaml new file mode 100644 index 00000000..9ded7497 --- /dev/null +++ b/.github/workflows/pushMaster.yaml @@ -0,0 +1,27 @@ +name: Push To Master + +on: + push: + branches: + - master + +jobs: + build: + name: Build Checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + - name: Install project dependencies + run: flutter pub get + - name: Dart Format Check + run: dart format lib/ test/ --set-exit-if-changed + - name: Import Sorter Check + run: flutter pub run import_sorter:main --no-comments --exit-if-changed + - name: Dart Analyze Check + run: flutter analyze + - name: Dart Test Check + run: flutter test From 8d73bbf6c7201c2126437d4b2c2f9b2276e755f1 Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Wed, 18 Dec 2024 14:55:02 +0000 Subject: [PATCH 08/35] import sorting fix --- lib/src/sip_ua_helper.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index fba7de89..f2562b07 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:logger/logger.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; + import 'package:sip_ua/sip_ua.dart'; import 'package:sip_ua/src/event_manager/internal_events.dart'; import 'package:sip_ua/src/map_helper.dart'; From 2ba5b69a52bbfb09e6d60f5388739480288fa553 Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Wed, 18 Dec 2024 14:57:20 +0000 Subject: [PATCH 09/35] Push to main check file fix --- .github/workflows/pushMaster.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pushMaster.yaml b/.github/workflows/pushMaster.yaml index 9ded7497..4a80bff8 100644 --- a/.github/workflows/pushMaster.yaml +++ b/.github/workflows/pushMaster.yaml @@ -3,7 +3,7 @@ name: Push To Master on: push: branches: - - master + - main jobs: build: From be291a32c714d0499a9ce328f33c1d22489036e9 Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Wed, 18 Dec 2024 15:50:37 +0000 Subject: [PATCH 10/35] Dart analyze check fixes --- example/lib/src/dialpad.dart | 8 ++++---- example/lib/src/register.dart | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/lib/src/dialpad.dart b/example/lib/src/dialpad.dart index 79db8747..6ec93de6 100644 --- a/example/lib/src/dialpad.dart +++ b/example/lib/src/dialpad.dart @@ -172,7 +172,7 @@ class _MyDialPadWidget extends State List _buildDialPad() { Color? textFieldColor = - Theme.of(context).textTheme.bodyMedium?.color?.withOpacity(0.5); + Theme.of(context).textTheme.bodyMedium?.color?.withValues(alpha: 0.5); Color? textFieldFill = Theme.of(context).buttonTheme.colorScheme?.surfaceContainerLowest; return [ @@ -190,15 +190,15 @@ class _MyDialPadWidget extends State filled: true, fillColor: textFieldFill, border: OutlineInputBorder( - borderSide: BorderSide(color: Colors.blue.withOpacity(0.5)), + borderSide: BorderSide(color: Colors.blue.withValues(alpha: 0.5)), borderRadius: BorderRadius.circular(5), ), enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.blue.withOpacity(0.5)), + borderSide: BorderSide(color: Colors.blue.withValues(alpha: 0.5)), borderRadius: BorderRadius.circular(5), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.blue.withOpacity(0.5)), + borderSide: BorderSide(color: Colors.blue.withValues(alpha: 0.5)), borderRadius: BorderRadius.circular(5), ), ), diff --git a/example/lib/src/register.dart b/example/lib/src/register.dart index f315bf3d..fecc2471 100644 --- a/example/lib/src/register.dart +++ b/example/lib/src/register.dart @@ -151,7 +151,7 @@ class _MyRegisterWidget extends State borderRadius: BorderRadius.circular(5), ); Color? textLabelColor = - Theme.of(context).textTheme.bodyMedium?.color?.withOpacity(0.5); + Theme.of(context).textTheme.bodyMedium?.color?.withValues(alpha: 0.5); return Scaffold( appBar: AppBar( title: Text("SIP Account"), From 014d86663d82432b413ef2e734f7a0c086bd3473 Mon Sep 17 00:00:00 2001 From: Victor Uvarov Date: Mon, 2 Dec 2024 08:51:46 -0800 Subject: [PATCH 11/35] use rethrow when possible --- analysis_options.yaml | 1 - lib/src/config.dart | 2 +- lib/src/rtc_session.dart | 4 ++-- lib/src/transports/tcp_socket.dart | 2 +- lib/src/transports/web_socket.dart | 2 +- lib/src/transports/websocket_dart_impl.dart | 2 +- lib/src/ua.dart | 4 ++-- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 3a1f08c9..9b708ede 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -53,7 +53,6 @@ analyzer: avoid_types_as_parameter_names: ignore empty_catches: ignore unawaited_futures: ignore - use_rethrow_when_possible: ignore unused_import: ignore must_be_immutable: ignore todo: ignore diff --git a/lib/src/config.dart b/lib/src/config.dart index 4ece468f..102e7ef5 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -286,6 +286,6 @@ void load(Settings src, Settings? dst) { }); } catch (e) { logger.e('Failed to load config: ${e.toString()}'); - throw e; + rethrow; } } diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 26e2dc33..b8dfa379 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -2354,7 +2354,7 @@ class RTCSession extends EventManager implements Owner { 'User Denied Media Access'); logger.e('emit "getusermediafailed" [error:${error.toString()}]'); emit(EventGetUserMediaFailed(exception: error)); - throw error; + rethrow; } } @@ -2406,7 +2406,7 @@ class RTCSession extends EventManager implements Owner { return; } logger.e('Failed to _sendInitialRequest: ${error.toString()}'); - throw error; + rethrow; } } diff --git a/lib/src/transports/tcp_socket.dart b/lib/src/transports/tcp_socket.dart index 85aa9ecc..ce3832c7 100644 --- a/lib/src/transports/tcp_socket.dart +++ b/lib/src/transports/tcp_socket.dart @@ -140,7 +140,7 @@ class SIPUATcpSocket extends SIPUASocketInterface { return true; } catch (error) { logger.e('send() | error sending message: $error'); - throw error; + rethrow; } } diff --git a/lib/src/transports/web_socket.dart b/lib/src/transports/web_socket.dart index 66123a00..57d610c8 100644 --- a/lib/src/transports/web_socket.dart +++ b/lib/src/transports/web_socket.dart @@ -142,7 +142,7 @@ class SIPUAWebSocket extends SIPUASocketInterface { return true; } catch (error) { logger.e('send() | error sending message: $error'); - throw error; + rethrow; } } diff --git a/lib/src/transports/websocket_dart_impl.dart b/lib/src/transports/websocket_dart_impl.dart index 6516515c..1ec30b21 100644 --- a/lib/src/transports/websocket_dart_impl.dart +++ b/lib/src/transports/websocket_dart_impl.dart @@ -118,7 +118,7 @@ class SIPUAWebSocketImpl { return webSocket; } catch (e) { logger.e('error $e'); - throw e; + rethrow; } } } diff --git a/lib/src/ua.dart b/lib/src/ua.dart index 92e581bb..6d0ed965 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -95,7 +95,7 @@ class UA extends EventManager { } catch (e) { _status = C.STATUS_NOT_READY; _error = C.CONFIGURATION_ERROR; - throw e; + rethrow; } // Initialize registrator. @@ -821,7 +821,7 @@ class UA extends EventManager { try { config.load(configuration, _configuration); } catch (e) { - throw e; + rethrow; } // Post Configuration Process. From fe8848a0071196195aa08418eb20e21c3e4ffef5 Mon Sep 17 00:00:00 2001 From: Victor Uvarov Date: Fri, 27 Dec 2024 10:18:03 -0800 Subject: [PATCH 12/35] refactor/class c to enum (#509) * RTCSessionState * subscriber enums * socket enums * remove transport constants * ua status and error * fix type passes * run formatter * fix non relative imports --------- Co-authored-by: Victor Uvarov --- lib/src/request_sender.dart | 3 +- lib/src/rtc_session.dart | 295 ++++++++++++++++--------------- lib/src/rtc_session/dtmf.dart | 10 +- lib/src/rtc_session/info.dart | 10 +- lib/src/socket_transport.dart | 102 ++++++----- lib/src/subscriber.dart | 133 +++++++------- lib/src/transport_constants.dart | 19 -- lib/src/ua.dart | 50 +++--- 8 files changed, 308 insertions(+), 314 deletions(-) delete mode 100644 lib/src/transport_constants.dart diff --git a/lib/src/request_sender.dart b/lib/src/request_sender.dart index 1d2f53cc..42ec8cb8 100644 --- a/lib/src/request_sender.dart +++ b/lib/src/request_sender.dart @@ -9,7 +9,6 @@ import 'transactions/ack_client.dart'; import 'transactions/invite_client.dart'; import 'transactions/non_invite_client.dart'; import 'transactions/transaction_base.dart'; -import 'ua.dart' as UAC; import 'ua.dart'; // Default event handlers. @@ -25,7 +24,7 @@ class RequestSender { _staled = false; // If ua is in closing process or even closed just allow sending Bye and ACK. - if (ua.status == UAC.C.STATUS_USER_CLOSED && + if (ua.status == UAStatus.userClosed && (_method != SipMethod.BYE || _method != SipMethod.ACK)) { _eventHandlers.emit(EventOnTransportError()); } diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index b8dfa379..27bf15f0 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -1,12 +1,9 @@ import 'dart:async'; -import 'dart:convert'; -import 'package:crypto/crypto.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; -import 'package:sdp_transform/sdp_transform.dart'; -import 'package:sip_ua/sip_ua.dart'; +import '../sip_ua.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'dialog.dart'; @@ -27,18 +24,17 @@ import 'transactions/transaction_base.dart'; import 'ua.dart'; import 'utils.dart' as utils; -class C { - // RTCSession states. - static const int STATUS_NULL = 0; - static const int STATUS_INVITE_SENT = 1; - static const int STATUS_1XX_RECEIVED = 2; - static const int STATUS_INVITE_RECEIVED = 3; - static const int STATUS_WAITING_FOR_ANSWER = 4; - static const int STATUS_ANSWERED = 5; - static const int STATUS_WAITING_FOR_ACK = 6; - static const int STATUS_CANCELED = 7; - static const int STATUS_TERMINATED = 8; - static const int STATUS_CONFIRMED = 9; +enum RtcSessionState { + none, // STATUS_NULL + inviteSent, // STATUS_INVITE_SENT + provisionalResponse, // STATUS_1XX_RECEIVED + inviteReceived, // STATUS_INVITE_RECEIVED + waitingForAnswer, // STATUS_WAITING_FOR_ANSWER + answered, // STATUS_ANSWERED + waitingForAck, // STATUS_WAITING_FOR_ACK + canceled, // STATUS_CANCELED + terminated, // STATUS_TERMINATED + confirmed, // STATUS_CONFIRMED } /** @@ -84,7 +80,7 @@ class RTCSession extends EventManager implements Owner { final UA _ua; String? _id; - int _status = C.STATUS_NULL; + RtcSessionState _state = RtcSessionState.none; Dialog? _dialog; final Map _earlyDialogs = {}; String? _contact; @@ -167,7 +163,7 @@ class RTCSession extends EventManager implements Owner { RTCPeerConnection? get connection => _connection; @override - int get TerminatedCode => C.STATUS_TERMINATED; + int get TerminatedCode => RtcSessionState.terminated.index; RTCDTMFSender get dtmfSender => _connection!.createDtmfSender(_localMediaStream!.getAudioTracks()[0]); @@ -188,15 +184,17 @@ class RTCSession extends EventManager implements Owner { UA get ua => _ua; @override - int get status => _status; + int get status => _state.index; + + RtcSessionState get state => _state; bool isInProgress() { - switch (_status) { - case C.STATUS_NULL: - case C.STATUS_INVITE_SENT: - case C.STATUS_1XX_RECEIVED: - case C.STATUS_INVITE_RECEIVED: - case C.STATUS_WAITING_FOR_ANSWER: + switch (_state) { + case RtcSessionState.none: + case RtcSessionState.inviteSent: + case RtcSessionState.provisionalResponse: + case RtcSessionState.inviteReceived: + case RtcSessionState.waitingForAnswer: return true; default: return false; @@ -204,10 +202,10 @@ class RTCSession extends EventManager implements Owner { } bool isEstablished() { - switch (_status) { - case C.STATUS_ANSWERED: - case C.STATUS_WAITING_FOR_ACK: - case C.STATUS_CONFIRMED: + switch (_state) { + case RtcSessionState.answered: + case RtcSessionState.waitingForAck: + case RtcSessionState.confirmed: return true; default: return false; @@ -215,9 +213,9 @@ class RTCSession extends EventManager implements Owner { } bool isEnded() { - switch (_status) { - case C.STATUS_CANCELED: - case C.STATUS_TERMINATED: + switch (_state) { + case RtcSessionState.canceled: + case RtcSessionState.terminated: return true; default: return false; @@ -262,8 +260,8 @@ class RTCSession extends EventManager implements Owner { } // Check Session Status. - if (_status != C.STATUS_NULL) { - throw Exceptions.InvalidStateError(_status); + if (_state != RtcSessionState.none) { + throw Exceptions.InvalidStateError(_state.name); } // Check WebRTC support. @@ -367,7 +365,7 @@ class RTCSession extends EventManager implements Owner { } // Session parameter initialization. - _status = C.STATUS_INVITE_RECEIVED; + _state = RtcSessionState.inviteReceived; _from_tag = request.from_tag; _id = request.call_id! + _from_tag!; _request = request; @@ -403,7 +401,7 @@ class RTCSession extends EventManager implements Owner { _late_sdp = true; } - _status = C.STATUS_WAITING_FOR_ANSWER; + _state = RtcSessionState.waitingForAnswer; // Set userNoAnswerTimer. _timers.userNoAnswerTimer = setTimeout(() { @@ -417,7 +415,7 @@ class RTCSession extends EventManager implements Owner { */ if (expires != null) { _timers.expiresTimer = setTimeout(() { - if (_status == C.STATUS_WAITING_FOR_ANSWER) { + if (_state == RtcSessionState.waitingForAnswer) { request.reply(487); _failed('system', null, null, null, 487, DartSIP_C.CausesType.EXPIRES, 'Timeout'); @@ -439,7 +437,7 @@ class RTCSession extends EventManager implements Owner { _newRTCSession('remote', request); // The user may have rejected the call in the 'newRTCSession' event. - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } @@ -492,8 +490,8 @@ class RTCSession extends EventManager implements Owner { } // Check Session status. - if (_status != C.STATUS_WAITING_FOR_ANSWER) { - throw Exceptions.InvalidStateError(_status); + if (_state != RtcSessionState.waitingForAnswer) { + throw Exceptions.InvalidStateError(_state.name); } // Session Timers. @@ -507,7 +505,7 @@ class RTCSession extends EventManager implements Owner { } } - _status = C.STATUS_ANSWERED; + _state = RtcSessionState.answered; // An error on dialog creation will fire 'failed' event. if (!_createDialog(request, 'UAS')) { @@ -597,7 +595,7 @@ class RTCSession extends EventManager implements Owner { stream = await navigator.mediaDevices.getUserMedia(mediaConstraints); emit(EventStream(session: this, originator: 'local', stream: stream)); } catch (error) { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } request.reply(480); @@ -615,7 +613,7 @@ class RTCSession extends EventManager implements Owner { } } - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } @@ -666,7 +664,7 @@ class RTCSession extends EventManager implements Owner { } // Create local description. - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } @@ -684,7 +682,7 @@ class RTCSession extends EventManager implements Owner { throw Exceptions.TypeError('_createLocalDescription() failed'); } - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } @@ -692,7 +690,7 @@ class RTCSession extends EventManager implements Owner { try { _handleSessionTimersInIncomingRequest(request, extraHeaders); request.reply(200, null, extraHeaders, desc.sdp, () { - _status = C.STATUS_WAITING_FOR_ACK; + _state = RtcSessionState.waitingForAck; _setInvite2xxTimer(request, desc.sdp); _setACKTimer(); _accepted('local'); @@ -701,7 +699,7 @@ class RTCSession extends EventManager implements Owner { DartSIP_C.CausesType.CONNECTION_ERROR, 'Transport Error'); }); } catch (error, s) { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } logger.e('Failed to answer(): ${error.toString()}', @@ -729,15 +727,15 @@ class RTCSession extends EventManager implements Owner { String? reason_phrase = options['reason_phrase'] as String?; // Check Session Status. - if (_status == C.STATUS_TERMINATED) { - throw Exceptions.InvalidStateError(_status); + if (_state == RtcSessionState.terminated) { + throw Exceptions.InvalidStateError(_state.name); } - switch (_status) { + switch (_state) { // - UAC - - case C.STATUS_NULL: - case C.STATUS_INVITE_SENT: - case C.STATUS_1XX_RECEIVED: + case RtcSessionState.none: + case RtcSessionState.inviteSent: + case RtcSessionState.provisionalResponse: logger.d('canceling session'); if (status_code != null && (status_code < 200 || status_code >= 700)) { @@ -748,23 +746,24 @@ class RTCSession extends EventManager implements Owner { } // Check Session Status. - if (_status == C.STATUS_NULL || _status == C.STATUS_INVITE_SENT) { + if (_state == RtcSessionState.none || + _state == RtcSessionState.inviteSent) { _is_canceled = true; _cancel_reason = cancel_reason; - } else if (_status == C.STATUS_1XX_RECEIVED) { + } else if (_state == RtcSessionState.provisionalResponse) { _request.cancel(cancel_reason ?? ''); } - _status = C.STATUS_CANCELED; + _state = RtcSessionState.canceled; cancel_reason = cancel_reason ?? 'Canceled by local'; status_code = status_code ?? 100; _failed('local', null, null, null, status_code, DartSIP_C.CausesType.CANCELED, cancel_reason); - break; + break; // - UAS - - case C.STATUS_WAITING_FOR_ANSWER: - case C.STATUS_ANSWERED: + case RtcSessionState.waitingForAnswer: + case RtcSessionState.answered: logger.d('rejecting session'); status_code = status_code ?? 480; @@ -778,9 +777,8 @@ class RTCSession extends EventManager implements Owner { _failed('local', null, null, null, status_code, DartSIP_C.CausesType.REJECTED, reason_phrase); break; - - case C.STATUS_WAITING_FOR_ACK: - case C.STATUS_CONFIRMED: + case RtcSessionState.waitingForAck: + case RtcSessionState.confirmed: logger.d('terminating session'); reason_phrase = options['reason_phrase'] as String? ?? @@ -800,7 +798,7 @@ class RTCSession extends EventManager implements Owner { * until it has received an ACK for its 2xx response or until the server * transaction times out." */ - if (_status == C.STATUS_WAITING_FOR_ACK && + if (_state == RtcSessionState.waitingForAck && _direction == 'incoming' && _request.server_transaction.state != TransactionState.TERMINATED) { /// Save the dialog for later restoration. @@ -809,10 +807,10 @@ class RTCSession extends EventManager implements Owner { // Send the BYE as soon as the ACK is received... receiveRequest = (IncomingMessage request) { if (request.method == SipMethod.ACK) { - sendRequest(SipMethod.BYE, { - 'extraHeaders': extraHeaders, - 'body': body - }); + sendRequest( + SipMethod.BYE, + {'extraHeaders': extraHeaders, 'body': body}, + ); dialog.terminate(); } }; @@ -831,12 +829,14 @@ class RTCSession extends EventManager implements Owner { }); _ended( - 'local', - null, - ErrorCause( - cause: cause as String?, - status_code: status_code, - reason_phrase: reason_phrase)); + 'local', + null, + ErrorCause( + cause: cause as String?, + status_code: status_code, + reason_phrase: reason_phrase, + ), + ); // Restore the dialog into 'this' in order to be able to send the in-dialog BYE :-). _dialog = dialog; @@ -849,13 +849,17 @@ class RTCSession extends EventManager implements Owner { reason_phrase = reason_phrase ?? 'Terminated by local'; status_code = status_code ?? 200; _ended( - 'local', - null, - ErrorCause( - cause: cause as String?, - status_code: status_code, - reason_phrase: reason_phrase)); + 'local', + null, + ErrorCause( + cause: cause as String?, + status_code: status_code, + reason_phrase: reason_phrase), + ); } + break; + default: + break; } } @@ -878,8 +882,9 @@ class RTCSession extends EventManager implements Owner { } // Check Session Status. - if (_status != C.STATUS_CONFIRMED && _status != C.STATUS_WAITING_FOR_ACK) { - throw Exceptions.InvalidStateError(_status); + if (_state != RtcSessionState.confirmed && + _state != RtcSessionState.waitingForAck) { + throw Exceptions.InvalidStateError(_state); } // Convert to string. @@ -932,7 +937,7 @@ class RTCSession extends EventManager implements Owner { if (tone == ',') { // queue the delay dtmfFuture = dtmfFuture.then((_) async { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } await Future.delayed(Duration(milliseconds: 2000), () {}); @@ -940,7 +945,7 @@ class RTCSession extends EventManager implements Owner { } else { // queue playing the tone dtmfFuture = dtmfFuture.then((_) async { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } @@ -965,8 +970,9 @@ class RTCSession extends EventManager implements Owner { logger.d('sendInfo()'); // Check Session Status. - if (_status != C.STATUS_CONFIRMED && _status != C.STATUS_WAITING_FOR_ACK) { - throw Exceptions.InvalidStateError(_status); + if (_state != RtcSessionState.confirmed && + _state != RtcSessionState.waitingForAck) { + throw Exceptions.InvalidStateError(_state); } RTCSession_Info.Info info = RTCSession_Info.Info(this); @@ -1036,7 +1042,8 @@ class RTCSession extends EventManager implements Owner { options = options ?? {}; - if (_status != C.STATUS_WAITING_FOR_ACK && _status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.waitingForAck && + _state != RtcSessionState.confirmed) { return false; } @@ -1088,7 +1095,8 @@ class RTCSession extends EventManager implements Owner { options = options ?? {}; - if (_status != C.STATUS_WAITING_FOR_ACK && _status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.waitingForAck && + _state != RtcSessionState.confirmed) { return false; } @@ -1151,7 +1159,8 @@ class RTCSession extends EventManager implements Owner { dynamic sdpSemantics = options['pcConfig']?['sdpSemantics'] ?? 'unified-plan'; - if (_status != C.STATUS_WAITING_FOR_ACK && _status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.waitingForAck && + _state != RtcSessionState.confirmed) { return false; } @@ -1222,7 +1231,8 @@ class RTCSession extends EventManager implements Owner { dynamic originalTarget = target; - if (_status != C.STATUS_WAITING_FOR_ACK && _status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.waitingForAck && + _state != RtcSessionState.confirmed) { return null; } @@ -1284,9 +1294,9 @@ class RTCSession extends EventManager implements Owner { * Terminate the whole session in case the user didn't accept (or yet send the answer) * nor reject the request opening the session. */ - if (_status == C.STATUS_WAITING_FOR_ANSWER || - _status == C.STATUS_ANSWERED) { - _status = C.STATUS_CANCELED; + if (_state == RtcSessionState.waitingForAnswer || + _state == RtcSessionState.answered) { + _state = RtcSessionState.canceled; _request.reply(487); _failed('remote', null, request, null, 487, DartSIP_C.CausesType.CANCELED, request.reason_phrase); @@ -1295,11 +1305,11 @@ class RTCSession extends EventManager implements Owner { // Requests arriving here are in-dialog requests. switch (request.method) { case SipMethod.ACK: - if (_status != C.STATUS_WAITING_FOR_ACK) { + if (_state != RtcSessionState.waitingForAck) { return; } // Update signaling status. - _status = C.STATUS_CONFIRMED; + _state = RtcSessionState.confirmed; clearTimeout(_timers.ackTimer); clearTimeout(_timers.invite2xxTimer); @@ -1335,7 +1345,7 @@ class RTCSession extends EventManager implements Owner { } break; case SipMethod.BYE: - if (_status == C.STATUS_CONFIRMED) { + if (_state == RtcSessionState.confirmed) { request.reply(200); _ended( 'remote', @@ -1344,7 +1354,7 @@ class RTCSession extends EventManager implements Owner { cause: DartSIP_C.CausesType.BYE, status_code: 200, reason_phrase: 'BYE Received')); - } else if (_status == C.STATUS_INVITE_RECEIVED) { + } else if (_state == RtcSessionState.inviteReceived) { request.reply(200); _request.reply(487, 'BYE Received'); _ended( @@ -1359,7 +1369,7 @@ class RTCSession extends EventManager implements Owner { } break; case SipMethod.INVITE: - if (_status == C.STATUS_CONFIRMED) { + if (_state == RtcSessionState.confirmed) { if (request.hasHeader('replaces')) { _receiveReplaces(request); } else { @@ -1370,11 +1380,11 @@ class RTCSession extends EventManager implements Owner { } break; case SipMethod.INFO: - if (_status == C.STATUS_1XX_RECEIVED || - _status == C.STATUS_WAITING_FOR_ANSWER || - _status == C.STATUS_ANSWERED || - _status == C.STATUS_WAITING_FOR_ACK || - _status == C.STATUS_CONFIRMED) { + if (_state == RtcSessionState.provisionalResponse || + _state == RtcSessionState.waitingForAnswer || + _state == RtcSessionState.answered || + _state == RtcSessionState.waitingForAck || + _state == RtcSessionState.confirmed) { String? contentType = request.getHeader('content-type'); if (contentType != null && contentType.contains(RegExp(r'^application\/dtmf-relay', @@ -1390,21 +1400,21 @@ class RTCSession extends EventManager implements Owner { } break; case SipMethod.UPDATE: - if (_status == C.STATUS_CONFIRMED) { + if (_state == RtcSessionState.confirmed) { _receiveUpdate(request); } else { request.reply(403, 'Wrong Status'); } break; case SipMethod.REFER: - if (_status == C.STATUS_CONFIRMED) { + if (_state == RtcSessionState.confirmed) { _receiveRefer(request); } else { request.reply(403, 'Wrong Status'); } break; case SipMethod.NOTIFY: - if (_status == C.STATUS_CONFIRMED) { + if (_state == RtcSessionState.confirmed) { _receiveNotify(request); } else { request.reply(403, 'Wrong Status'); @@ -1421,7 +1431,7 @@ class RTCSession extends EventManager implements Owner { */ void onTransportError() { logger.e('onTransportError()'); - if (_status != C.STATUS_TERMINATED) { + if (_state != RtcSessionState.terminated) { terminate({ 'status_code': 500, 'reason_phrase': DartSIP_C.CausesType.CONNECTION_ERROR, @@ -1433,7 +1443,7 @@ class RTCSession extends EventManager implements Owner { void onRequestTimeout() { logger.e('onRequestTimeout()'); - if (_status != C.STATUS_TERMINATED) { + if (_state != RtcSessionState.terminated) { terminate({ 'status_code': 408, 'reason_phrase': DartSIP_C.CausesType.REQUEST_TIMEOUT, @@ -1445,7 +1455,7 @@ class RTCSession extends EventManager implements Owner { void onDialogError() { logger.e('onDialogError()'); - if (_status != C.STATUS_TERMINATED) { + if (_state != RtcSessionState.terminated) { terminate({ 'status_code': 500, 'reason_phrase': DartSIP_C.CausesType.DIALOG_ERROR, @@ -1499,10 +1509,10 @@ class RTCSession extends EventManager implements Owner { void _close() async { logger.d('close()'); - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } - _status = C.STATUS_TERMINATED; + _state = RtcSessionState.terminated; // Terminate RTC. if (_connection != null) { try { @@ -1563,7 +1573,7 @@ class RTCSession extends EventManager implements Owner { int timeout = Timers.T1; void invite2xxRetransmission() { - if (_status != C.STATUS_WAITING_FOR_ACK) { + if (_state != RtcSessionState.waitingForAck) { return; } request.reply(200, null, ['Contact: $_contact'], body); @@ -1586,7 +1596,7 @@ class RTCSession extends EventManager implements Owner { */ void _setACKTimer() { _timers.ackTimer = setTimeout(() { - if (_status == C.STATUS_WAITING_FOR_ACK) { + if (_state == RtcSessionState.waitingForAck) { logger.d('no ACK received, terminating the session'); clearTimeout(_timers.invite2xxTimer); @@ -1712,7 +1722,7 @@ class RTCSession extends EventManager implements Owner { } Future ready() async { - if (!finished && _status != C.STATUS_TERMINATED) { + if (!finished && _state != RtcSessionState.terminated) { finished = true; _connection!.onIceCandidate = null; _connection!.onIceGatheringState = null; @@ -1853,7 +1863,7 @@ class RTCSession extends EventManager implements Owner { } request.reply(200, null, extraHeaders, sdp, () { - _status = C.STATUS_WAITING_FOR_ACK; + _state = RtcSessionState.waitingForAck; _setInvite2xxTimer(request, sdp); _setACKTimer(); }); @@ -1892,7 +1902,7 @@ class RTCSession extends EventManager implements Owner { String? reason_phrase = options['reason_phrase']; List extraHeaders = utils.cloneArray(options['extraHeaders']); - if (_status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.confirmed) { return false; } @@ -1909,7 +1919,7 @@ class RTCSession extends EventManager implements Owner { Future acceptReInvite(dynamic options) async { try { // Send answer. - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return false; } sendAnswer(desc.sdp); @@ -1957,7 +1967,7 @@ class RTCSession extends EventManager implements Owner { String reason_phrase = options['reason_phrase'] ?? ''; List extraHeaders = utils.cloneArray(options['extraHeaders']); - if (_status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.confirmed) { return false; } @@ -1999,7 +2009,7 @@ class RTCSession extends EventManager implements Owner { try { RTCSessionDescription desc = await _processInDialogSdpOffer(request); - if (_status == C.STATUS_TERMINATED) return; + if (_state == RtcSessionState.terminated) return; // Send answer. sendAnswer(desc.sdp); } catch (error) { @@ -2085,7 +2095,7 @@ class RTCSession extends EventManager implements Owner { RTCSessionDescription offer = RTCSessionDescription(processedSDP, 'offer'); - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } try { @@ -2101,7 +2111,7 @@ class RTCSession extends EventManager implements Owner { 'peerconnection.setRemoteDescription() failed'); } - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } @@ -2115,7 +2125,7 @@ class RTCSession extends EventManager implements Owner { // Create local description. - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } @@ -2155,8 +2165,8 @@ class RTCSession extends EventManager implements Owner { InitSuccessCallback? initCallback, Map options) { initCallback = (initCallback is Function) ? initCallback : null; - if (_status != C.STATUS_WAITING_FOR_ACK && - _status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.waitingForAck && + _state != RtcSessionState.confirmed) { return false; } @@ -2264,8 +2274,8 @@ class RTCSession extends EventManager implements Owner { logger.d('receiveReplaces()'); bool accept(InitSuccessCallback initCallback) { - if (_status != C.STATUS_WAITING_FOR_ACK && - _status != C.STATUS_CONFIRMED) { + if (_state != RtcSessionState.waitingForAck && + _state != RtcSessionState.confirmed) { return false; } @@ -2341,7 +2351,7 @@ class RTCSession extends EventManager implements Owner { stream = await navigator.mediaDevices.getUserMedia(mediaConstraints); emit(EventStream(session: this, originator: 'local', stream: stream)); } catch (error) { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } _failed( @@ -2358,7 +2368,7 @@ class RTCSession extends EventManager implements Owner { } } - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } @@ -2385,12 +2395,12 @@ class RTCSession extends EventManager implements Owner { try { RTCSessionDescription desc = await _createLocalDescription('offer', rtcOfferConstraints); - if (_is_canceled || _status == C.STATUS_TERMINATED) { + if (_is_canceled || _state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } _request.body = desc.sdp; - _status = C.STATUS_INVITE_SENT; + _state = RtcSessionState.inviteSent; logger.d('emit "sending" [request]'); @@ -2402,7 +2412,7 @@ class RTCSession extends EventManager implements Owner { logger.e(error.toString(), error: error, stackTrace: s); _failed('local', null, null, null, 500, DartSIP_C.CausesType.WEBRTC_ERROR, 'Can\'t create local SDP'); - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } logger.e('Failed to _sendInitialRequest: ${error.toString()}'); @@ -2412,7 +2422,7 @@ class RTCSession extends EventManager implements Owner { /// Reception of Response for Initial INVITE void _receiveInviteResponse(IncomingResponse? response) async { - logger.d('receiveInviteResponse() current status: $_status '); + logger.d('receiveInviteResponse() current status: $_state '); if (response == null) { logger.d('No response received'); @@ -2457,7 +2467,8 @@ class RTCSession extends EventManager implements Owner { return; } - if (_status != C.STATUS_INVITE_SENT && _status != C.STATUS_1XX_RECEIVED) { + if (_state != RtcSessionState.inviteSent && + _state != RtcSessionState.provisionalResponse) { return; } @@ -2465,7 +2476,7 @@ class RTCSession extends EventManager implements Owner { if (utils.test100(status_code)) { // 100 trying - _status = C.STATUS_1XX_RECEIVED; + _state = RtcSessionState.provisionalResponse; } else if (utils.test1XX(status_code)) { // 1XX // Do nothing with 1xx responses without To tag. @@ -2482,7 +2493,7 @@ class RTCSession extends EventManager implements Owner { } } - _status = C.STATUS_1XX_RECEIVED; + _state = RtcSessionState.provisionalResponse; _progress('remote', response, int.parse(status_code)); if (response.body == null || response.body!.isEmpty) { @@ -2504,7 +2515,7 @@ class RTCSession extends EventManager implements Owner { } } else if (utils.test2XX(status_code)) { // 2XX - _status = C.STATUS_CONFIRMED; + _state = RtcSessionState.confirmed; if (response.body == null || response.body!.isEmpty) { _acceptAndTerminate(response, 400, DartSIP_C.CausesType.MISSING_SDP); @@ -2609,7 +2620,7 @@ class RTCSession extends EventManager implements Owner { } void onSucceeded(IncomingResponse? response) async { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } @@ -2732,7 +2743,7 @@ class RTCSession extends EventManager implements Owner { emit(EventStream( session: this, originator: 'local', stream: _localMediaStream)); } catch (error) { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } request.reply(480); @@ -2765,7 +2776,7 @@ class RTCSession extends EventManager implements Owner { } void onSucceeded(IncomingResponse? response) async { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } @@ -2872,7 +2883,7 @@ class RTCSession extends EventManager implements Owner { } void onSucceeded(IncomingResponse? response) async { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } @@ -3002,7 +3013,7 @@ class RTCSession extends EventManager implements Owner { } // Update session status. - _status = C.STATUS_TERMINATED; + _state = RtcSessionState.terminated; } /** @@ -3168,7 +3179,7 @@ class RTCSession extends EventManager implements Owner { // I'm the refresher. if (_sessionTimers.refresher) { _sessionTimers.timer = setTimeout(() { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } @@ -3184,7 +3195,7 @@ class RTCSession extends EventManager implements Owner { // I'm not the refresher. else { _sessionTimers.timer = setTimeout(() { - if (_status == C.STATUS_TERMINATED) { + if (_state == RtcSessionState.terminated) { return; } diff --git a/lib/src/rtc_session/dtmf.dart b/lib/src/rtc_session/dtmf.dart index e1fe8f16..15f8de1a 100644 --- a/lib/src/rtc_session/dtmf.dart +++ b/lib/src/rtc_session/dtmf.dart @@ -1,12 +1,12 @@ import 'package:flutter_webrtc/flutter_webrtc.dart'; -import 'package:sip_ua/sip_ua.dart'; +import '../../sip_ua.dart'; import '../constants.dart'; import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../exceptions.dart' as Exceptions; import '../logger.dart'; -import '../rtc_session.dart' as rtc; +import '../rtc_session.dart'; import '../utils.dart' as Utils; class C { @@ -22,7 +22,7 @@ class DTMF extends EventManager { _mode = mode; } - final rtc.RTCSession _session; + final RTCSession _session; DtmfMode? _mode; String? _direction; String? _tone; @@ -45,8 +45,8 @@ class DTMF extends EventManager { _direction = 'outgoing'; // Check RTCSession Status. - if (_session.status != rtc.C.STATUS_CONFIRMED && - _session.status != rtc.C.STATUS_WAITING_FOR_ACK) { + if (_session.state != RtcSessionState.confirmed && + _session.state != RtcSessionState.waitingForAck) { throw Exceptions.InvalidStateError(_session.status); } diff --git a/lib/src/rtc_session/info.dart b/lib/src/rtc_session/info.dart index 6eca4c2d..d74f70de 100644 --- a/lib/src/rtc_session/info.dart +++ b/lib/src/rtc_session/info.dart @@ -1,15 +1,15 @@ -import 'package:sip_ua/src/sip_message.dart'; import '../constants.dart'; import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../exceptions.dart' as Exceptions; -import '../rtc_session.dart' as rtc; +import '../rtc_session.dart'; +import '../sip_message.dart'; import '../utils.dart' as utils; class Info extends EventManager { Info(this._session); - final rtc.RTCSession _session; + final RTCSession _session; String? _direction; String? _contentType; String? _body; @@ -29,8 +29,8 @@ class Info extends EventManager { } // Check RTCSession Status. - if (_session.status != rtc.C.STATUS_CONFIRMED && - _session.status != rtc.C.STATUS_WAITING_FOR_ACK) { + if (_session.state != RtcSessionState.confirmed && + _session.state != RtcSessionState.waitingForAck) { throw Exceptions.InvalidStateError(_session.status); } diff --git a/lib/src/socket_transport.dart b/lib/src/socket_transport.dart index d094524a..71a2f0b4 100644 --- a/lib/src/socket_transport.dart +++ b/lib/src/socket_transport.dart @@ -1,17 +1,35 @@ import 'dart:async'; import 'dart:math'; -import 'package:sip_ua/src/event_manager/events.dart'; -import 'package:sip_ua/src/transport_constants.dart'; -import 'package:sip_ua/src/transports/socket_interface.dart'; -import 'package:sip_ua/src/transports/tcp_socket.dart'; +import './event_manager/events.dart'; +import './transports/socket_interface.dart'; import 'exceptions.dart' as Exceptions; import 'logger.dart'; import 'stack_trace_nj.dart'; import 'timers.dart'; -import 'transports/web_socket.dart'; import 'utils.dart'; +enum TransportStatus { connected, connecting, disconnected } + +enum SocketStatus { ready, error } + +class SocketInfo { + SocketInfo({ + required this.socket, + required this.weight, + required this.status, + }); + + SIPUASocketInterface socket; + int weight; + SocketStatus status; +} + +const Map defaultRecoveryOptions = { + 'min_interval': 2, // minimum interval in seconds between recover attempts + 'max_interval': 30 // maximum interval in seconds between recover attempts +}; + /* * Manages one or multiple DartSIP.Socket instances. * Is reponsible for transport recovery logic among all socket instances. @@ -19,8 +37,10 @@ import 'utils.dart'; * @socket DartSIP::Socket instance */ class SocketTransport { - SocketTransport(List? sockets, - [Map recovery_options = C.recovery_options]) { + SocketTransport( + List? sockets, [ + Map recovery_options = defaultRecoveryOptions, + ]) { logger.d('Socket Transport new()'); _recovery_options = recovery_options; @@ -31,22 +51,22 @@ class SocketTransport { 'invalid argument: Must recieve atleast 1 web socket'); } - for (SIPUASocketInterface socket in sockets) { - _socketsMap.add({ - 'socket': socket, - 'weight': socket.weight ?? 0, - 'status': C.SOCKET_STATUS_READY - }); + for (final SIPUASocketInterface socket in sockets) { + _sockets.add(SocketInfo( + socket: socket, + weight: socket.weight ?? 0, + status: SocketStatus.ready, + )); } // Get the socket with higher weight. _getSocket(); } - int status = C.STATUS_DISCONNECTED; + TransportStatus status = TransportStatus.disconnected; // Current socket. late SIPUASocketInterface socket; // Socket collection. - final List> _socketsMap = >[]; + final List _sockets = []; late Map _recovery_options; int _recover_attempts = 0; Timer? _recovery_timer; @@ -82,7 +102,7 @@ class SocketTransport { } _close_requested = false; - status = C.STATUS_CONNECTING; + status = TransportStatus.connecting; onconnecting(socket, _recover_attempts); if (!_close_requested) { @@ -100,7 +120,7 @@ class SocketTransport { _close_requested = true; _recover_attempts = 0; - status = C.STATUS_DISCONNECTED; + status = TransportStatus.disconnected; // Clear recovery_timer. if (_recovery_timer != null) { @@ -117,11 +137,10 @@ class SocketTransport { socket.disconnect(); ondisconnect( - socket, - ErrorCause( - cause: 'disconnect', - status_code: 0, - reason_phrase: 'close by local')); + socket, + ErrorCause( + cause: 'disconnect', status_code: 0, reason_phrase: 'close by local'), + ); } bool send(dynamic data) { @@ -139,11 +158,11 @@ class SocketTransport { } bool isConnected() { - return status == C.STATUS_CONNECTED; + return status == TransportStatus.connected; } bool isConnecting() { - return status == C.STATUS_CONNECTING; + return status == TransportStatus.connecting; } /** @@ -180,28 +199,28 @@ class SocketTransport { void _getSocket() { // If we dont have at least 1 socket to try and use, thiw will loop endlessly - if (_socketsMap.length == 0) { + if (_sockets.isEmpty) { throw Exceptions.TypeError('invalid argument: too few sockets'); } - List> candidates = >[]; + List candidates = []; - for (Map socket in _socketsMap) { - if (socket['status'] == C.SOCKET_STATUS_ERROR) { + for (final SocketInfo socket in _sockets) { + if (socket.status == SocketStatus.error) { return; // continue the array iteration } else if (candidates.isEmpty) { candidates.add(socket); - } else if (socket['weight'] > candidates[0]['weight']) { - candidates = >[socket]; - } else if (socket['weight'] == candidates[0]['weight']) { + } else if (socket.weight > candidates[0].weight) { + candidates = [socket]; + } else if (socket.weight == candidates[0].weight) { candidates.add(socket); } } if (candidates.isEmpty) { // All sockets have failed. reset sockets status. - for (Map socket in _socketsMap) { - socket['status'] = C.SOCKET_STATUS_READY; + for (final SocketInfo socket in _sockets) { + socket.status = SocketStatus.ready; } // Get next available socket. _getSocket(); @@ -210,7 +229,7 @@ class SocketTransport { num idx = (Math.randomDouble() * candidates.length).floor(); - socket = candidates[idx as int]['socket']; + socket = candidates[idx as int].socket; } /** @@ -219,7 +238,7 @@ class SocketTransport { void _onConnect() { _recover_attempts = 0; - status = C.STATUS_CONNECTED; + status = TransportStatus.connected; // Clear recovery_timer. if (_recovery_timer != null) { @@ -231,7 +250,7 @@ class SocketTransport { void _onDisconnect( SIPUASocketInterface socket, bool error, int? closeCode, String? reason) { - status = C.STATUS_DISCONNECTED; + status = TransportStatus.disconnected; ondisconnect( socket, ErrorCause( @@ -242,9 +261,9 @@ class SocketTransport { } // Update socket status. else { - for (Map socket in _socketsMap) { - if (socket == socket['socket']) { - socket['status'] = C.SOCKET_STATUS_ERROR; + for (final SocketInfo s in _sockets) { + if (socket == s.socket) { + s.status = SocketStatus.error; } } } @@ -266,8 +285,9 @@ class SocketTransport { data = String.fromCharCodes(data); } catch (evt) { logger.d( - 'received binary message [${data.runtimeType}]failed to be converted into string,' - ' message discarded'); + 'received binary message [${data.runtimeType}]failed to be converted into string,' + ' message discarded', + ); return; } logger.d('received binary message:\n\n$data\n'); diff --git a/lib/src/subscriber.dart b/lib/src/subscriber.dart index 41ef7fd5..45ad1d13 100644 --- a/lib/src/subscriber.dart +++ b/lib/src/subscriber.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; +import './exceptions.dart' as exceptions; import 'constants.dart'; import 'dialog.dart'; import 'event_manager/event_manager.dart'; @@ -16,35 +17,31 @@ import 'timers.dart'; import 'ua.dart'; import 'utils.dart'; -/** - * Termination codes. - */ -class C { - // Termination codes. - static const int SUBSCRIBE_RESPONSE_TIMEOUT = 0; - static const int SUBSCRIBE_TRANSPORT_ERROR = 1; - static const int SUBSCRIBE_NON_OK_RESPONSE = 2; - static const int SUBSCRIBE_BAD_OK_RESPONSE = 3; - static const int SUBSCRIBE_FAILED_AUTHENTICATION = 4; - static const int UNSUBSCRIBE_TIMEOUT = 5; - static const int RECEIVE_FINAL_NOTIFY = 6; - static const int RECEIVE_BAD_NOTIFY = 7; - - // Subscriber states. - static const int STATE_PENDING = 0; - static const int STATE_ACTIVE = 1; - static const int STATE_TERMINATED = 2; - static const int STATE_INIT = 3; - static const int STATE_NOTIFY_WAIT = 4; +enum SubscriberTerminationCode { + subscribeResponseTimeout, + subscribeTransportError, + subscribeNonOkResponse, + subscribeBadOkResponse, + subscribeFailedAuthentication, + unsubscribeTimeout, + receiveFinalNotify, + receiveBadNotify } +enum SubscriberState { pending, active, terminated, init, notifyWait } + class Subscriber extends EventManager implements Owner { - Subscriber(this.ua, this._target, String eventName, String accept, - [int expires = 900, - String? contentType, - String? allowEvents, - Map requestParams = const {}, - List extraHeaders = const []]) { + Subscriber( + this.ua, + this._target, + String eventName, + String accept, [ + int expires = 900, + String? contentType, + String? allowEvents, + Map requestParams = const {}, + List extraHeaders = const [], + ]) { logger.d('new'); _expires = expires; @@ -106,7 +103,7 @@ class Subscriber extends EventManager implements Owner { late Map _params; - int _state = C.STATE_INIT; + SubscriberState _state = SubscriberState.init; Dialog? _dialog; @@ -137,13 +134,13 @@ class Subscriber extends EventManager implements Owner { String? get id => _id; @override - int get status => _state; + int get status => _state.index; @override - int get TerminatedCode => C.STATE_TERMINATED; + int get TerminatedCode => SubscriberState.terminated.index; void onRequestTimeout() { - _dialogTerminated(C.SUBSCRIBE_RESPONSE_TIMEOUT); + _dialogTerminated(SubscriberTerminationCode.subscribeResponseTimeout); } /** @@ -151,7 +148,7 @@ class Subscriber extends EventManager implements Owner { */ void onTransportError() { - _dialogTerminated(C.SUBSCRIBE_TRANSPORT_ERROR); + _dialogTerminated(SubscriberTerminationCode.subscribeTransportError); } /** @@ -171,7 +168,7 @@ class Subscriber extends EventManager implements Owner { if (eventHeader == null) { logger.w('missed Event header'); request.reply(400); - _dialogTerminated(C.RECEIVE_BAD_NOTIFY); + _dialogTerminated(SubscriberTerminationCode.receiveBadNotify); return; } @@ -182,7 +179,7 @@ class Subscriber extends EventManager implements Owner { if (eventName != _event_name || eventId != _event_id) { logger.w('Event header does not match SUBSCRIBE'); request.reply(489); - _dialogTerminated(C.RECEIVE_BAD_NOTIFY); + _dialogTerminated(SubscriberTerminationCode.receiveBadNotify); return; } @@ -193,17 +190,18 @@ class Subscriber extends EventManager implements Owner { if (subsState == null) { logger.w('missed Subscription-State header'); request.reply(400); - _dialogTerminated(C.RECEIVE_BAD_NOTIFY); + _dialogTerminated(SubscriberTerminationCode.receiveBadNotify); return; } request.reply(200); - int newState = _stateStringToNumber(subsState.state); - int prevState = _state; + SubscriberState newState = _parseStateString(subsState.state); + SubscriberState prevState = _state; - if (prevState != C.STATE_TERMINATED && newState != C.STATE_TERMINATED) { + if (prevState != SubscriberState.terminated && + newState != SubscriberState.terminated) { _state = newState; if (subsState.expires != null) { @@ -222,10 +220,12 @@ class Subscriber extends EventManager implements Owner { } } - if (prevState != C.STATE_PENDING && newState == C.STATE_PENDING) { + if (prevState != SubscriberState.pending && + newState == SubscriberState.pending) { logger.d('emit "pending"'); emit(EventPending()); - } else if (prevState != C.STATE_ACTIVE && newState == C.STATE_ACTIVE) { + } else if (prevState != SubscriberState.active && + newState == SubscriberState.active) { logger.d('emit "active"'); emit(EventActive()); } @@ -233,7 +233,7 @@ class Subscriber extends EventManager implements Owner { String? body = request.body; // Check if the notify is final. - bool isFinal = newState == C.STATE_TERMINATED; + bool isFinal = newState == SubscriberState.terminated; // Notify event fired only for notify with body. if (body != null) { @@ -255,7 +255,8 @@ class Subscriber extends EventManager implements Owner { retryAfter = int.tryParse(subsState.params['retry-after'], radix: 10); } - _dialogTerminated(C.RECEIVE_FINAL_NOTIFY, reason, retryAfter); + _dialogTerminated( + SubscriberTerminationCode.receiveFinalNotify, reason, retryAfter); } } @@ -266,7 +267,7 @@ class Subscriber extends EventManager implements Owner { void subscribe([String? target, String? body]) { logger.d('subscribe()'); - if (_state == C.STATE_INIT) { + if (_state == SubscriberState.init) { _sendInitialSubscribe(body, _headers); } else { _sendSubsequentSubscribe(body, _headers); @@ -292,7 +293,7 @@ class Subscriber extends EventManager implements Owner { return header.startsWith('Expires') ? 'Expires: 0' : header; }).toList(); - if (_state == C.STATE_INIT) { + if (_state == SubscriberState.init) { // fetch-subscribe - initial subscribe with Expires: 0. _sendInitialSubscribe(body, headers); } else { @@ -303,18 +304,18 @@ class Subscriber extends EventManager implements Owner { int final_notify_timeout = 30000; _unsubscribe_timeout_timer = setTimeout(() { - _dialogTerminated(C.UNSUBSCRIBE_TIMEOUT); + _dialogTerminated(SubscriberTerminationCode.unsubscribeTimeout); }, final_notify_timeout); } - void _dialogTerminated(int terminationCode, + void _dialogTerminated(SubscriberTerminationCode code, [String? reason, int? retryAfter]) { // To prevent duplicate emit terminated event. - if (_state == C.STATE_TERMINATED) { + if (_state == SubscriberState.terminated) { return; } - _state = C.STATE_TERMINATED; + _state = SubscriberState.terminated; // Clear timers. clearTimeout(_expires_timer); @@ -325,11 +326,9 @@ class Subscriber extends EventManager implements Owner { _dialog = null; } - logger.d('emit "terminated" code=$terminationCode'); + logger.d('emit "terminated" code=$code'); emit(EventTerminated( - TerminationCode: terminationCode, - reason: reason, - retryAfter: retryAfter)); + TerminationCode: code.index, reason: reason, retryAfter: retryAfter)); } void _handlePresence(EventNotify event) { @@ -350,7 +349,7 @@ class Subscriber extends EventManager implements Owner { _dialog = dialog; } catch (e) { logger.w(e.toString()); - _dialogTerminated(C.SUBSCRIBE_BAD_OK_RESPONSE); + _dialogTerminated(SubscriberTerminationCode.subscribeBadOkResponse); return; } @@ -391,9 +390,10 @@ class Subscriber extends EventManager implements Owner { _scheduleSubscribe(expires); } } else if (response.status_code == 401 || response.status_code == 407) { - _dialogTerminated(C.SUBSCRIBE_FAILED_AUTHENTICATION); + _dialogTerminated( + SubscriberTerminationCode.subscribeFailedAuthentication); } else if (response.status_code >= 300) { - _dialogTerminated(C.SUBSCRIBE_NON_OK_RESPONSE); + _dialogTerminated(SubscriberTerminationCode.subscribeNonOkResponse); } } @@ -439,7 +439,7 @@ class Subscriber extends EventManager implements Owner { headers.add('Content-Type: $_contentType'); } - _state = C.STATE_NOTIFY_WAIT; + _state = SubscriberState.notifyWait; OutgoingRequest request = OutgoingRequest(SipMethod.SUBSCRIBE, ua.normalizeTarget(_target)!, ua, _params, headers, body); @@ -464,7 +464,7 @@ class Subscriber extends EventManager implements Owner { } void _sendSubsequentSubscribe(String? body, List headers) { - if (_state == C.STATE_TERMINATED) { + if (_state == SubscriberState.terminated) { return; } @@ -512,29 +512,22 @@ class Subscriber extends EventManager implements Owner { }); } - int _stateStringToNumber(String? strState) { + SubscriberState _parseStateString(String? strState) { switch (strState) { case 'pending': - return C.STATE_PENDING; + return SubscriberState.pending; case 'active': - return C.STATE_ACTIVE; + return SubscriberState.active; case 'terminated': - return C.STATE_TERMINATED; + return SubscriberState.terminated; case 'init': - return C.STATE_INIT; + return SubscriberState.init; case 'notify_wait': - return C.STATE_NOTIFY_WAIT; + return SubscriberState.notifyWait; default: - throw TypeError('wrong state value'); + throw exceptions.TypeError('wrong state value'); } } - - /** - * Expose C object. - */ - static C getC() { - return C(); - } } class SubscriptionId { diff --git a/lib/src/transport_constants.dart b/lib/src/transport_constants.dart deleted file mode 100644 index a2d84e5c..00000000 --- a/lib/src/transport_constants.dart +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Constants - */ -class C { - // Transport status. - static const int STATUS_CONNECTED = 0; - static const int STATUS_CONNECTING = 1; - static const int STATUS_DISCONNECTED = 2; - - // Socket status. - static const int SOCKET_STATUS_READY = 0; - static const int SOCKET_STATUS_ERROR = 1; - - // Recovery options. - static const Map recovery_options = { - 'min_interval': 2, // minimum interval in seconds between recover attempts - 'max_interval': 30 // maximum interval in seconds between recover attempts - }; -} diff --git a/lib/src/ua.dart b/lib/src/ua.dart index 6d0ed965..4b6fc81b 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'package:sip_ua/src/transport_type.dart'; -import 'package:sip_ua/src/transports/socket_interface.dart'; import 'config.dart' as config; import 'config.dart'; import 'constants.dart' as DartSIP_C; @@ -28,21 +26,13 @@ import 'transactions/non_invite_client.dart'; import 'transactions/non_invite_server.dart'; import 'transactions/transaction_base.dart'; import 'transactions/transactions.dart'; -import 'transports/web_socket.dart'; +import 'transports/socket_interface.dart'; import 'uri.dart'; import 'utils.dart' as Utils; -class C { - // UA status codes. - static const int STATUS_INIT = 0; - static const int STATUS_READY = 1; - static const int STATUS_USER_CLOSED = 2; - static const int STATUS_NOT_READY = 3; +enum UAStatus { init, ready, userClosed, notReady } - // UA error codes. - static const int CONFIGURATION_ERROR = 1; - static const int NETWORK_ERROR = 2; -} +enum UAError { configuration, network } // TODO(Perondas): Figure out what this is final bool hasRTCPeerConnection = true; @@ -93,8 +83,8 @@ class UA extends EventManager { try { _loadConfig(configuration); } catch (e) { - _status = C.STATUS_NOT_READY; - _error = C.CONFIGURATION_ERROR; + _status = UAStatus.notReady; + _error = UAError.configuration; rethrow; } @@ -118,8 +108,8 @@ class UA extends EventManager { final Map _sessions = {}; SocketTransport? _socketTransport; Contact? _contact; - int _status = C.STATUS_INIT; - int? _error; + UAStatus _status = UAStatus.init; + UAError? _error; late TransactionBag _transactions; // Custom UA empty object for high level use. @@ -128,7 +118,7 @@ class UA extends EventManager { Timer? _closeTimer; late Registrator _registrator; - int get status => _status; + UAStatus get status => _status; Contact? get contact => _contact; @@ -154,9 +144,9 @@ class UA extends EventManager { _transactions = TransactionBag(); - if (_status == C.STATUS_INIT) { + if (_status == UAStatus.init) { _socketTransport!.connect(); - } else if (_status == C.STATUS_USER_CLOSED) { + } else if (_status == UAStatus.userClosed) { logger.d('restarting UA'); // Disconnect. @@ -167,9 +157,9 @@ class UA extends EventManager { } // Reconnect. - _status = C.STATUS_INIT; + _status = UAStatus.init; _socketTransport!.connect(); - } else if (_status == C.STATUS_READY) { + } else if (_status == UAStatus.ready) { logger.d('UA is in READY status, not restarted'); } else { logger.d( @@ -313,7 +303,7 @@ class UA extends EventManager { // Remove dynamic settings. _dynConfiguration = null; - if (_status == C.STATUS_USER_CLOSED) { + if (_status == UAStatus.userClosed) { logger.d('UA already closed'); return; @@ -362,7 +352,7 @@ class UA extends EventManager { } catch (error) {} } - _status = C.STATUS_USER_CLOSED; + _status = UAStatus.userClosed; int num_transactions = _transactions.countTransactions(); if (num_transactions == 0 && num_sessions == 0) { @@ -927,10 +917,10 @@ class UA extends EventManager { // Transport connected event. void onTransportConnect(SocketTransport transport) { logger.d('Transport connected'); - if (_status == C.STATUS_USER_CLOSED) { + if (_status == UAStatus.userClosed) { return; } - _status = C.STATUS_READY; + _status = UAStatus.ready; _error = null; emit(EventSocketConnected(socket: transport.socket)); @@ -952,9 +942,9 @@ class UA extends EventManager { // Call registrator _onTransportClosed_. _registrator.onTransportClosed(); - if (_status != C.STATUS_USER_CLOSED) { - _status = C.STATUS_NOT_READY; - _error = C.NETWORK_ERROR; + if (_status != UAStatus.userClosed) { + _status = UAStatus.notReady; + _error = UAError.network; } } @@ -966,7 +956,7 @@ class UA extends EventManager { return; } - if (_status == C.STATUS_USER_CLOSED && message is IncomingRequest) { + if (_status == UAStatus.userClosed && message is IncomingRequest) { return; } From 04a7652d226fd93bd7b4c453afda8517a1f694b5 Mon Sep 17 00:00:00 2001 From: Victor Uvarov <35702719+VictorUvarov@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:32:44 -0800 Subject: [PATCH 13/35] Update registrator.dart (#511) Fixes #506 --- lib/src/registrator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/registrator.dart b/lib/src/registrator.dart index 4de8deb9..2244cf86 100644 --- a/lib/src/registrator.dart +++ b/lib/src/registrator.dart @@ -206,7 +206,7 @@ class Registrator { expires ??= _expires; - expires = num.tryParse(expires) ?? 0; + expires = num.tryParse(expires.toString()) ?? 0; if (expires < MIN_REGISTER_EXPIRES) { expires = MIN_REGISTER_EXPIRES; From 3d34f90e1d440c052df186727ad5d747cc9e578f Mon Sep 17 00:00:00 2001 From: Victor Uvarov Date: Fri, 27 Dec 2024 12:41:11 -0800 Subject: [PATCH 14/35] Fix/prefer is empty lint (#510) * make sure the session terminates * fix prefer_is_empty lint rule warnings --------- Co-authored-by: Victor Uvarov --- analysis_options.yaml | 1 - lib/src/config.dart | 2 +- lib/src/name_addr_header.dart | 2 +- lib/src/rtc_session/dtmf.dart | 2 +- lib/src/socket_transport.dart | 2 +- lib/src/transports/tcp_socket.dart | 2 +- lib/src/transports/web_socket.dart | 2 +- lib/src/uri.dart | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 9b708ede..93b8d111 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -45,7 +45,6 @@ analyzer: library_prefixes: ignore unused_field: ignore avoid_init_to_null: ignore - prefer_is_empty: ignore unused_element: ignore curly_braces_in_flow_control_structures: ignore unnecessary_null_in_if_null_operators: ignore diff --git a/lib/src/config.dart b/lib/src/config.dart index 102e7ef5..5caa5dd3 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -85,7 +85,7 @@ class Checks { * List of Objects and Socket: [{socket: socket1}, socket2] */ List copy = []; - if (sockets is List && sockets!.length > 0) { + if (sockets is List && sockets!.isNotEmpty) { for (SIPUASocketInterface socket in sockets) { copy.add(socket); } diff --git a/lib/src/name_addr_header.dart b/lib/src/name_addr_header.dart index a2924071..8e934490 100644 --- a/lib/src/name_addr_header.dart +++ b/lib/src/name_addr_header.dart @@ -89,7 +89,7 @@ class NameAddrHeader { @override String toString() { - String body = (_display_name != null && _display_name!.length > 0) + String body = (_display_name != null && _display_name!.isNotEmpty) ? '"${_quote(_display_name!)}" ' : ''; diff --git a/lib/src/rtc_session/dtmf.dart b/lib/src/rtc_session/dtmf.dart index 15f8de1a..60179beb 100644 --- a/lib/src/rtc_session/dtmf.dart +++ b/lib/src/rtc_session/dtmf.dart @@ -120,7 +120,7 @@ class DTMF extends EventManager { if (request.body != null) { List body = request.body!.split('\n'); - if (body.length >= 1) { + if (body.isNotEmpty) { if (body[0].contains(RegExp(reg_tone))) { _tone = body[0].replaceAll(reg_tone, '\$2'); } diff --git a/lib/src/socket_transport.dart b/lib/src/socket_transport.dart index 71a2f0b4..ea82046d 100644 --- a/lib/src/socket_transport.dart +++ b/lib/src/socket_transport.dart @@ -46,7 +46,7 @@ class SocketTransport { _recovery_options = recovery_options; // We must recieve at least 1 socket - if (sockets!.length == 0) { + if (sockets!.isEmpty) { throw Exceptions.TypeError( 'invalid argument: Must recieve atleast 1 web socket'); } diff --git a/lib/src/transports/tcp_socket.dart b/lib/src/transports/tcp_socket.dart index ce3832c7..59f67217 100644 --- a/lib/src/transports/tcp_socket.dart +++ b/lib/src/transports/tcp_socket.dart @@ -166,7 +166,7 @@ class SIPUATcpSocket extends SIPUASocketInterface { void _onMessage(dynamic data) { logger.d('Received TcpSocket data'); if (data != null) { - if (data.toString().trim().length > 0) { + if (data.toString().trim().isNotEmpty) { ondata!(data); } else { logger.d('Received and ignored empty packet'); diff --git a/lib/src/transports/web_socket.dart b/lib/src/transports/web_socket.dart index 57d610c8..9a7302bf 100644 --- a/lib/src/transports/web_socket.dart +++ b/lib/src/transports/web_socket.dart @@ -175,7 +175,7 @@ class SIPUAWebSocket extends SIPUASocketInterface { void _onMessage(dynamic data) { logger.d('Received WebSocket message'); if (data != null) { - if (data.toString().trim().length > 0) { + if (data.toString().trim().isNotEmpty) { ondata!(data); } else { logger.d('Received and ignored empty packet'); diff --git a/lib/src/uri.dart b/lib/src/uri.dart index 7996516f..66d30b93 100644 --- a/lib/src/uri.dart +++ b/lib/src/uri.dart @@ -181,7 +181,7 @@ class URI { }); }); - if (headers.length > 0) { + if (headers.isNotEmpty) { uri += '?${headers.join('&')}'; } From e9fdea469ee64ccd1d713cc8d160e455ba746d19 Mon Sep 17 00:00:00 2001 From: Victor Uvarov Date: Fri, 27 Dec 2024 12:44:27 -0800 Subject: [PATCH 15/35] replace string enum usage with dart enums (#479) * replace usage of direction as a string with an CallDirection enum * convert more string enums to actual enums --------- Co-authored-by: Victor Uvarov --- example/lib/src/callscreen.dart | 12 +- example/pubspec.yaml | 2 +- lib/sip_ua.dart | 1 + lib/src/config.dart | 5 +- lib/src/enums.dart | 39 +++ lib/src/event_manager/call_events.dart | 25 +- lib/src/event_manager/internal_events.dart | 13 +- lib/src/event_manager/message_events.dart | 3 +- lib/src/event_manager/notifier_events.dart | 2 +- lib/src/event_manager/options_events.dart | 3 +- lib/src/event_manager/transport_events.dart | 2 +- lib/src/message.dart | 40 +-- lib/src/options.dart | 38 +-- lib/src/registrator.dart | 2 +- lib/src/rtc_session.dart | 255 +++++++++++--------- lib/src/rtc_session/dtmf.dart | 18 +- lib/src/rtc_session/info.dart | 19 +- lib/src/rtc_session/refer_subscriber.dart | 2 +- lib/src/sip_message.dart | 2 +- lib/src/sip_ua_helper.dart | 31 ++- lib/src/transactions/ack_client.dart | 2 +- lib/src/transactions/invite_client.dart | 2 +- lib/src/transactions/transactions.dart | 2 +- lib/src/transports/tcp_socket.dart | 6 +- lib/src/transports/tcp_socket_impl.dart | 2 +- lib/src/transports/web_socket.dart | 4 +- lib/src/ua.dart | 7 +- 27 files changed, 314 insertions(+), 225 deletions(-) create mode 100644 lib/src/enums.dart diff --git a/example/lib/src/callscreen.dart b/example/lib/src/callscreen.dart index a6b6ebe9..1d6bba56 100644 --- a/example/lib/src/callscreen.dart +++ b/example/lib/src/callscreen.dart @@ -34,7 +34,7 @@ class _MyCallScreenWidget extends State bool _speakerOn = false; bool _hold = false; bool _mirror = true; - String? _holdOriginator; + Originator? _holdOriginator; bool _callConfirmed = false; CallStateEnum _state = CallStateEnum.NONE; @@ -47,7 +47,7 @@ class _MyCallScreenWidget extends State String? get remoteIdentity => call!.remote_identity; - String get direction => call!.direction; + Direction? get direction => call!.direction; Call? get call => widget._call; @@ -177,7 +177,7 @@ class _MyCallScreenWidget extends State void _handleStreams(CallState event) async { MediaStream? stream = event.stream; - if (event.originator == 'local') { + if (event.originator == Originator.local) { if (_localRenderer != null) { _localRenderer!.srcObject = stream; } @@ -189,7 +189,7 @@ class _MyCallScreenWidget extends State } _localStream = stream; } - if (event.originator == 'remote') { + if (event.originator == Originator.remote) { if (_remoteRenderer != null) { _remoteRenderer!.srcObject = stream; } @@ -423,7 +423,7 @@ class _MyCallScreenWidget extends State switch (_state) { case CallStateEnum.NONE: case CallStateEnum.CONNECTING: - if (direction == 'INCOMING') { + if (direction == Direction.incoming) { basicActions.add(ActionButton( title: "Accept", fillColor: Colors.green, @@ -608,7 +608,7 @@ class _MyCallScreenWidget extends State child: Text( (voiceOnly ? 'VOICE CALL' : 'VIDEO CALL') + (_hold - ? ' PAUSED BY ${_holdOriginator!.toUpperCase()}' + ? ' PAUSED BY ${_holdOriginator!.name}' : ''), style: TextStyle(fontSize: 24, color: textColor), ), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 6c4c2372..12a2a9a4 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -15,7 +15,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.15.0 <3.0.0" flutter: ">=1.10.0" dependencies: diff --git a/lib/sip_ua.dart b/lib/sip_ua.dart index 397defee..8fe2256f 100644 --- a/lib/sip_ua.dart +++ b/lib/sip_ua.dart @@ -1,4 +1,5 @@ export 'src/enum_helper.dart'; +export 'src/enums.dart'; export 'src/sip_message.dart'; export 'src/sip_ua_helper.dart'; export 'src/transport_type.dart'; diff --git a/lib/src/config.dart b/lib/src/config.dart index 5caa5dd3..8273df83 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -1,11 +1,10 @@ -import 'package:sip_ua/sip_ua.dart'; -import 'package:sip_ua/src/transports/socket_interface.dart'; -import 'package:sip_ua/src/transports/tcp_socket.dart'; +import '../sip_ua.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'exceptions.dart' as Exceptions; import 'grammar.dart'; import 'logger.dart'; +import 'transports/socket_interface.dart'; import 'transports/web_socket.dart'; import 'utils.dart' as Utils; diff --git a/lib/src/enums.dart b/lib/src/enums.dart new file mode 100644 index 00000000..814b6f8a --- /dev/null +++ b/lib/src/enums.dart @@ -0,0 +1,39 @@ +/// Defines the direction of a communication, +/// indicating whether it is outgoing or incoming. +/// +/// Used to specify the flow of calls or messages. +enum Direction { + /// Represents an outgoing call or message. + outgoing, + + /// Represents an incoming call or message. + incoming +} + +/// Identifies the originator of a communication, +/// specifying whether the initiator is local or remote. +/// +/// This is useful for determining who started the call or message. +enum Originator { + /// Represents the user of this device initiated the communication. + local, + + /// Represents the communication was initiated by someone else. + remote, + + /// Represents that the communication was initiated by the system (e.g., automated processes). + system, +} + +/// Represents the type of SDP (Session Description Protocol) message +/// used in a communication session. +/// +/// SDP messages are exchanged between peers during the setup of a media connection. +enum SdpType { + /// Represents an SDP offer, which is the initial proposal sent to set up a media session. + offer, + + /// Represents an SDP answer, which is the response to an SDP offer, + /// confirming or adjusting the session parameters. + answer +} diff --git a/lib/src/event_manager/call_events.dart b/lib/src/event_manager/call_events.dart index 0270a8cc..feb13750 100644 --- a/lib/src/event_manager/call_events.dart +++ b/lib/src/event_manager/call_events.dart @@ -1,5 +1,6 @@ import 'package:flutter_webrtc/flutter_webrtc.dart'; +import '../enums.dart'; import '../rtc_session.dart'; import '../sip_message.dart'; import 'events.dart'; @@ -11,9 +12,10 @@ class CallEvent extends EventType { } class EventNewRTCSession extends CallEvent { - EventNewRTCSession({RTCSession? session, String? originator, dynamic request}) + EventNewRTCSession( + {RTCSession? session, Originator? originator, dynamic request}) : super(session); - String? originator; + Originator? originator; dynamic request; } @@ -25,7 +27,7 @@ class EventCallEnded extends CallEvent { EventCallEnded( {RTCSession? session, this.originator, this.cause, this.request}) : super(session); - String? originator; + Originator? originator; ErrorCause? cause; IncomingRequest? request; } @@ -34,7 +36,7 @@ class EventCallProgress extends CallEvent { EventCallProgress( {RTCSession? session, this.originator, this.response, this.cause}) : super(session); - String? originator; + Originator? originator; dynamic response; ErrorCause? cause; } @@ -42,18 +44,19 @@ class EventCallProgress extends CallEvent { class EventCallConfirmed extends CallEvent { EventCallConfirmed({RTCSession? session, this.originator, this.ack}) : super(session); - String? originator; + Originator? originator; dynamic ack; } class EventCallHold extends CallEvent { EventCallHold({RTCSession? session, this.originator}) : super(session); - String? originator; + Originator? originator; } class EventCallUnhold extends CallEvent { - EventCallUnhold({RTCSession? session, String? originator}) : super(session); - String? originator; + EventCallUnhold({RTCSession? session, Originator? originator}) + : super(session); + Originator? originator; } class EventCallMuted extends CallEvent { @@ -73,7 +76,7 @@ class EventCallUnmuted extends CallEvent { class EventCallAccepted extends CallEvent { EventCallAccepted({RTCSession? session, this.originator, this.response}) : super(session); - String? originator; + Originator? originator; dynamic response; } @@ -89,7 +92,7 @@ class EventCallFailed extends CallEvent { this.status_line}) : super(session); dynamic response; - String? originator; + Originator? originator; ErrorCause? cause; dynamic request; String? status_line; @@ -98,7 +101,7 @@ class EventCallFailed extends CallEvent { class EventStream extends CallEvent { EventStream({RTCSession? session, this.originator, this.stream}) : super(session); - String? originator; + Originator? originator; MediaStream? stream; } diff --git a/lib/src/event_manager/internal_events.dart b/lib/src/event_manager/internal_events.dart index 0d2f3ed8..9b5adad2 100644 --- a/lib/src/event_manager/internal_events.dart +++ b/lib/src/event_manager/internal_events.dart @@ -1,5 +1,6 @@ import 'package:flutter_webrtc/flutter_webrtc.dart'; +import '../enums.dart'; import '../rtc_session.dart' show RTCSession; import '../rtc_session/dtmf.dart'; import '../rtc_session/info.dart'; @@ -33,8 +34,8 @@ class EventOnAuthenticated extends EventType { class EventSdp extends EventType { EventSdp({this.originator, this.type, this.sdp}); - String? originator; - String? type; + Originator? originator; + SdpType? type; String? sdp; } @@ -55,7 +56,7 @@ class EventSetLocalDescriptionFailed extends EventType { class EventFailedUnderScore extends EventType { EventFailedUnderScore({this.originator, this.cause}); - String? originator; + Originator? originator; ErrorCause? cause; } @@ -66,14 +67,14 @@ class EventGetUserMediaFailed extends EventType { class EventNewDTMF extends EventType { EventNewDTMF({this.originator, this.request, this.dtmf}); - String? originator; + Originator? originator; dynamic request; DTMF? dtmf; } class EventNewInfo extends EventType { EventNewInfo({this.originator, this.request, this.info}); - String? originator; + Originator? originator; dynamic request; Info? info; } @@ -127,7 +128,7 @@ class EventOnFialed extends EventType {} class EventSucceeded extends EventType { EventSucceeded({this.response, this.originator}); - String? originator; + Originator? originator; IncomingMessage? response; } diff --git a/lib/src/event_manager/message_events.dart b/lib/src/event_manager/message_events.dart index ccc15b68..445f61f9 100644 --- a/lib/src/event_manager/message_events.dart +++ b/lib/src/event_manager/message_events.dart @@ -1,9 +1,10 @@ +import '../enums.dart'; import '../message.dart'; import 'events.dart'; class EventNewMessage extends EventType { EventNewMessage({this.message, this.originator, this.request}); dynamic request; - String? originator; + Originator? originator; Message? message; } diff --git a/lib/src/event_manager/notifier_events.dart b/lib/src/event_manager/notifier_events.dart index 945e735b..d1ef620d 100644 --- a/lib/src/event_manager/notifier_events.dart +++ b/lib/src/event_manager/notifier_events.dart @@ -1,4 +1,4 @@ -import 'package:sip_ua/src/sip_message.dart'; +import '../sip_message.dart'; import 'events.dart'; class EventTerminated extends EventType { diff --git a/lib/src/event_manager/options_events.dart b/lib/src/event_manager/options_events.dart index 668c2b5d..2bfe65a7 100644 --- a/lib/src/event_manager/options_events.dart +++ b/lib/src/event_manager/options_events.dart @@ -1,9 +1,10 @@ +import '../enums.dart'; import '../options.dart'; import 'events.dart'; class EventNewOptions extends EventType { EventNewOptions({this.message, this.originator, this.request}); dynamic request; - String? originator; + Originator? originator; Options? message; } diff --git a/lib/src/event_manager/transport_events.dart b/lib/src/event_manager/transport_events.dart index 881a9d2d..83bc9f1b 100644 --- a/lib/src/event_manager/transport_events.dart +++ b/lib/src/event_manager/transport_events.dart @@ -1,4 +1,4 @@ -import 'package:sip_ua/src/transports/socket_interface.dart'; +import '../transports/socket_interface.dart'; import '../transports/web_socket.dart'; import 'events.dart'; diff --git a/lib/src/message.dart b/lib/src/message.dart index 2aeffe49..c8f6092d 100644 --- a/lib/src/message.dart +++ b/lib/src/message.dart @@ -1,10 +1,11 @@ -import 'package:sip_ua/src/name_addr_header.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; +import 'enums.dart'; import 'event_manager/event_manager.dart'; import 'event_manager/internal_events.dart'; import 'exceptions.dart' as Exceptions; import 'logger.dart'; +import 'name_addr_header.dart'; import 'request_sender.dart'; import 'sip_message.dart'; import 'ua.dart'; @@ -17,14 +18,14 @@ class Message extends EventManager with Applicant { final UA _ua; dynamic _request; bool _closed = false; - String? _direction; + Direction? _direction; NameAddrHeader? _local_identity; NameAddrHeader? _remote_identity; // Whether an incoming message has been replied. bool _is_replied = false; // Custom message empty object for high level use. final Map _data = {}; - String? get direction => _direction; + Direction? get direction => _direction; NameAddrHeader? get local_identity => _local_identity; @@ -76,7 +77,7 @@ class Message extends EventManager with Applicant { RequestSender request_sender = RequestSender(_ua, _request, handlers); - _newMessage('local', _request); + _newMessage(Originator.local, _request); request_sender.send(); } @@ -84,7 +85,7 @@ class Message extends EventManager with Applicant { void init_incoming(IncomingRequest request) { _request = request; - _newMessage('remote', request); + _newMessage(Originator.remote, request); // Reply with a 200 OK if the user didn't reply. if (!_is_replied) { @@ -103,7 +104,7 @@ class Message extends EventManager with Applicant { List extraHeaders = Utils.cloneArray(options['extraHeaders']); String? body = options['body']; - if (_direction != 'incoming') { + if (_direction != Direction.incoming) { throw Exceptions.NotSupportedError( '"accept" not supported for outgoing Message'); } @@ -126,7 +127,7 @@ class Message extends EventManager with Applicant { List extraHeaders = Utils.cloneArray(options['extraHeaders']); String? body = options['body']; - if (_direction != 'incoming') { + if (_direction != Direction.incoming) { throw Exceptions.NotSupportedError( '"reject" not supported for outgoing Message'); } @@ -151,10 +152,11 @@ class Message extends EventManager with Applicant { // Ignore provisional responses. } else if (RegExp(r'^2[0-9]{2}$') .hasMatch(response.status_code.toString())) { - _succeeded('remote', response); + _succeeded(Originator.remote, response); } else { String cause = Utils.sipErrorCause(response.status_code); - _failed('remote', response.status_code, cause, response.reason_phrase); + _failed(Originator.remote, response.status_code, cause, + response.reason_phrase); } } @@ -162,15 +164,15 @@ class Message extends EventManager with Applicant { if (_closed) { return; } - _failed( - 'system', 408, DartSIP_C.CausesType.REQUEST_TIMEOUT, 'Request Timeout'); + _failed(Originator.system, 408, DartSIP_C.CausesType.REQUEST_TIMEOUT, + 'Request Timeout'); } void _onTransportError() { if (_closed) { return; } - _failed('system', 500, DartSIP_C.CausesType.CONNECTION_ERROR, + _failed(Originator.system, 500, DartSIP_C.CausesType.CONNECTION_ERROR, 'Transport Error'); } @@ -184,13 +186,13 @@ class Message extends EventManager with Applicant { * Internal Callbacks */ - void _newMessage(String originator, dynamic request) { - if (originator == 'remote') { - _direction = 'incoming'; + void _newMessage(Originator originator, dynamic request) { + if (originator == Originator.remote) { + _direction = Direction.incoming; _local_identity = request.to; _remote_identity = request.from; - } else if (originator == 'local') { - _direction = 'outgoing'; + } else if (originator == Originator.local) { + _direction = Direction.outgoing; _local_identity = request.from; _remote_identity = request.to; } @@ -198,7 +200,7 @@ class Message extends EventManager with Applicant { _ua.newMessage(this, originator, request); } - void _failed(String originator, int? status_code, String cause, + void _failed(Originator originator, int? status_code, String cause, String? reason_phrase) { logger.d('MESSAGE failed'); close(); @@ -211,7 +213,7 @@ class Message extends EventManager with Applicant { reason_phrase: reason_phrase))); } - void _succeeded(String originator, IncomingResponse? response) { + void _succeeded(Originator originator, IncomingResponse? response) { logger.d('MESSAGE succeeded'); close(); diff --git a/lib/src/options.dart b/lib/src/options.dart index cff29729..e8e1ece6 100644 --- a/lib/src/options.dart +++ b/lib/src/options.dart @@ -1,3 +1,4 @@ +import 'package:sip_ua/src/enums.dart'; import 'package:sip_ua/src/name_addr_header.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; @@ -17,14 +18,14 @@ class Options extends EventManager with Applicant { final UA _ua; dynamic _request; bool _closed = false; - String? _direction; + Direction? _direction; NameAddrHeader? _local_identity; NameAddrHeader? _remote_identity; // Whether an incoming Options has been replied. bool _is_replied = false; // Custom Options empty object for high level use. final Map _data = {}; - String? get direction => _direction; + Direction? get direction => _direction; NameAddrHeader? get local_identity => _local_identity; @@ -75,7 +76,7 @@ class Options extends EventManager with Applicant { RequestSender request_sender = RequestSender(_ua, _request, handlers); - _newOptions('local', _request); + _newOptions(Originator.local, _request); request_sender.send(); } @@ -83,7 +84,7 @@ class Options extends EventManager with Applicant { void init_incoming(IncomingRequest request) { _request = request; - _newOptions('remote', request); + _newOptions(Originator.remote, request); // Reply with a 200 OK if the user didn't reply. if (!_is_replied) { @@ -102,7 +103,7 @@ class Options extends EventManager with Applicant { List extraHeaders = Utils.cloneArray(options['extraHeaders']); String? body = options['body']; - if (_direction != 'incoming') { + if (_direction != Direction.incoming) { throw Exceptions.NotSupportedError( '"accept" not supported for outgoing Options'); } @@ -125,7 +126,7 @@ class Options extends EventManager with Applicant { List extraHeaders = Utils.cloneArray(options['extraHeaders']); String? body = options['body']; - if (_direction != 'incoming') { + if (_direction != Direction.incoming) { throw Exceptions.NotSupportedError( '"reject" not supported for outgoing Options'); } @@ -149,10 +150,11 @@ class Options extends EventManager with Applicant { if (RegExp(r'^1[0-9]{2}$').hasMatch(response!.status_code)) { // Ignore provisional responses. } else if (RegExp(r'^2[0-9]{2}$').hasMatch(response.status_code)) { - _succeeded('remote', response); + _succeeded(Originator.remote, response); } else { String cause = Utils.sipErrorCause(response.status_code); - _failed('remote', response.status_code, cause, response.reason_phrase); + _failed(Originator.remote, response.status_code, cause, + response.reason_phrase); } } @@ -160,15 +162,15 @@ class Options extends EventManager with Applicant { if (_closed != null) { return; } - _failed( - 'system', 408, DartSIP_C.CausesType.REQUEST_TIMEOUT, 'Request Timeout'); + _failed(Originator.system, 408, DartSIP_C.CausesType.REQUEST_TIMEOUT, + 'Request Timeout'); } void _onTransportError() { if (_closed != null) { return; } - _failed('system', 500, DartSIP_C.CausesType.CONNECTION_ERROR, + _failed(Originator.system, 500, DartSIP_C.CausesType.CONNECTION_ERROR, 'Transport Error'); } @@ -182,13 +184,13 @@ class Options extends EventManager with Applicant { * Internal Callbacks */ - void _newOptions(String originator, dynamic request) { - if (originator == 'remote') { - _direction = 'incoming'; + void _newOptions(Originator originator, dynamic request) { + if (originator == Originator.remote) { + _direction = Direction.incoming; _local_identity = request.to; _remote_identity = request.from; - } else if (originator == 'local') { - _direction = 'outgoing'; + } else if (originator == Originator.local) { + _direction = Direction.outgoing; _local_identity = request.from; _remote_identity = request.to; } @@ -196,7 +198,7 @@ class Options extends EventManager with Applicant { _ua.newOptions(this, originator, request); } - void _failed(String originator, int? status_code, String cause, + void _failed(Originator originator, int? status_code, String cause, String? reason_phrase) { logger.d('OPTIONS failed'); close(); @@ -209,7 +211,7 @@ class Options extends EventManager with Applicant { reason_phrase: reason_phrase))); } - void _succeeded(String originator, IncomingResponse? response) { + void _succeeded(Originator originator, IncomingResponse? response) { logger.d('OPTIONS succeeded'); close(); diff --git a/lib/src/registrator.dart b/lib/src/registrator.dart index 2244cf86..795b1bab 100644 --- a/lib/src/registrator.dart +++ b/lib/src/registrator.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:sip_ua/src/socket_transport.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'event_manager/event_manager.dart'; @@ -10,6 +9,7 @@ import 'logger.dart'; import 'name_addr_header.dart'; import 'request_sender.dart'; import 'sip_message.dart'; +import 'socket_transport.dart'; import 'timers.dart'; import 'ua.dart'; import 'uri.dart'; diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 27bf15f0..2ccf6f98 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -120,7 +120,7 @@ class RTCSession extends EventManager implements Owner { final SIPTimers _timers = SIPTimers(); // Session info. - String? _direction; + Direction? _direction; NameAddrHeader? _local_identity; NameAddrHeader? _remote_identity; DateTime? _start_time; @@ -170,7 +170,7 @@ class RTCSession extends EventManager implements Owner { String? get contact => _contact; - String? get direction => _direction; + Direction? get direction => _direction; NameAddrHeader? get local_identity => _local_identity; @@ -337,7 +337,7 @@ class RTCSession extends EventManager implements Owner { await _createRTCConnection(pcConfig, rtcConstraints); // Set internal properties. - _direction = 'outgoing'; + _direction = Direction.outgoing; _local_identity = _request.from; _remote_identity = _request.to; @@ -346,7 +346,7 @@ class RTCSession extends EventManager implements Owner { initCallback(this); } - _newRTCSession('local', _request); + _newRTCSession(Originator.local, _request); await _sendInitialRequest( pcConfig, mediaConstraints, rtcOfferConstraints, mediaStream); } @@ -406,8 +406,8 @@ class RTCSession extends EventManager implements Owner { // Set userNoAnswerTimer. _timers.userNoAnswerTimer = setTimeout(() { request.reply(408); - _failed('local', null, null, null, 408, DartSIP_C.CausesType.NO_ANSWER, - 'No Answer'); + _failed(Originator.local, null, null, null, 408, + DartSIP_C.CausesType.NO_ANSWER, 'No Answer'); }, _ua.configuration.no_answer_timeout); /* Set expiresTimer @@ -417,14 +417,14 @@ class RTCSession extends EventManager implements Owner { _timers.expiresTimer = setTimeout(() { if (_state == RtcSessionState.waitingForAnswer) { request.reply(487); - _failed('system', null, null, null, 487, DartSIP_C.CausesType.EXPIRES, - 'Timeout'); + _failed(Originator.system, null, null, null, 487, + DartSIP_C.CausesType.EXPIRES, 'Timeout'); } }, expires); } // Set internal properties. - _direction = 'incoming'; + _direction = Direction.incoming; _local_identity = request.to; _remote_identity = request.from; @@ -434,7 +434,7 @@ class RTCSession extends EventManager implements Owner { } // Fire 'newRTCSession' event. - _newRTCSession('remote', request); + _newRTCSession(Originator.remote, request); // The user may have rejected the call in the 'newRTCSession' event. if (_state == RtcSessionState.terminated) { @@ -446,7 +446,7 @@ class RTCSession extends EventManager implements Owner { // Fire 'progress' event. // TODO(cloudwebrtc): Document that 'response' field in 'progress' event is null for incoming calls. - _progress('local', null); + _progress(Originator.local, null); } /** @@ -484,7 +484,7 @@ class RTCSession extends EventManager implements Owner { data = options['data'] ?? data; // Check Session Direction and Status. - if (_direction != 'incoming') { + if (_direction != Direction.incoming) { throw Exceptions.NotSupportedError( '"answer" not supported for outgoing RTCSession'); } @@ -585,7 +585,8 @@ class RTCSession extends EventManager implements Owner { // A local MediaStream is given, use it. if (mediaStream != null) { stream = mediaStream; - emit(EventStream(session: this, originator: 'local', stream: stream)); + emit(EventStream( + session: this, originator: Originator.local, stream: stream)); } // Audio and/or video requested, prompt getUserMedia. else if (mediaConstraints['audio'] != null || @@ -593,14 +594,15 @@ class RTCSession extends EventManager implements Owner { _localMediaStreamLocallyGenerated = true; try { stream = await navigator.mediaDevices.getUserMedia(mediaConstraints); - emit(EventStream(session: this, originator: 'local', stream: stream)); + emit(EventStream( + session: this, originator: Originator.local, stream: stream)); } catch (error) { if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } request.reply(480); _failed( - 'local', + Originator.local, null, null, null, @@ -641,15 +643,17 @@ class RTCSession extends EventManager implements Owner { logger.d('emit "sdp"'); final String? processedSDP = _sdpOfferToWebRTC(request.body); - emit(EventSdp(originator: 'remote', type: 'offer', sdp: processedSDP)); + emit(EventSdp( + originator: Originator.remote, type: SdpType.offer, sdp: processedSDP)); - RTCSessionDescription offer = RTCSessionDescription(processedSDP, 'offer'); + RTCSessionDescription offer = + RTCSessionDescription(processedSDP, SdpType.offer.name); try { await _connection!.setRemoteDescription(offer); } catch (error) { request.reply(488); _failed( - 'system', + Originator.system, null, null, null, @@ -673,9 +677,11 @@ class RTCSession extends EventManager implements Owner { RTCSessionDescription desc; try { if (!_late_sdp) { - desc = await _createLocalDescription('answer', rtcAnswerConstraints); + desc = + await _createLocalDescription(SdpType.answer, rtcAnswerConstraints); } else { - desc = await _createLocalDescription('offer', _rtcOfferConstraints); + desc = + await _createLocalDescription(SdpType.offer, _rtcOfferConstraints); } } catch (e) { request.reply(500); @@ -693,9 +699,9 @@ class RTCSession extends EventManager implements Owner { _state = RtcSessionState.waitingForAck; _setInvite2xxTimer(request, desc.sdp); _setACKTimer(); - _accepted('local'); + _accepted(Originator.local); }, () { - _failed('system', null, null, null, 500, + _failed(Originator.system, null, null, null, 500, DartSIP_C.CausesType.CONNECTION_ERROR, 'Transport Error'); }); } catch (error, s) { @@ -757,7 +763,7 @@ class RTCSession extends EventManager implements Owner { _state = RtcSessionState.canceled; cancel_reason = cancel_reason ?? 'Canceled by local'; status_code = status_code ?? 100; - _failed('local', null, null, null, status_code, + _failed(Originator.local, null, null, null, status_code, DartSIP_C.CausesType.CANCELED, cancel_reason); break; @@ -774,7 +780,7 @@ class RTCSession extends EventManager implements Owner { } _request.reply(status_code, reason_phrase, extraHeaders, body); - _failed('local', null, null, null, status_code, + _failed(Originator.local, null, null, null, status_code, DartSIP_C.CausesType.REJECTED, reason_phrase); break; case RtcSessionState.waitingForAck: @@ -799,7 +805,7 @@ class RTCSession extends EventManager implements Owner { * transaction times out." */ if (_state == RtcSessionState.waitingForAck && - _direction == 'incoming' && + _direction == Direction.incoming && _request.server_transaction.state != TransactionState.TERMINATED) { /// Save the dialog for later restoration. Dialog dialog = _dialog!; @@ -829,14 +835,13 @@ class RTCSession extends EventManager implements Owner { }); _ended( - 'local', - null, - ErrorCause( - cause: cause as String?, - status_code: status_code, - reason_phrase: reason_phrase, - ), - ); + Originator.local, + null, + ErrorCause( + cause: cause as String?, + status_code: status_code, + reason_phrase: reason_phrase, + )); // Restore the dialog into 'this' in order to be able to send the in-dialog BYE :-). _dialog = dialog; @@ -849,13 +854,12 @@ class RTCSession extends EventManager implements Owner { reason_phrase = reason_phrase ?? 'Terminated by local'; status_code = status_code ?? 200; _ended( - 'local', - null, - ErrorCause( - cause: cause as String?, - status_code: status_code, - reason_phrase: reason_phrase), - ); + Originator.local, + null, + ErrorCause( + cause: cause as String?, + status_code: status_code, + reason_phrase: reason_phrase)); } break; default: @@ -1056,7 +1060,7 @@ class RTCSession extends EventManager implements Owner { } _localHold = true; - _onhold('local'); + _onhold(Originator.local); EventManager handlers = EventManager(); @@ -1109,7 +1113,7 @@ class RTCSession extends EventManager implements Owner { } _localHold = false; - _onunhold('local'); + _onunhold(Originator.local); EventManager handlers = EventManager(); handlers.on(EventSucceeded(), (EventSucceeded event) { @@ -1298,7 +1302,7 @@ class RTCSession extends EventManager implements Owner { _state == RtcSessionState.answered) { _state = RtcSessionState.canceled; _request.reply(487); - _failed('remote', null, request, null, 487, + _failed(Originator.remote, null, request, null, 487, DartSIP_C.CausesType.CANCELED, request.reason_phrase); } } else { @@ -1324,10 +1328,12 @@ class RTCSession extends EventManager implements Owner { logger.d('emit "sdp"'); emit(EventSdp( - originator: 'remote', type: 'answer', sdp: request.body)); + originator: Originator.remote, + type: SdpType.answer, + sdp: request.body)); RTCSessionDescription answer = - RTCSessionDescription(request.body, 'answer'); + RTCSessionDescription(request.body, SdpType.answer.name); try { await _connection!.setRemoteDescription(answer); } catch (error) { @@ -1341,14 +1347,14 @@ class RTCSession extends EventManager implements Owner { } } if (!_is_confirmed) { - _confirmed('remote', request); + _confirmed(Originator.remote, request); } break; case SipMethod.BYE: if (_state == RtcSessionState.confirmed) { request.reply(200); _ended( - 'remote', + Originator.remote, request, ErrorCause( cause: DartSIP_C.CausesType.BYE, @@ -1358,7 +1364,7 @@ class RTCSession extends EventManager implements Owner { request.reply(200); _request.reply(487, 'BYE Received'); _ended( - 'remote', + Originator.remote, request, ErrorCause( cause: DartSIP_C.CausesType.BYE, @@ -1465,14 +1471,14 @@ class RTCSession extends EventManager implements Owner { } // Called from DTMF handler. - void newDTMF(String originator, DTMF dtmf, dynamic request) { + void newDTMF(Originator originator, DTMF dtmf, dynamic request) { logger.d('newDTMF()'); emit(EventNewDTMF(originator: originator, dtmf: dtmf, request: request)); } // Called from Info handler. - void newInfo(String originator, Info info, dynamic request) { + void newInfo(Originator originator, Info info, dynamic request) { logger.d('newInfo()'); emit(EventNewInfo(originator: originator, info: info, request: request)); @@ -1602,7 +1608,7 @@ class RTCSession extends EventManager implements Owner { clearTimeout(_timers.invite2xxTimer); sendRequest(SipMethod.BYE); _ended( - 'remote', + Originator.remote, null, ErrorCause( cause: DartSIP_C.CausesType.NO_ACK, @@ -1650,14 +1656,16 @@ class RTCSession extends EventManager implements Owner { _connection!.onTrack = (RTCTrackEvent event) { if (event.streams.isNotEmpty) { emit(EventStream( - session: this, originator: 'remote', stream: event.streams[0])); + session: this, + originator: Originator.remote, + stream: event.streams[0])); } }; break; case 'plan-b': _connection!.onAddStream = (MediaStream stream) { - emit( - EventStream(session: this, originator: 'remote', stream: stream)); + emit(EventStream( + session: this, originator: Originator.remote, stream: stream)); }; break; } @@ -1668,7 +1676,7 @@ class RTCSession extends EventManager implements Owner { } Future _createLocalDescription( - String type, Map? constraints) async { + SdpType type, Map? constraints) async { logger.d('createLocalDescription()'); _iceGatheringState ??= RTCIceGatheringState.RTCIceGatheringStateNew; Completer completer = @@ -1686,14 +1694,14 @@ class RTCSession extends EventManager implements Owner { constraints['offerModifiers'] = null; - if (type != 'offer' && type != 'answer') { + if (type != SdpType.offer && type != SdpType.answer) { completer.completeError(Exceptions.TypeError( 'createLocalDescription() | invalid type "$type"')); } _rtcReady = false; late RTCSessionDescription desc; - if (type == 'offer') { + if (type == SdpType.offer) { try { desc = await _connection!.createOffer(constraints); } catch (error) { @@ -1730,7 +1738,8 @@ class RTCSession extends EventManager implements Owner { _rtcReady = true; RTCSessionDescription? desc = await _connection!.getLocalDescription(); logger.d('emit "sdp"'); - emit(EventSdp(originator: 'local', type: type, sdp: desc!.sdp)); + emit( + EventSdp(originator: Originator.local, type: type, sdp: desc!.sdp)); completer.complete(desc); } } @@ -1777,7 +1786,7 @@ class RTCSession extends EventManager implements Owner { _rtcReady = true; RTCSessionDescription? desc = await _connection!.getLocalDescription(); logger.d('emit "sdp"'); - emit(EventSdp(originator: 'local', type: type, sdp: desc!.sdp)); + emit(EventSdp(originator: Originator.local, type: type, sdp: desc!.sdp)); return desc; } @@ -1803,7 +1812,7 @@ class RTCSession extends EventManager implements Owner { } catch (error) { logger.d('$error'); _failed( - 'remote', + Originator.remote, message, null, null, @@ -1836,7 +1845,7 @@ class RTCSession extends EventManager implements Owner { } catch (error) { logger.d(error.toString()); _failed( - 'remote', + Originator.remote, message, null, null, @@ -1882,7 +1891,7 @@ class RTCSession extends EventManager implements Owner { try { RTCSessionDescription desc = - await _createLocalDescription('offer', _rtcOfferConstraints); + await _createLocalDescription(SdpType.offer, _rtcOfferConstraints); sendAnswer(desc.sdp); } catch (_) { request.reply(500); @@ -2082,7 +2091,7 @@ class RTCSession extends EventManager implements Owner { } } emit(EventStream( - session: this, originator: 'local', stream: localStream)); + session: this, originator: Originator.local, stream: localStream)); } else { logger.w( 'Remote wants to upgrade to video but no camera available to send'); @@ -2091,9 +2100,11 @@ class RTCSession extends EventManager implements Owner { logger.d('emit "sdp"'); final String? processedSDP = _sdpOfferToWebRTC(request.body); - emit(EventSdp(originator: 'remote', type: 'offer', sdp: processedSDP)); + emit(EventSdp( + originator: Originator.remote, type: SdpType.offer, sdp: processedSDP)); - RTCSessionDescription offer = RTCSessionDescription(processedSDP, 'offer'); + RTCSessionDescription offer = + RTCSessionDescription(processedSDP, SdpType.offer.name); if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); @@ -2117,10 +2128,10 @@ class RTCSession extends EventManager implements Owner { if (_remoteHold == true && hold == false) { _remoteHold = false; - _onunhold('remote'); + _onunhold(Originator.remote); } else if (_remoteHold == false && hold == true) { _remoteHold = true; - _onhold('remote'); + _onhold(Originator.remote); } // Create local description. @@ -2130,7 +2141,8 @@ class RTCSession extends EventManager implements Owner { } try { - return await _createLocalDescription('answer', _rtcAnswerConstraints); + return await _createLocalDescription( + SdpType.answer, _rtcAnswerConstraints); } catch (_) { request.reply(500); throw Exceptions.TypeError('_createLocalDescription() failed'); @@ -2342,20 +2354,22 @@ class RTCSession extends EventManager implements Owner { // A stream is given, var the app set events such as 'peerconnection' and 'connecting'. if (mediaStream != null) { stream = mediaStream; - emit(EventStream(session: this, originator: 'local', stream: stream)); + emit(EventStream( + session: this, originator: Originator.local, stream: stream)); } // Request for user media access. else if (mediaConstraints['audio'] != null || mediaConstraints['video'] != null) { _localMediaStreamLocallyGenerated = true; try { stream = await navigator.mediaDevices.getUserMedia(mediaConstraints); - emit(EventStream(session: this, originator: 'local', stream: stream)); + emit(EventStream( + session: this, originator: Originator.local, stream: stream)); } catch (error) { if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } _failed( - 'local', + Originator.local, null, null, null, @@ -2394,7 +2408,7 @@ class RTCSession extends EventManager implements Owner { _connecting(_request); try { RTCSessionDescription desc = - await _createLocalDescription('offer', rtcOfferConstraints); + await _createLocalDescription(SdpType.offer, rtcOfferConstraints); if (_is_canceled || _state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } @@ -2410,8 +2424,8 @@ class RTCSession extends EventManager implements Owner { request_sender.send(); } catch (error, s) { logger.e(error.toString(), error: error, stackTrace: s); - _failed('local', null, null, null, 500, DartSIP_C.CausesType.WEBRTC_ERROR, - 'Can\'t create local SDP'); + _failed(Originator.local, null, null, null, 500, + DartSIP_C.CausesType.WEBRTC_ERROR, 'Can\'t create local SDP'); if (_state == RtcSessionState.terminated) { return; } @@ -2494,17 +2508,20 @@ class RTCSession extends EventManager implements Owner { } _state = RtcSessionState.provisionalResponse; - _progress('remote', response, int.parse(status_code)); + _progress(Originator.remote, response, int.parse(status_code)); if (response.body == null || response.body!.isEmpty) { return; } logger.d('emit "sdp"'); - emit(EventSdp(originator: 'remote', type: 'answer', sdp: response.body)); + emit(EventSdp( + originator: Originator.remote, + type: SdpType.answer, + sdp: response.body)); RTCSessionDescription answer = - RTCSessionDescription(response.body, 'answer'); + RTCSessionDescription(response.body, SdpType.answer.name); try { await _connection!.setRemoteDescription(answer); @@ -2519,7 +2536,7 @@ class RTCSession extends EventManager implements Owner { if (response.body == null || response.body!.isEmpty) { _acceptAndTerminate(response, 400, DartSIP_C.CausesType.MISSING_SDP); - _failed('remote', null, null, response, 400, + _failed(Originator.remote, null, null, response, 400, DartSIP_C.CausesType.BAD_MEDIA_DESCRIPTION, 'Missing SDP'); return; } @@ -2528,7 +2545,7 @@ class RTCSession extends EventManager implements Owner { if (mediaPort == 0 && _ua.configuration.terminateOnAudioMediaPortZero) { _acceptAndTerminate(response, 400, DartSIP_C.CausesType.MISSING_SDP); - _failed('remote', null, null, response, 400, + _failed(Originator.remote, null, null, response, 400, DartSIP_C.CausesType.BAD_MEDIA_DESCRIPTION, 'Media port is zero'); return; } @@ -2539,10 +2556,13 @@ class RTCSession extends EventManager implements Owner { } logger.d('emit "sdp"'); - emit(EventSdp(originator: 'remote', type: 'answer', sdp: response.body)); + emit(EventSdp( + originator: Originator.remote, + type: SdpType.answer, + sdp: response.body)); RTCSessionDescription answer = - RTCSessionDescription(response.body, 'answer'); + RTCSessionDescription(response.body, SdpType.answer.name); // Be ready for 200 with SDP after a 180/183 with SDP. // We created a SDP 'answer' for it, so check the current signaling state. @@ -2557,7 +2577,7 @@ class RTCSession extends EventManager implements Owner { } catch (error) { _acceptAndTerminate(response, 500, error.toString()); _failed( - 'local', + Originator.local, null, null, response, @@ -2571,12 +2591,12 @@ class RTCSession extends EventManager implements Owner { await _connection!.setRemoteDescription(answer); // Handle Session Timers. _handleSessionTimersInIncomingResponse(response); - _accepted('remote', response); + _accepted(Originator.remote, response); OutgoingRequest ack = sendRequest(SipMethod.ACK); - _confirmed('local', ack); + _confirmed(Originator.local, ack); } catch (error) { _acceptAndTerminate(response, 488, 'Not Acceptable Here'); - _failed('remote', null, null, response, 488, + _failed(Originator.remote, null, null, response, 488, DartSIP_C.CausesType.BAD_MEDIA_DESCRIPTION, 'Not Acceptable Here'); logger.e( 'emit "peerconnection:setremotedescriptionfailed" [error:${error.toString()}]'); @@ -2584,8 +2604,8 @@ class RTCSession extends EventManager implements Owner { } } else { String cause = utils.sipErrorCause(response.status_code); - _failed('remote', null, null, response, response.status_code, cause, - response.reason_phrase); + _failed(Originator.remote, null, null, response, response.status_code, + cause, response.reason_phrase); } } @@ -2644,10 +2664,14 @@ class RTCSession extends EventManager implements Owner { } logger.d('emit "sdp"'); - emit(EventSdp(originator: 'remote', type: 'answer', sdp: response.body)); + emit(EventSdp( + originator: Originator.remote, + type: SdpType.answer, + sdp: response.body, + )); RTCSessionDescription answer = - RTCSessionDescription(response.body, 'answer'); + RTCSessionDescription(response.body, SdpType.answer.name); try { await _connection!.setRemoteDescription(answer); @@ -2662,10 +2686,11 @@ class RTCSession extends EventManager implements Owner { try { RTCSessionDescription desc = - await _createLocalDescription('offer', rtcOfferConstraints); + await _createLocalDescription(SdpType.offer, rtcOfferConstraints); String? sdp = _mangleOffer(desc.sdp); logger.d('emit "sdp"'); - emit(EventSdp(originator: 'local', type: 'offer', sdp: sdp)); + emit(EventSdp( + originator: Originator.local, type: SdpType.offer, sdp: sdp)); EventManager handlers = EventManager(); handlers.on(EventOnSuccessResponse(), (EventOnSuccessResponse event) { @@ -2741,14 +2766,16 @@ class RTCSession extends EventManager implements Owner { } emit(EventStream( - session: this, originator: 'local', stream: _localMediaStream)); + session: this, + originator: Originator.local, + stream: _localMediaStream)); } catch (error) { if (_state == RtcSessionState.terminated) { throw Exceptions.InvalidStateError('terminated'); } request.reply(480); _failed( - 'local', + Originator.local, null, null, null, @@ -2800,10 +2827,13 @@ class RTCSession extends EventManager implements Owner { } logger.d('emit "sdp"'); - emit(EventSdp(originator: 'remote', type: 'answer', sdp: response.body)); + emit(EventSdp( + originator: Originator.remote, + type: SdpType.answer, + sdp: response.body)); RTCSessionDescription answer = - RTCSessionDescription(response.body, 'answer'); + RTCSessionDescription(response.body, SdpType.answer.name); try { await _connection!.setRemoteDescription(answer); @@ -2818,10 +2848,11 @@ class RTCSession extends EventManager implements Owner { try { RTCSessionDescription desc = - await _createLocalDescription('offer', rtcOfferConstraints); + await _createLocalDescription(SdpType.offer, rtcOfferConstraints); String? sdp = _mangleOffer(desc.sdp); logger.d('emit "sdp"'); - emit(EventSdp(originator: 'local', type: 'offer', sdp: sdp)); + emit(EventSdp( + originator: Originator.local, type: SdpType.offer, sdp: sdp)); EventManager handlers = EventManager(); handlers.on(EventOnSuccessResponse(), (EventOnSuccessResponse event) { @@ -2906,11 +2937,13 @@ class RTCSession extends EventManager implements Owner { } logger.d('emit "sdp"'); - emit( - EventSdp(originator: 'remote', type: 'answer', sdp: response.body)); + emit(EventSdp( + originator: Originator.remote, + type: SdpType.answer, + sdp: response.body)); RTCSessionDescription answer = - RTCSessionDescription(response.body, 'answer'); + RTCSessionDescription(response.body, SdpType.answer.name); try { await _connection!.setRemoteDescription(answer); @@ -2932,11 +2965,12 @@ class RTCSession extends EventManager implements Owner { extraHeaders.add('Content-Type: application/sdp'); try { RTCSessionDescription desc = - await _createLocalDescription('offer', rtcOfferConstraints); + await _createLocalDescription(SdpType.offer, rtcOfferConstraints); String? sdp = _mangleOffer(desc.sdp); logger.d('emit "sdp"'); - emit(EventSdp(originator: 'local', type: 'offer', sdp: sdp)); + emit(EventSdp( + originator: Originator.local, type: SdpType.offer, sdp: sdp)); EventManager handlers = EventManager(); handlers.on(EventOnSuccessResponse(), (EventOnSuccessResponse event) { @@ -3226,7 +3260,7 @@ class RTCSession extends EventManager implements Owner { } } - void _newRTCSession(String originator, dynamic request) { + void _newRTCSession(Originator originator, dynamic request) { logger.d('newRTCSession()'); _ua.newRTCSession(originator: originator, session: this, request: request); } @@ -3237,7 +3271,7 @@ class RTCSession extends EventManager implements Owner { emit(EventCallConnecting(session: this, request: request)); } - void _progress(String originator, dynamic response, [int? status_code]) { + void _progress(Originator originator, dynamic response, [int? status_code]) { logger.d('session progress'); logger.d('emit "progress"'); @@ -3250,7 +3284,7 @@ class RTCSession extends EventManager implements Owner { cause: errorCause)); } - void _accepted(String originator, [dynamic message]) { + void _accepted(Originator originator, [dynamic message]) { logger.d('session accepted'); _start_time = DateTime.now(); logger.d('emit "accepted"'); @@ -3258,14 +3292,15 @@ class RTCSession extends EventManager implements Owner { session: this, originator: originator, response: message)); } - void _confirmed(String originator, dynamic ack) { + void _confirmed(Originator originator, dynamic ack) { logger.d('session confirmed'); _is_confirmed = true; logger.d('emit "confirmed"'); emit(EventCallConfirmed(session: this, originator: originator, ack: ack)); } - void _ended(String originator, IncomingRequest? request, ErrorCause cause) { + void _ended( + Originator originator, IncomingRequest? request, ErrorCause cause) { logger.d('session ended'); _end_time = DateTime.now(); _close(); @@ -3274,7 +3309,7 @@ class RTCSession extends EventManager implements Owner { session: this, originator: originator, request: request, cause: cause)); } - void _failed(String originator, dynamic message, dynamic request, + void _failed(Originator originator, dynamic message, dynamic request, dynamic response, int? status_code, String cause, String? reason_phrase) { logger.d('session failed'); @@ -3299,14 +3334,14 @@ class RTCSession extends EventManager implements Owner { response: response)); } - void _onhold(String originator) { + void _onhold(Originator originator) { logger.d('session onhold'); _setLocalMediaStatus(); logger.d('emit "hold"'); emit(EventCallHold(session: this, originator: originator)); } - void _onunhold(String originator) { + void _onunhold(Originator originator) { logger.d('session onunhold'); _setLocalMediaStatus(); logger.d('emit "unhold"'); diff --git a/lib/src/rtc_session/dtmf.dart b/lib/src/rtc_session/dtmf.dart index 60179beb..b7c27c65 100644 --- a/lib/src/rtc_session/dtmf.dart +++ b/lib/src/rtc_session/dtmf.dart @@ -24,7 +24,7 @@ class DTMF extends EventManager { final RTCSession _session; DtmfMode? _mode; - String? _direction; + Direction? _direction; String? _tone; int? _duration; int? _interToneGap; @@ -35,14 +35,14 @@ class DTMF extends EventManager { int? get duration => _duration; - String? get direction => _direction; + Direction? get direction => _direction; void send(String tone, Map options) { if (tone == null) { throw Exceptions.TypeError('Not enough arguments'); } - _direction = 'outgoing'; + _direction = Direction.outgoing; // Check RTCSession Status. if (_session.state != RtcSessionState.confirmed && @@ -78,16 +78,18 @@ class DTMF extends EventManager { body += 'Duration=$_duration'; - _session.newDTMF('local', this, _request); + _session.newDTMF(Originator.local, this, _request); EventManager handlers = EventManager(); handlers.on(EventOnSuccessResponse(), (EventOnSuccessResponse event) { - emit(EventSucceeded(originator: 'remote', response: event.response)); + emit(EventSucceeded( + originator: Originator.remote, response: event.response)); }); handlers.on(EventOnErrorResponse(), (EventOnErrorResponse event) { _eventHandlers.emit(EventOnFialed()); emit(EventOnFialed()); - emit(EventCallFailed(originator: 'remote', response: event.response)); + emit(EventCallFailed( + originator: Originator.remote, response: event.response)); }); handlers.on(EventOnRequestTimeout(), (EventOnRequestTimeout event) { _session.onRequestTimeout(); @@ -112,7 +114,7 @@ class DTMF extends EventManager { String reg_tone = r'^(Signal\s*?=\s*?)([0-9A-D#*]{1})(\s)?.*'; String reg_duration = r'^(Duration\s?=\s?)([0-9]{1,4})(\s)?.*'; - _direction = 'incoming'; + _direction = Direction.incoming; _request = request; request.reply(200); @@ -138,7 +140,7 @@ class DTMF extends EventManager { if (_tone == null) { logger.d('invalid INFO DTMF received, discarded'); } else { - _session.newDTMF('remote', this, request); + _session.newDTMF(Originator.remote, this, request); } } } diff --git a/lib/src/rtc_session/info.dart b/lib/src/rtc_session/info.dart index d74f70de..5399ba23 100644 --- a/lib/src/rtc_session/info.dart +++ b/lib/src/rtc_session/info.dart @@ -1,4 +1,5 @@ import '../constants.dart'; +import '../enums.dart'; import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../exceptions.dart' as Exceptions; @@ -10,7 +11,7 @@ class Info extends EventManager { Info(this._session); final RTCSession _session; - String? _direction; + Direction? _direction; String? _contentType; String? _body; IncomingRequest? _request; @@ -19,10 +20,10 @@ class Info extends EventManager { String? get body => _body; - String? get direction => _direction; + Direction? get direction => _direction; void send(String contentType, String body, Map options) { - _direction = 'outgoing'; + _direction = Direction.outgoing; if (contentType == null) { throw Exceptions.TypeError('Not enough arguments'); @@ -41,14 +42,16 @@ class Info extends EventManager { extraHeaders.add('Content-Type: $contentType'); - _session.newInfo('local', this, _request); + _session.newInfo(Originator.local, this, _request); EventManager handlers = EventManager(); handlers.on(EventOnSuccessResponse(), (EventOnSuccessResponse event) { - emit(EventSucceeded(originator: 'remote', response: event.response)); + emit(EventSucceeded( + originator: Originator.remote, response: event.response)); }); handlers.on(EventOnErrorResponse(), (EventOnErrorResponse event) { - emit(EventCallFailed(originator: 'remote', response: event.response)); + emit(EventCallFailed( + originator: Originator.remote, response: event.response)); }); handlers.on(EventOnTransportError(), (EventOnTransportError event) { _session.onTransportError(); @@ -68,7 +71,7 @@ class Info extends EventManager { } void init_incoming(IncomingRequest request) { - _direction = 'incoming'; + _direction = Direction.incoming; _request = request; request.reply(200); @@ -76,6 +79,6 @@ class Info extends EventManager { _contentType = request.getHeader('content-type'); _body = request.body; - _session.newInfo('remote', this, request); + _session.newInfo(Originator.remote, this, request); } } diff --git a/lib/src/rtc_session/refer_subscriber.dart b/lib/src/rtc_session/refer_subscriber.dart index 6d410df2..cb424349 100644 --- a/lib/src/rtc_session/refer_subscriber.dart +++ b/lib/src/rtc_session/refer_subscriber.dart @@ -1,4 +1,3 @@ -import 'package:sip_ua/src/sip_message.dart'; import '../constants.dart' as DartSIP_C; import '../constants.dart'; import '../event_manager/event_manager.dart'; @@ -6,6 +5,7 @@ import '../event_manager/internal_events.dart'; import '../grammar.dart'; import '../logger.dart'; import '../rtc_session.dart' as rtc; +import '../sip_message.dart'; import '../uri.dart'; import '../utils.dart' as Utils; diff --git a/lib/src/sip_message.dart b/lib/src/sip_message.dart index c593fe0f..1c6e6eff 100644 --- a/lib/src/sip_message.dart +++ b/lib/src/sip_message.dart @@ -2,7 +2,6 @@ import 'dart:convert' show utf8; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; -import 'package:sip_ua/src/transactions/transaction_base.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'data.dart'; @@ -11,6 +10,7 @@ import 'grammar.dart'; import 'logger.dart'; import 'name_addr_header.dart'; import 'socket_transport.dart'; +import 'transactions/transaction_base.dart'; import 'ua.dart'; import 'uri.dart'; import 'utils.dart' as utils; diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index f2562b07..1fb9c780 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -4,22 +4,24 @@ import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:logger/logger.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; -import 'package:sip_ua/sip_ua.dart'; -import 'package:sip_ua/src/event_manager/internal_events.dart'; -import 'package:sip_ua/src/map_helper.dart'; -import 'package:sip_ua/src/transports/socket_interface.dart'; -import 'package:sip_ua/src/transports/tcp_socket.dart'; import 'config.dart'; import 'constants.dart' as DartSIP_C; +import 'enums.dart'; import 'event_manager/event_manager.dart'; +import 'event_manager/internal_events.dart'; import 'event_manager/subscriber_events.dart'; import 'logger.dart'; +import 'map_helper.dart'; import 'message.dart'; import 'options.dart'; import 'rtc_session.dart'; import 'rtc_session/refer_subscriber.dart'; +import 'sip_message.dart'; import 'stack_trace_nj.dart'; import 'subscriber.dart'; +import 'transport_type.dart'; +import 'transports/socket_interface.dart'; +import 'transports/tcp_socket.dart'; import 'transports/web_socket.dart'; import 'ua.dart'; @@ -233,7 +235,7 @@ class SIPUAHelper extends EventManager { _ua!.on(EventNewRTCSession(), (EventNewRTCSession event) { logger.d('newRTCSession => $event'); RTCSession session = event.session!; - if (session.direction == 'incoming') { + if (session.direction == Direction.incoming) { // Set event handlers. session.addAllEventHandlers( buildCallOptions()['eventHandlers'] as EventManager); @@ -251,7 +253,7 @@ class SIPUAHelper extends EventManager { _ua!.on(EventNewMessage(), (EventNewMessage event) { logger.d('newMessage => $event'); //Only notify incoming message to listener - if (event.message!.direction == 'incoming') { + if (event.message!.direction == Direction.incoming) { SIPMessageRequest message = SIPMessageRequest(event.message, event.originator, event.request); _notifyNewMessageListeners(message); @@ -560,7 +562,7 @@ class Call { assert(_session != null, 'ERROR(hangup): rtc session is invalid!'); if (peerConnection != null) { for (MediaStream? stream in peerConnection!.getLocalStreams()) { - if (stream == null) continue; + if (stream == null) return; logger.d( 'Stopping local stream with tracks: ${stream.getTracks().length}'); for (MediaStreamTrack track in stream.getTracks()) { @@ -569,7 +571,7 @@ class Call { } } for (MediaStream? stream in peerConnection!.getRemoteStreams()) { - if (stream == null) continue; + if (stream == null) return; logger.d( 'Stopping remote stream with tracks: ${stream.getTracks().length}'); for (MediaStreamTrack track in stream.getTracks()) { @@ -663,12 +665,9 @@ class Call { return ''; } - String get direction { + Direction? get direction { assert(_session != null, 'ERROR(get direction): rtc session is invalid!'); - if (_session.direction != null) { - return _session.direction!.toUpperCase(); - } - return ''; + return _session.direction; } bool get remote_has_audio => _peerHasMediaLine('audio'); @@ -715,7 +714,7 @@ class CallState { this.refer}); CallStateEnum state; ErrorCause? cause; - String? originator; + Originator? originator; bool? audio; bool? video; MediaStream? stream; @@ -751,7 +750,7 @@ class TransportState { class SIPMessageRequest { SIPMessageRequest(this.message, this.originator, this.request); dynamic request; - String? originator; + Originator? originator; Message? message; } diff --git a/lib/src/transactions/ack_client.dart b/lib/src/transactions/ack_client.dart index e57b39aa..c1eacc1f 100644 --- a/lib/src/transactions/ack_client.dart +++ b/lib/src/transactions/ack_client.dart @@ -1,7 +1,7 @@ -import 'package:sip_ua/src/sip_message.dart'; import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; +import '../sip_message.dart'; import '../socket_transport.dart'; import '../ua.dart'; import '../utils.dart'; diff --git a/lib/src/transactions/invite_client.dart b/lib/src/transactions/invite_client.dart index 7c1a93a6..5e1290e1 100644 --- a/lib/src/transactions/invite_client.dart +++ b/lib/src/transactions/invite_client.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:sip_ua/src/event_manager/event_manager.dart'; import '../constants.dart'; +import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; import '../sip_message.dart'; diff --git a/lib/src/transactions/transactions.dart b/lib/src/transactions/transactions.dart index 83ed099d..257f29c3 100644 --- a/lib/src/transactions/transactions.dart +++ b/lib/src/transactions/transactions.dart @@ -1,5 +1,5 @@ -import 'package:sip_ua/src/sip_message.dart'; import '../constants.dart'; +import '../sip_message.dart'; import '../timers.dart'; import 'invite_server.dart'; import 'non_invite_server.dart'; diff --git a/lib/src/transports/tcp_socket.dart b/lib/src/transports/tcp_socket.dart index 59f67217..379be330 100644 --- a/lib/src/transports/tcp_socket.dart +++ b/lib/src/transports/tcp_socket.dart @@ -1,7 +1,7 @@ -import 'package:sip_ua/sip_ua.dart'; -import 'package:sip_ua/src/transports/socket_interface.dart'; -import 'package:sip_ua/src/transports/tcp_socket_impl.dart'; +import '../../sip_ua.dart'; import '../logger.dart'; +import 'socket_interface.dart'; +import 'tcp_socket_impl.dart'; class SIPUATcpSocket extends SIPUASocketInterface { SIPUATcpSocket(String host, String port, diff --git a/lib/src/transports/tcp_socket_impl.dart b/lib/src/transports/tcp_socket_impl.dart index d3e1de2e..68a4727d 100644 --- a/lib/src/transports/tcp_socket_impl.dart +++ b/lib/src/transports/tcp_socket_impl.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'package:sip_ua/src/sip_ua_helper.dart'; import '../logger.dart'; +import '../sip_ua_helper.dart'; typedef OnMessageCallback = void Function(dynamic msg); typedef OnCloseCallback = void Function(int? code, String? reason); diff --git a/lib/src/transports/web_socket.dart b/lib/src/transports/web_socket.dart index 9a7302bf..77c7753e 100644 --- a/lib/src/transports/web_socket.dart +++ b/lib/src/transports/web_socket.dart @@ -1,7 +1,7 @@ -import 'package:sip_ua/sip_ua.dart'; -import 'package:sip_ua/src/transports/socket_interface.dart'; +import '../../sip_ua.dart'; import '../grammar.dart'; import '../logger.dart'; +import 'socket_interface.dart'; import 'websocket_dart_impl.dart' if (dart.library.js) 'websocket_web_impl.dart'; diff --git a/lib/src/ua.dart b/lib/src/ua.dart index 4b6fc81b..8d3626da 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -6,6 +6,7 @@ import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'data.dart'; import 'dialog.dart'; +import 'enums.dart'; import 'event_manager/event_manager.dart'; import 'event_manager/internal_events.dart'; import 'exceptions.dart' as Exceptions; @@ -491,7 +492,7 @@ class UA extends EventManager { /** * Message */ - void newMessage(Message message, String originator, dynamic request) { + void newMessage(Message message, Originator originator, dynamic request) { if (_stopping) { return; } @@ -503,7 +504,7 @@ class UA extends EventManager { /** * Options */ - void newOptions(Options message, String originator, dynamic request) { + void newOptions(Options message, Originator originator, dynamic request) { if (_stopping) { return; } @@ -537,7 +538,7 @@ class UA extends EventManager { * RTCSession */ void newRTCSession( - {required RTCSession session, String? originator, dynamic request}) { + {required RTCSession session, Originator? originator, dynamic request}) { _sessions[session.id] = session; emit(EventNewRTCSession( session: session, originator: originator, request: request)); From 1622e271ab094629b34e682d3e82640c688a62e8 Mon Sep 17 00:00:00 2001 From: Victor Uvarov <35702719+VictorUvarov@users.noreply.github.com> Date: Sat, 4 Jan 2025 13:18:51 -0800 Subject: [PATCH 16/35] remove recase dependency (#512) --- example/lib/src/callscreen.dart | 2 +- example/lib/src/dialpad.dart | 2 +- example/lib/src/register.dart | 2 +- lib/sip_ua.dart | 1 - lib/src/enum_helper.dart | 43 --------------------------------- lib/src/logger.dart | 4 +-- pubspec.yaml | 1 - 7 files changed, 4 insertions(+), 51 deletions(-) delete mode 100644 lib/src/enum_helper.dart diff --git a/example/lib/src/callscreen.dart b/example/lib/src/callscreen.dart index 1d6bba56..9fbb7c14 100644 --- a/example/lib/src/callscreen.dart +++ b/example/lib/src/callscreen.dart @@ -655,7 +655,7 @@ class _MyCallScreenWidget extends State return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, - title: Text('[$direction] ${EnumHelper.getName(_state)}'), + title: Text('[$direction] ${_state.name}'), ), body: _buildContent(), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, diff --git a/example/lib/src/dialpad.dart b/example/lib/src/dialpad.dart index 6ec93de6..0b5b6e0a 100644 --- a/example/lib/src/dialpad.dart +++ b/example/lib/src/dialpad.dart @@ -321,7 +321,7 @@ class _MyDialPadWidget extends State SizedBox(height: 8), Center( child: Text( - 'Register Status: ${EnumHelper.getName(helper!.registerState.state)}', + 'Register Status: ${helper!.registerState.state?.name ?? ''}', style: TextStyle(fontSize: 18, color: textColor), ), ), diff --git a/example/lib/src/register.dart b/example/lib/src/register.dart index fecc2471..d6071440 100644 --- a/example/lib/src/register.dart +++ b/example/lib/src/register.dart @@ -182,7 +182,7 @@ class _MyRegisterWidget extends State children: [ Center( child: Text( - 'Register Status: ${EnumHelper.getName(_registerState.state)}', + 'Register Status: ${_registerState.state?.name ?? ''}', style: TextStyle(fontSize: 18, color: textColor), ), ), diff --git a/lib/sip_ua.dart b/lib/sip_ua.dart index 8fe2256f..7c37f449 100644 --- a/lib/sip_ua.dart +++ b/lib/sip_ua.dart @@ -1,4 +1,3 @@ -export 'src/enum_helper.dart'; export 'src/enums.dart'; export 'src/sip_message.dart'; export 'src/sip_ua_helper.dart'; diff --git a/lib/src/enum_helper.dart b/lib/src/enum_helper.dart deleted file mode 100644 index 55f8949e..00000000 --- a/lib/src/enum_helper.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:recase/recase.dart'; - -/// -/// Provides a collection of methods that help when working with -/// enums. -/// -class EnumHelper { - static T getByIndex(List values, int index) { - return values.elementAt(index - 1); - } - - static int getIndexOf(List values, T value) { - return values.indexOf(value); - } - - /// - /// Returns the Enum name without the enum class. - /// e.g. DayName.Wednesday becomes Wednesday. - /// By default we recase the value to Title Case. - /// You can pass an alternate method to control the format. - /// - static String getName(T enumValue, - {String Function(String value) recase = reCase}) { - String name = enumValue.toString(); - int period = name.indexOf('.'); - - return recase(name.substring(period + 1)); - } - - static String reCase(String value) { - return ReCase(value).titleCase; - } - - static T getEnum(String enumName, List values) { - String cleanedName = reCase(enumName); - for (int i = 0; i < values.length; i++) { - if (cleanedName == getName(values[i])) { - return values[i]; - } - } - throw Exception("$cleanedName doesn't exist in the list of enums $values"); - } -} diff --git a/lib/src/logger.dart b/lib/src/logger.dart index d8989695..21169d23 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -1,7 +1,6 @@ import 'package:intl/intl.dart'; import 'package:logger/logger.dart'; -import 'enum_helper.dart'; import 'stack_trace_nj.dart'; Logger logger = Log(); @@ -41,8 +40,7 @@ class MyLogPrinter extends LogPrinter { @override List log(LogEvent event) { - if (EnumHelper.getIndexOf(Level.values, Log._loggingLevel) > - EnumHelper.getIndexOf(Level.values, event.level)) { + if (Log._loggingLevel.index > event.level.index) { // don't log events where the log level is set higher return []; } diff --git a/pubspec.yaml b/pubspec.yaml index 6535e6de..ae977034 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,6 @@ dependencies: logger: ^2.0.2+1 path: ^1.6.4 random_string: ^2.3.1 - recase: ^4.1.0 sdp_transform: ^0.3.2 text: ^0.2.0 uuid: ^4.2.1 From 651859c6c885b577cdc89e341fd352686f3f8100 Mon Sep 17 00:00:00 2001 From: Victor Uvarov <35702719+VictorUvarov@users.noreply.github.com> Date: Mon, 3 Feb 2025 05:19:30 -0800 Subject: [PATCH 17/35] Parse the target uri and contact uri as soon they are provided in the settings. (#513) - The lib was juggling between string or URI? and assuming URI the majority of the time --- lib/src/config.dart | 4 ++-- lib/src/rtc_session/refer_subscriber.dart | 2 +- lib/src/sip_ua_helper.dart | 6 ++++-- lib/src/ua.dart | 10 +++++----- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/src/config.dart b/lib/src/config.dart index 8273df83..aa66a138 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -18,8 +18,8 @@ class Settings { // SIP account. String? display_name; - dynamic uri; - dynamic contact_uri; + URI? uri; + URI? contact_uri; String user_agent = DartSIP_C.USER_AGENT; // SIP instance id (GRUU). diff --git a/lib/src/rtc_session/refer_subscriber.dart b/lib/src/rtc_session/refer_subscriber.dart index cb424349..716db186 100644 --- a/lib/src/rtc_session/refer_subscriber.dart +++ b/lib/src/rtc_session/refer_subscriber.dart @@ -45,7 +45,7 @@ class ReferSubscriber extends EventManager { // Referred-By header field. String referredBy = - 'Referred-By: <${_session.ua.configuration.uri.scheme}:${_session.ua.configuration.uri.user}@${_session.ua.configuration.uri.host}>'; + 'Referred-By: <${_session.ua.configuration.uri!.scheme}:${_session.ua.configuration.uri!.user}@${_session.ua.configuration.uri!.host}>'; extraHeaders.add(referredBy); extraHeaders.add('Contact: ${_session.contact}'); diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 1fb9c780..35282cb4 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -161,7 +161,7 @@ class SIPUAHelper extends EventManager { } _settings.transportType = uaSettings.transportType!; - _settings.uri = uaSettings.uri; + _settings.uri = uaSettings.uri != null ? URI.parse(uaSettings.uri!) : null; _settings.sip_message_delay = uaSettings.sip_message_delay; _settings.realm = uaSettings.realm; _settings.password = uaSettings.password; @@ -181,7 +181,9 @@ class SIPUAHelper extends EventManager { uaSettings.sessionTimersRefreshMethod; _settings.instance_id = uaSettings.instanceId; _settings.registrar_server = uaSettings.registrarServer; - _settings.contact_uri = uaSettings.contact_uri; + _settings.contact_uri = uaSettings.contact_uri != null + ? URI.parse(uaSettings.contact_uri!) + : null; _settings.connection_recovery_max_interval = uaSettings.connectionRecoveryMaxInterval; _settings.connection_recovery_min_interval = diff --git a/lib/src/ua.dart b/lib/src/ua.dart index 8d3626da..a93ad411 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -595,7 +595,7 @@ class UA extends EventManager { DartSIP_C.SipMethod? method = request.method; // Check that request URI points to us. - if (request.ruri!.user != _configuration.uri.user && + if (request.ruri!.user != _configuration.uri!.user && request.ruri!.user != _contact!.uri!.user) { logger.d('Request-URI does not point to us'); if (request.method != SipMethod.ACK) { @@ -830,7 +830,7 @@ class UA extends EventManager { _configuration.jssip_id = Utils.createRandomToken(5); // String containing _configuration.uri without scheme and user. - URI hostport_params = _configuration.uri.clone(); + URI hostport_params = _configuration.uri!.clone(); _configuration.terminateOnAudioMediaPortZero = configuration.terminateOnAudioMediaPortZero; @@ -865,12 +865,12 @@ class UA extends EventManager { // Check whether authorization_user is explicitly defined. // Take '_configuration.uri.user' value if not. - _configuration.authorization_user ??= _configuration.uri.user; + _configuration.authorization_user ??= _configuration.uri!.user; // If no 'registrar_server' is set use the 'uri' value without user portion and // without URI params/headers. if (_configuration.registrar_server == null) { - URI registrar_server = _configuration.uri.clone(); + URI registrar_server = _configuration.uri!.clone(); registrar_server.user = null; registrar_server.clearParams(); registrar_server.clearHeaders(); @@ -890,7 +890,7 @@ class UA extends EventManager { // Via Host. if (_configuration.contact_uri != null) { - _configuration.via_host = _configuration.contact_uri.host; + _configuration.via_host = _configuration.contact_uri!.host; } // Contact URI. else { From 9e1b995c8ffde8ea748b40f51626b765b0e78daf Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Mon, 3 Feb 2025 13:44:12 +0000 Subject: [PATCH 18/35] Analyze and Import sorter fixes --- .github/workflows/pushMaster.yaml | 2 +- lib/src/config.dart | 1 + lib/src/data.dart | 2 ++ lib/src/dialog.dart | 1 + lib/src/dialog/request_sender.dart | 2 ++ lib/src/digest_authentication.dart | 1 + lib/src/event_manager/call_events.dart | 2 ++ lib/src/event_manager/event_manager.dart | 1 + lib/src/event_manager/internal_events.dart | 2 ++ lib/src/event_manager/message_events.dart | 1 + lib/src/event_manager/notifier_events.dart | 1 + lib/src/event_manager/options_events.dart | 1 + lib/src/event_manager/refer_events.dart | 1 + lib/src/event_manager/register_events.dart | 1 + lib/src/event_manager/subscriber_events.dart | 1 + lib/src/event_manager/transport_events.dart | 1 + lib/src/exceptions.dart | 1 + lib/src/grammar.dart | 1 + lib/src/grammar_parser.dart | 2 ++ lib/src/logger.dart | 2 ++ lib/src/message.dart | 1 + lib/src/name_addr_header.dart | 1 + lib/src/options.dart | 1 + lib/src/parser.dart | 2 ++ lib/src/parser_error.dart | 1 + lib/src/registrator.dart | 2 ++ lib/src/request_sender.dart | 1 + lib/src/rtc_session.dart | 3 +++ lib/src/rtc_session/dtmf.dart | 2 ++ lib/src/rtc_session/info.dart | 1 + lib/src/rtc_session/refer_notifier.dart | 1 + lib/src/rtc_session/refer_subscriber.dart | 1 + lib/src/sanity_check.dart | 1 + lib/src/sip_message.dart | 3 +++ lib/src/sip_ua_helper.dart | 4 ++++ lib/src/socket_transport.dart | 2 ++ lib/src/stack_trace_nj.dart | 2 ++ lib/src/subscriber.dart | 3 +++ lib/src/timers.dart | 1 + lib/src/transactions/ack_client.dart | 1 + lib/src/transactions/invite_client.dart | 2 ++ lib/src/transactions/invite_server.dart | 2 ++ lib/src/transactions/non_invite_client.dart | 2 ++ lib/src/transactions/non_invite_server.dart | 2 ++ lib/src/transactions/transaction_base.dart | 1 + lib/src/transactions/transactions.dart | 1 + lib/src/transports/tcp_socket.dart | 1 + lib/src/transports/tcp_socket_impl.dart | 2 ++ lib/src/transports/web_socket.dart | 1 + lib/src/transports/websocket_dart_impl.dart | 2 ++ lib/src/transports/websocket_web_impl.dart | 2 ++ lib/src/ua.dart | 2 ++ lib/src/uri.dart | 1 + lib/src/utils.dart | 3 +++ test/all_test.dart | 1 + test/test2_websocket.dart | 1 + test/test_classes.dart | 2 ++ test/test_digest_authentication.dart | 2 ++ test/test_normalize_target.dart | 2 ++ test/test_parser.dart | 2 ++ test/test_sip_message_parser.dart | 2 ++ test/test_sip_ua.dart | 3 +++ test/test_uri_parse.dart | 2 ++ test/test_websocket.dart | 3 +++ 64 files changed, 104 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pushMaster.yaml b/.github/workflows/pushMaster.yaml index 4a80bff8..8e05950d 100644 --- a/.github/workflows/pushMaster.yaml +++ b/.github/workflows/pushMaster.yaml @@ -20,7 +20,7 @@ jobs: - name: Dart Format Check run: dart format lib/ test/ --set-exit-if-changed - name: Import Sorter Check - run: flutter pub run import_sorter:main --no-comments --exit-if-changed + run: dart run import_sorter:main - name: Dart Analyze Check run: flutter analyze - name: Dart Test Check diff --git a/lib/src/config.dart b/lib/src/config.dart index aa66a138..6209884a 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -1,3 +1,4 @@ +// Project imports: import '../sip_ua.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; diff --git a/lib/src/data.dart b/lib/src/data.dart index 916ce420..f8dd62fd 100644 --- a/lib/src/data.dart +++ b/lib/src/data.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:core'; +// Project imports: import 'constants.dart'; import 'uri.dart'; diff --git a/lib/src/dialog.dart b/lib/src/dialog.dart index 1d05cea8..c46c131c 100644 --- a/lib/src/dialog.dart +++ b/lib/src/dialog.dart @@ -1,3 +1,4 @@ +// Project imports: import 'constants.dart'; import 'dialog/request_sender.dart'; import 'event_manager/event_manager.dart'; diff --git a/lib/src/dialog/request_sender.dart b/lib/src/dialog/request_sender.dart index 414ae99b..5858afc6 100644 --- a/lib/src/dialog/request_sender.dart +++ b/lib/src/dialog/request_sender.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:async'; +// Project imports: import '../constants.dart'; import '../dialog.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/digest_authentication.dart b/lib/src/digest_authentication.dart index 4bcd5dd3..c17b3ba2 100644 --- a/lib/src/digest_authentication.dart +++ b/lib/src/digest_authentication.dart @@ -1,3 +1,4 @@ +// Project imports: import 'constants.dart'; import 'logger.dart'; import 'utils.dart' as utils; diff --git a/lib/src/event_manager/call_events.dart b/lib/src/event_manager/call_events.dart index feb13750..1d1dd49e 100644 --- a/lib/src/event_manager/call_events.dart +++ b/lib/src/event_manager/call_events.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; +// Project imports: import '../enums.dart'; import '../rtc_session.dart'; import '../sip_message.dart'; diff --git a/lib/src/event_manager/event_manager.dart b/lib/src/event_manager/event_manager.dart index aa84e7b0..22d16947 100644 --- a/lib/src/event_manager/event_manager.dart +++ b/lib/src/event_manager/event_manager.dart @@ -1,3 +1,4 @@ +// Project imports: import '../logger.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/internal_events.dart b/lib/src/event_manager/internal_events.dart index 9b5adad2..a4930dcd 100644 --- a/lib/src/event_manager/internal_events.dart +++ b/lib/src/event_manager/internal_events.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; +// Project imports: import '../enums.dart'; import '../rtc_session.dart' show RTCSession; import '../rtc_session/dtmf.dart'; diff --git a/lib/src/event_manager/message_events.dart b/lib/src/event_manager/message_events.dart index 445f61f9..4e12f036 100644 --- a/lib/src/event_manager/message_events.dart +++ b/lib/src/event_manager/message_events.dart @@ -1,3 +1,4 @@ +// Project imports: import '../enums.dart'; import '../message.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/notifier_events.dart b/lib/src/event_manager/notifier_events.dart index d1ef620d..2b11b029 100644 --- a/lib/src/event_manager/notifier_events.dart +++ b/lib/src/event_manager/notifier_events.dart @@ -1,3 +1,4 @@ +// Project imports: import '../sip_message.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/options_events.dart b/lib/src/event_manager/options_events.dart index 2bfe65a7..7345b686 100644 --- a/lib/src/event_manager/options_events.dart +++ b/lib/src/event_manager/options_events.dart @@ -1,3 +1,4 @@ +// Project imports: import '../enums.dart'; import '../options.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/refer_events.dart b/lib/src/event_manager/refer_events.dart index ca5f38f2..fb2565a6 100644 --- a/lib/src/event_manager/refer_events.dart +++ b/lib/src/event_manager/refer_events.dart @@ -1,3 +1,4 @@ +// Project imports: import 'events.dart'; class EventReferTrying extends EventType { diff --git a/lib/src/event_manager/register_events.dart b/lib/src/event_manager/register_events.dart index aeb931db..8f9afcee 100644 --- a/lib/src/event_manager/register_events.dart +++ b/lib/src/event_manager/register_events.dart @@ -1,3 +1,4 @@ +// Project imports: import 'events.dart'; class EventRegistrationExpiring extends EventType { diff --git a/lib/src/event_manager/subscriber_events.dart b/lib/src/event_manager/subscriber_events.dart index 099e7365..bd55d863 100644 --- a/lib/src/event_manager/subscriber_events.dart +++ b/lib/src/event_manager/subscriber_events.dart @@ -1,3 +1,4 @@ +// Project imports: import '../sip_message.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/transport_events.dart b/lib/src/event_manager/transport_events.dart index 83bc9f1b..edd15062 100644 --- a/lib/src/event_manager/transport_events.dart +++ b/lib/src/event_manager/transport_events.dart @@ -1,3 +1,4 @@ +// Project imports: import '../transports/socket_interface.dart'; import '../transports/web_socket.dart'; import 'events.dart'; diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart index 8c3e3151..b5dca5ae 100644 --- a/lib/src/exceptions.dart +++ b/lib/src/exceptions.dart @@ -1,3 +1,4 @@ +// Project imports: import 'utils.dart'; class ErrorImpl extends Error { diff --git a/lib/src/grammar.dart b/lib/src/grammar.dart index fb2feb8b..46f41951 100644 --- a/lib/src/grammar.dart +++ b/lib/src/grammar.dart @@ -1,3 +1,4 @@ +// Project imports: import 'grammar_parser.dart'; import 'parser_error.dart'; diff --git a/lib/src/grammar_parser.dart b/lib/src/grammar_parser.dart index 47eaebb6..39703504 100644 --- a/lib/src/grammar_parser.dart +++ b/lib/src/grammar_parser.dart @@ -1,9 +1,11 @@ // This code was generated by a tool. // Processing tool available at https://github.com/mezoni/peg +// Dart imports: import 'dart:convert'; import 'dart:core'; +// Project imports: import 'data.dart'; import 'name_addr_header.dart'; import 'uri.dart'; diff --git a/lib/src/logger.dart b/lib/src/logger.dart index 21169d23..46bb2120 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -1,6 +1,8 @@ +// Package imports: import 'package:intl/intl.dart'; import 'package:logger/logger.dart'; +// Project imports: import 'stack_trace_nj.dart'; Logger logger = Log(); diff --git a/lib/src/message.dart b/lib/src/message.dart index c8f6092d..01e73071 100644 --- a/lib/src/message.dart +++ b/lib/src/message.dart @@ -1,3 +1,4 @@ +// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'enums.dart'; diff --git a/lib/src/name_addr_header.dart b/lib/src/name_addr_header.dart index 8e934490..c02abd68 100644 --- a/lib/src/name_addr_header.dart +++ b/lib/src/name_addr_header.dart @@ -1,3 +1,4 @@ +// Project imports: import 'grammar.dart'; import 'uri.dart'; import 'utils.dart'; diff --git a/lib/src/options.dart b/lib/src/options.dart index e8e1ece6..fe5028a8 100644 --- a/lib/src/options.dart +++ b/lib/src/options.dart @@ -1,3 +1,4 @@ +// Project imports: import 'package:sip_ua/src/enums.dart'; import 'package:sip_ua/src/name_addr_header.dart'; import 'constants.dart' as DartSIP_C; diff --git a/lib/src/parser.dart b/lib/src/parser.dart index c169bcdb..57d6ac2a 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:convert' show utf8; +// Project imports: import 'grammar.dart'; import 'logger.dart'; import 'sip_message.dart'; diff --git a/lib/src/parser_error.dart b/lib/src/parser_error.dart index bc80928f..1c2cefac 100644 --- a/lib/src/parser_error.dart +++ b/lib/src/parser_error.dart @@ -1,3 +1,4 @@ +// Package imports: import 'package:text/text.dart'; class ParserErrorMessage { diff --git a/lib/src/registrator.dart b/lib/src/registrator.dart index 795b1bab..703edd5f 100644 --- a/lib/src/registrator.dart +++ b/lib/src/registrator.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:async'; +// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'event_manager/event_manager.dart'; diff --git a/lib/src/request_sender.dart b/lib/src/request_sender.dart index 42ec8cb8..a745f9ee 100644 --- a/lib/src/request_sender.dart +++ b/lib/src/request_sender.dart @@ -1,3 +1,4 @@ +// Project imports: import 'constants.dart'; import 'data.dart'; import 'digest_authentication.dart'; diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 2ccf6f98..d3e26f1e 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -1,8 +1,11 @@ +// Dart imports: import 'dart:async'; +// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; +// Project imports: import '../sip_ua.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; diff --git a/lib/src/rtc_session/dtmf.dart b/lib/src/rtc_session/dtmf.dart index b7c27c65..c75967e2 100644 --- a/lib/src/rtc_session/dtmf.dart +++ b/lib/src/rtc_session/dtmf.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; +// Project imports: import '../../sip_ua.dart'; import '../constants.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/rtc_session/info.dart b/lib/src/rtc_session/info.dart index 5399ba23..a6d97156 100644 --- a/lib/src/rtc_session/info.dart +++ b/lib/src/rtc_session/info.dart @@ -1,3 +1,4 @@ +// Project imports: import '../constants.dart'; import '../enums.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/rtc_session/refer_notifier.dart b/lib/src/rtc_session/refer_notifier.dart index 76f81cb3..9d14abd0 100644 --- a/lib/src/rtc_session/refer_notifier.dart +++ b/lib/src/rtc_session/refer_notifier.dart @@ -1,3 +1,4 @@ +// Project imports: import '../constants.dart' as DartSIP_C; import '../constants.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/rtc_session/refer_subscriber.dart b/lib/src/rtc_session/refer_subscriber.dart index 716db186..f2b5527e 100644 --- a/lib/src/rtc_session/refer_subscriber.dart +++ b/lib/src/rtc_session/refer_subscriber.dart @@ -1,3 +1,4 @@ +// Project imports: import '../constants.dart' as DartSIP_C; import '../constants.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/sanity_check.dart b/lib/src/sanity_check.dart index 01d1884a..1751ffd0 100644 --- a/lib/src/sanity_check.dart +++ b/lib/src/sanity_check.dart @@ -1,3 +1,4 @@ +// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'logger.dart'; diff --git a/lib/src/sip_message.dart b/lib/src/sip_message.dart index 1c6e6eff..5e226fc0 100644 --- a/lib/src/sip_message.dart +++ b/lib/src/sip_message.dart @@ -1,7 +1,10 @@ +// Dart imports: import 'dart:convert' show utf8; +// Package imports: import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; +// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'data.dart'; diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 35282cb4..f1b37633 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -1,9 +1,13 @@ +// Dart imports: import 'dart:async'; +// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:logger/logger.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; +// Project imports: +import 'package:sip_ua/src/uri.dart'; import 'config.dart'; import 'constants.dart' as DartSIP_C; import 'enums.dart'; diff --git a/lib/src/socket_transport.dart b/lib/src/socket_transport.dart index ea82046d..1f2b083e 100644 --- a/lib/src/socket_transport.dart +++ b/lib/src/socket_transport.dart @@ -1,6 +1,8 @@ +// Dart imports: import 'dart:async'; import 'dart:math'; +// Project imports: import './event_manager/events.dart'; import './transports/socket_interface.dart'; import 'exceptions.dart' as Exceptions; diff --git a/lib/src/stack_trace_nj.dart b/lib/src/stack_trace_nj.dart index 146470de..f5626b8d 100644 --- a/lib/src/stack_trace_nj.dart +++ b/lib/src/stack_trace_nj.dart @@ -1,7 +1,9 @@ +// Dart imports: import 'dart:core' as core show StackTrace; import 'dart:core'; import 'dart:io'; +// Package imports: import 'package:path/path.dart'; class StackTraceNJ implements core.StackTrace { diff --git a/lib/src/subscriber.dart b/lib/src/subscriber.dart index 45ad1d13..8eaabca2 100644 --- a/lib/src/subscriber.dart +++ b/lib/src/subscriber.dart @@ -1,7 +1,10 @@ +// Dart imports: import 'dart:async'; +// Package imports: import 'package:collection/collection.dart'; +// Project imports: import './exceptions.dart' as exceptions; import 'constants.dart'; import 'dialog.dart'; diff --git a/lib/src/timers.dart b/lib/src/timers.dart index f0f52432..67a18557 100644 --- a/lib/src/timers.dart +++ b/lib/src/timers.dart @@ -1,3 +1,4 @@ +// Dart imports: import 'dart:async'; class Timers { diff --git a/lib/src/transactions/ack_client.dart b/lib/src/transactions/ack_client.dart index c1eacc1f..3c9c0ed4 100644 --- a/lib/src/transactions/ack_client.dart +++ b/lib/src/transactions/ack_client.dart @@ -1,3 +1,4 @@ +// Project imports: import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; diff --git a/lib/src/transactions/invite_client.dart b/lib/src/transactions/invite_client.dart index 5e1290e1..ae876f77 100644 --- a/lib/src/transactions/invite_client.dart +++ b/lib/src/transactions/invite_client.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:async'; +// Project imports: import '../constants.dart'; import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; diff --git a/lib/src/transactions/invite_server.dart b/lib/src/transactions/invite_server.dart index f42246b2..55340455 100644 --- a/lib/src/transactions/invite_server.dart +++ b/lib/src/transactions/invite_server.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:async'; +// Project imports: import '../event_manager/internal_events.dart'; import '../logger.dart'; import '../sip_message.dart'; diff --git a/lib/src/transactions/non_invite_client.dart b/lib/src/transactions/non_invite_client.dart index ee725543..ac8da0d9 100644 --- a/lib/src/transactions/non_invite_client.dart +++ b/lib/src/transactions/non_invite_client.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:async'; +// Project imports: import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; diff --git a/lib/src/transactions/non_invite_server.dart b/lib/src/transactions/non_invite_server.dart index d801b8e3..6a83776f 100644 --- a/lib/src/transactions/non_invite_server.dart +++ b/lib/src/transactions/non_invite_server.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:async'; +// Project imports: import 'package:sip_ua/src/sip_message.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; diff --git a/lib/src/transactions/transaction_base.dart b/lib/src/transactions/transaction_base.dart index 38940087..9e3b6e42 100644 --- a/lib/src/transactions/transaction_base.dart +++ b/lib/src/transactions/transaction_base.dart @@ -1,3 +1,4 @@ +// Project imports: import '../event_manager/event_manager.dart'; import '../sip_message.dart'; import '../socket_transport.dart'; diff --git a/lib/src/transactions/transactions.dart b/lib/src/transactions/transactions.dart index 257f29c3..53888a5c 100644 --- a/lib/src/transactions/transactions.dart +++ b/lib/src/transactions/transactions.dart @@ -1,3 +1,4 @@ +// Project imports: import '../constants.dart'; import '../sip_message.dart'; import '../timers.dart'; diff --git a/lib/src/transports/tcp_socket.dart b/lib/src/transports/tcp_socket.dart index 379be330..6e475b79 100644 --- a/lib/src/transports/tcp_socket.dart +++ b/lib/src/transports/tcp_socket.dart @@ -1,3 +1,4 @@ +// Project imports: import '../../sip_ua.dart'; import '../logger.dart'; import 'socket_interface.dart'; diff --git a/lib/src/transports/tcp_socket_impl.dart b/lib/src/transports/tcp_socket_impl.dart index 68a4727d..fa91f0bd 100644 --- a/lib/src/transports/tcp_socket_impl.dart +++ b/lib/src/transports/tcp_socket_impl.dart @@ -1,8 +1,10 @@ +// Dart imports: import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; +// Project imports: import '../logger.dart'; import '../sip_ua_helper.dart'; diff --git a/lib/src/transports/web_socket.dart b/lib/src/transports/web_socket.dart index 77c7753e..80c21d97 100644 --- a/lib/src/transports/web_socket.dart +++ b/lib/src/transports/web_socket.dart @@ -1,3 +1,4 @@ +// Project imports: import '../../sip_ua.dart'; import '../grammar.dart'; import '../logger.dart'; diff --git a/lib/src/transports/websocket_dart_impl.dart b/lib/src/transports/websocket_dart_impl.dart index 1ec30b21..89ac9480 100644 --- a/lib/src/transports/websocket_dart_impl.dart +++ b/lib/src/transports/websocket_dart_impl.dart @@ -1,8 +1,10 @@ +// Dart imports: import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; +// Project imports: import 'package:sip_ua/src/sip_ua_helper.dart'; import '../logger.dart'; diff --git a/lib/src/transports/websocket_web_impl.dart b/lib/src/transports/websocket_web_impl.dart index f72ebe11..522a1375 100644 --- a/lib/src/transports/websocket_web_impl.dart +++ b/lib/src/transports/websocket_web_impl.dart @@ -1,6 +1,8 @@ +// Dart imports: import 'dart:html'; import 'dart:js_util' as JSUtils; +// Project imports: import 'package:sip_ua/src/sip_ua_helper.dart'; import '../logger.dart'; diff --git a/lib/src/ua.dart b/lib/src/ua.dart index a93ad411..aa0e5a31 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:async'; +// Project imports: import 'config.dart' as config; import 'config.dart'; import 'constants.dart' as DartSIP_C; diff --git a/lib/src/uri.dart b/lib/src/uri.dart index 66d30b93..b7b2bc12 100644 --- a/lib/src/uri.dart +++ b/lib/src/uri.dart @@ -1,3 +1,4 @@ +// Project imports: import 'constants.dart' as DartSIP_C; import 'grammar.dart'; import 'utils.dart' as utils; diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 3365b4f6..737bb3ac 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,11 +1,14 @@ +// Dart imports: import 'dart:convert'; import 'dart:core'; import 'dart:math' as DartMath; +// Package imports: import 'package:crypto/crypto.dart'; import 'package:random_string/random_string.dart'; import 'package:uuid/uuid.dart'; +// Project imports: import 'constants.dart' as DartSIP_C; import 'grammar.dart'; import 'uri.dart'; diff --git a/test/all_test.dart b/test/all_test.dart index 7667228e..26a611c0 100644 --- a/test/all_test.dart +++ b/test/all_test.dart @@ -1,3 +1,4 @@ +// Project imports: import 'test_classes.dart' as Classes; import 'test_digest_authentication.dart' as DigestAuthentication; import 'test_normalize_target.dart' as NormalizeTarget; diff --git a/test/test2_websocket.dart b/test/test2_websocket.dart index 8b4b25f0..722b0b4d 100644 --- a/test/test2_websocket.dart +++ b/test/test2_websocket.dart @@ -1,3 +1,4 @@ +// Dart imports: import 'dart:async'; import 'dart:io'; diff --git a/test/test_classes.dart b/test/test_classes.dart index 5ef70df4..3650c7bd 100644 --- a/test/test_classes.dart +++ b/test/test_classes.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:test/test.dart'; +// Project imports: import 'package:sip_ua/src/name_addr_header.dart'; import 'package:sip_ua/src/uri.dart'; diff --git a/test/test_digest_authentication.dart b/test/test_digest_authentication.dart index a7738209..d1ac9b94 100644 --- a/test/test_digest_authentication.dart +++ b/test/test_digest_authentication.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:test/test.dart'; +// Project imports: import 'package:sip_ua/src/constants.dart'; import 'package:sip_ua/src/digest_authentication.dart'; diff --git a/test/test_normalize_target.dart b/test/test_normalize_target.dart index 37f7f0ed..b81cd903 100644 --- a/test/test_normalize_target.dart +++ b/test/test_normalize_target.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:test/test.dart'; +// Project imports: import 'package:sip_ua/src/uri.dart'; import 'package:sip_ua/src/utils.dart' as Utils; diff --git a/test/test_parser.dart b/test/test_parser.dart index 5409d874..060ea248 100644 --- a/test/test_parser.dart +++ b/test/test_parser.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:test/test.dart'; +// Project imports: import 'package:sip_ua/src/data.dart'; import 'package:sip_ua/src/grammar.dart'; import 'package:sip_ua/src/name_addr_header.dart'; diff --git a/test/test_sip_message_parser.dart b/test/test_sip_message_parser.dart index 4b4018cd..3369247d 100644 --- a/test/test_sip_message_parser.dart +++ b/test/test_sip_message_parser.dart @@ -1,5 +1,7 @@ +// Package imports: import 'package:test/test.dart'; +// Project imports: import 'package:sip_ua/src/parser.dart'; import 'data/sip_message.dart'; diff --git a/test/test_sip_ua.dart b/test/test_sip_ua.dart index 1b4c8118..7f114dbc 100644 --- a/test/test_sip_ua.dart +++ b/test/test_sip_ua.dart @@ -1,7 +1,10 @@ +// Dart imports: import 'dart:async'; +// Package imports: import 'package:test/test.dart'; +// Project imports: import 'package:sip_ua/src/config.dart' as config; import 'package:sip_ua/src/event_manager/event_manager.dart'; import 'package:sip_ua/src/transports/socket_interface.dart'; diff --git a/test/test_uri_parse.dart b/test/test_uri_parse.dart index 6b990818..2d536502 100644 --- a/test/test_uri_parse.dart +++ b/test/test_uri_parse.dart @@ -1,5 +1,7 @@ +// Dart imports: import 'dart:io'; +// Package imports: import 'package:test/test.dart'; List testFunctions = [ diff --git a/test/test_websocket.dart b/test/test_websocket.dart index cad2dda5..aad4b275 100644 --- a/test/test_websocket.dart +++ b/test/test_websocket.dart @@ -1,8 +1,11 @@ +// Dart imports: import 'dart:async'; import 'dart:io'; +// Package imports: import 'package:test/test.dart'; +// Project imports: import 'package:sip_ua/src/event_manager/events.dart'; import 'package:sip_ua/src/socket_transport.dart'; import 'package:sip_ua/src/transports/socket_interface.dart'; From b895a53af40ccb6e7b9b96371d48827260eab503 Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Wed, 29 Jan 2025 16:52:00 +0000 Subject: [PATCH 19/35] Added local video stream to localStreaMedia so you can mute your video on an upgrade, and now can await unregister --- lib/src/registrator.dart | 10 ++++++++-- lib/src/rtc_session.dart | 15 ++++++++++++++- lib/src/sip_ua_helper.dart | 5 +++-- lib/src/ua.dart | 4 ++-- pubspec.yaml | 3 +-- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/src/registrator.dart b/lib/src/registrator.dart index 703edd5f..f172d87b 100644 --- a/lib/src/registrator.dart +++ b/lib/src/registrator.dart @@ -276,11 +276,11 @@ class Registrator { request_sender.send(); } - void unregister(bool unregister_all) { + Future unregister(bool unregister_all) async { if (_registered == false) { logger.d('already unregistered'); - return; + return true; } _registered = false; @@ -314,11 +314,14 @@ class Registrator { extraHeaders); EventManager handlers = EventManager(); + Completer completer = Completer(); handlers.on(EventOnRequestTimeout(), (EventOnRequestTimeout value) { _unregistered(null, DartSIP_C.CausesType.REQUEST_TIMEOUT); + completer.complete(false); }); handlers.on(EventOnTransportError(), (EventOnTransportError value) { _unregistered(null, DartSIP_C.CausesType.CONNECTION_ERROR); + completer.complete(false); }); handlers.on(EventOnAuthenticated(), (EventOnAuthenticated response) { // Increase the CSeq on authentication. @@ -329,17 +332,20 @@ class Registrator { String status_code = event.response!.status_code.toString(); if (utils.test2XX(status_code)) { _unregistered(event.response); + completer.complete(true); } else if (utils.test1XX(status_code)) { // Ignore provisional responses. } else { String cause = utils.sipErrorCause(event.response!.status_code); _unregistered(event.response, cause); + completer.complete(true); } }); RequestSender request_sender = RequestSender(_ua, request, handlers); request_sender.send(); + return completer.future; } void close() { diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index d3e26f1e..a80a59de 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -2091,10 +2091,13 @@ class RTCSession extends EventManager implements Owner { for (MediaStreamTrack track in localStream.getTracks()) { if (track.kind == 'video') { _connection!.addTrack(track, localStream); + _localMediaStream?.addTrack(track); } } emit(EventStream( - session: this, originator: Originator.local, stream: localStream)); + session: this, + originator: Originator.local, + stream: _localMediaStream)); } else { logger.w( 'Remote wants to upgrade to video but no camera available to send'); @@ -3249,17 +3252,27 @@ class RTCSession extends EventManager implements Owner { void _toggleMuteAudio(bool mute) { if (_localMediaStream != null) { + if (_localMediaStream!.getAudioTracks().isEmpty) { + logger.w('Went to mute video but local stream has no video tracks'); + } for (MediaStreamTrack track in _localMediaStream!.getAudioTracks()) { track.enabled = !mute; } + } else { + logger.w('Went to mute audio but local stream is null'); } } void _toggleMuteVideo(bool mute) { if (_localMediaStream != null) { + if (_localMediaStream!.getVideoTracks().isEmpty) { + logger.w('Went to mute video but local stream has no video tracks'); + } for (MediaStreamTrack track in _localMediaStream!.getVideoTracks()) { track.enabled = !mute; } + } else { + logger.w('Went to mute video but local stream is null'); } } diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index f1b37633..6373ee9e 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -86,12 +86,13 @@ class SIPUAHelper extends EventManager { _ua!.register(); } - void unregister([bool all = true]) { + Future unregister([bool all = true]) async { if (_ua != null) { assert(registered, 'ERROR: you must call register first.'); - _ua!.unregister(all: all); + return _ua!.unregister(all: all); } else { logger.e('ERROR: unregister called, you must call start first.'); + return false; } } diff --git a/lib/src/ua.dart b/lib/src/ua.dart index aa0e5a31..c97edde5 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -185,11 +185,11 @@ class UA extends EventManager { /** * Unregister. */ - void unregister({bool all = false}) { + Future unregister({bool all = false}) { logger.d('unregister()'); _dynConfiguration!.register = false; - _registrator.unregister(all); + return _registrator.unregister(all); } /** diff --git a/pubspec.yaml b/pubspec.yaml index ae977034..b402264a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: collection: ^1.18.0 crypto: ^3.0.3 - flutter_webrtc: ^0.12.4 + flutter_webrtc: ^0.12.6 intl: ^0.20.1 logger: ^2.0.2+1 path: ^1.6.4 @@ -18,7 +18,6 @@ dependencies: text: ^0.2.0 uuid: ^4.2.1 - dev_dependencies: import_sorter: ^4.6.0 lints: ^4.0.0 From 903169a04d1946cd1e5108545ff7b4e0c8b88593 Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Mon, 3 Feb 2025 10:04:28 +0000 Subject: [PATCH 20/35] Removed unnecessary video/audio toggles --- example/ios/Podfile.lock | 49 ++++++++++++++++++++++++++++++++++++++++ lib/src/rtc_session.dart | 17 +++++--------- 2 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 example/ios/Podfile.lock diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 00000000..9cc4716d --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,49 @@ +PODS: + - Flutter (1.0.0) + - flutter_webrtc (0.12.2): + - Flutter + - WebRTC-SDK (= 125.6422.06) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - permission_handler_apple (9.3.0): + - Flutter + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - WebRTC-SDK (125.6422.06) + +DEPENDENCIES: + - Flutter (from `Flutter`) + - flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + +SPEC REPOS: + trunk: + - WebRTC-SDK + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + flutter_webrtc: + :path: ".symlinks/plugins/flutter_webrtc/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + +SPEC CHECKSUMS: + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db + +PODFILE CHECKSUM: a5dd15803c05a42a9ae1068254e24631f03bf853 + +COCOAPODS: 1.15.2 diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index a80a59de..1c55cc5d 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -997,13 +997,10 @@ class RTCSession extends EventManager implements Owner { if (!_audioMuted && audio) { _audioMuted = true; changed = true; - _toggleMuteAudio(true); } - if (!_videoMuted && video) { _videoMuted = true; changed = true; - _toggleMuteVideo(true); } if (changed) { @@ -1177,7 +1174,7 @@ class RTCSession extends EventManager implements Owner { options['mediaConstraints']?['mandatory']?['video'] != null) && rtcOfferConstraints?['offerToReceiveVideo'] == null; } catch (e) { - print('Failed to determine upgrade to video: $e'); + logger.w('Failed to determine upgrade to video: $e'); } if (!_isReadyToReOffer()) { @@ -1197,7 +1194,6 @@ class RTCSession extends EventManager implements Owner { 'reason_phrase': 'Media Renegotiation Failed' }); }); - _setLocalMediaStatus(); if (options['useUpdate'] != null) { @@ -1644,6 +1640,7 @@ class RTCSession extends EventManager implements Owner { }); } else if (state == RTCIceConnectionState.RTCIceConnectionStateDisconnected) { + if (_state == RtcSessionState.terminated) return; _iceRestart(); } }; @@ -1921,7 +1918,8 @@ class RTCSession extends EventManager implements Owner { if (status_code < 300 || status_code >= 700) { throw Exceptions.TypeError('Invalid status_code: $status_code'); } - print('Rejecting with status code: $status_code, reason: $reason_phrase'); + logger.i( + 'Rejecting with status code: $status_code, reason: $reason_phrase'); request.reply(status_code, reason_phrase, extraHeaders); return true; } @@ -3136,20 +3134,16 @@ class RTCSession extends EventManager implements Owner { void _setLocalMediaStatus() { bool enableAudio = true, enableVideo = true; - if (_localHold || _remoteHold) { enableAudio = false; enableVideo = false; } - if (_audioMuted) { enableAudio = false; } - if (_videoMuted) { enableVideo = false; } - _toggleMuteAudio(!enableAudio); _toggleMuteVideo(!enableVideo); } @@ -3266,7 +3260,8 @@ class RTCSession extends EventManager implements Owner { void _toggleMuteVideo(bool mute) { if (_localMediaStream != null) { if (_localMediaStream!.getVideoTracks().isEmpty) { - logger.w('Went to mute video but local stream has no video tracks'); + logger.w( + 'Went to toggle mute video but local stream has no video tracks'); } for (MediaStreamTrack track in _localMediaStream!.getVideoTracks()) { track.enabled = !mute; From 4e8e03fc6781114dd55ea18918ab7d5c62be6376 Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Tue, 4 Feb 2025 13:42:02 +0000 Subject: [PATCH 21/35] Added re-register mechanism in example app after call ends or fails for more quality of service --- README.md | 43 +++++++++++++- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 4 +- example/lib/main.dart | 18 ++++-- example/lib/src/dialpad.dart | 31 +++++++++- example/lib/src/register.dart | 59 +++++++++---------- example/lib/src/user_state/sip_user.dart | 51 ++++++++++++++++ .../lib/src/user_state/sip_user_cubit.dart | 31 ++++++++++ example/pubspec.yaml | 5 +- lib/src/config.dart | 15 ++--- lib/src/sip_ua_helper.dart | 8 ++- pubspec.yaml | 1 + 12 files changed, 209 insertions(+), 59 deletions(-) create mode 100644 example/lib/src/user_state/sip_user.dart create mode 100644 example/lib/src/user_state/sip_user_cubit.dart diff --git a/README.md b/README.md index f934e45e..07b867a4 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ A dart-lang version of the SIP UA stack, ported from [JsSIP](https://github.com/ ## Overview - Use pure [dart-lang](https://dart.dev) -- SIP over WebSocket (use real SIP in your flutter mobile, [desktop](https://flutter.dev/desktop), [web](https://flutter.dev/web) apps) +- SIP over WebSocket && TCP (use real SIP in your flutter mobile, [desktop](https://flutter.dev/desktop), [web](https://flutter.dev/web) apps) - Audio/video calls ([flutter-webrtc](https://github.com/cloudwebrtc/flutter-webrtc)) and instant messaging -- Support with standard SIP servers such as OpenSIPS, Kamailio, Asterisk and FreeSWITCH. +- Support with standard SIP servers such as OpenSIPS, Kamailio, Asterisk, 3CX and FreeSWITCH. - Support RFC2833 or INFO to send DTMF. ## Currently supported platforms @@ -50,9 +50,47 @@ Register with SIP server: - [Asterisk](https://github.com/flutter-webrtc/dockers/tree/main/asterisk) - FreeSWITCH - OpenSIPS +- 3CX - Kamailio - or add your server example. +## FAQ's OR ISSUES +
+ +expand + +## Server not configured for DTLS/SRTP + +WEBRTC_SET_REMOTE_DESCRIPTION_ERROR: Failed to set remote offer sdp: Called with SDP without DTLS fingerprint. + +Your server is not sending a DTLS fingerprint inside the SDP when inviting the sip_ua client to start a call. + +WebRTC uses encryption by Default, all WebRTC communications (audio, video, and data) are encrypted using DTLS and SRTP, ensuring secure communication. Your PBX must be configured to use DTLS/SRTP when calling sip_ua. + + +## Why isn't there a UDP connection option? + +This package uses a WS or TCP connection for the signalling processs to initiate or terminate a session (sip messages). +Once the session is connected WebRTC transmits the actual media (audio/video) over UDP. + +If anyone actually still wants to use UDP for the signalling process, feel free to submit a PR with the large amount of work needed to set it up, packet order checking, error checking, reliability timeouts, flow control, security etc etc. + +## SIP/2.0 488 Not acceptable here + +The codecs on your PBX server don't match the codecs used by WebRTC + +- **opus** (payload type 111, 48kHz, 2 channels) +- **red** (payload type 63, 48kHz, 2 channels) +- **G722** (payload type 9, 8kHz, 1 channel) +- **ILBC** (payload type 102, 8kHz, 1 channel) +- **PCMU** (payload type 0, 8kHz, 1 channel) +- **PCMA** (payload type 8, 8kHz, 1 channel) +- **CN** (payload type 13, 8kHz, 1 channel) +- **telephone-event** (payload type 110, 48kHz, 1 channel for wideband, 8000Hz, 1 channel for narrowband) + +
+ + ## NOTE Thanks to the original authors of [JsSIP](https://github.com/versatica/JsSIP) for providing the JS version, which makes it possible to port the [dart-lang](https://dart.dev). - [José Luis Millán](https://github.com/jmillan) @@ -69,6 +107,7 @@ The project is inseparable from the contributors of the community. - [Robert Sutton](https://github.com/rlsutton1) - Contributor - [Gavin Henry](https://github.com/ghenry) - Contributor - [Perondas](https://github.com/Perondas) - Contributor +- [Mikael Wills](https://github.com/mikaelwills) - Contributor ## License dart-sip-ua is released under the [MIT license](https://github.com/cloudwebrtc/dart-sip-ua/blob/master/LICENSE). diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index e1ca574e..8bc9958a 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 536165d3..b9e43bd3 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -18,8 +18,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "com.android.application" version "8.1.0" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false } include ":app" diff --git a/example/lib/main.dart b/example/lib/main.dart index 5ed459a3..c9f91f6f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,5 @@ import 'package:dart_sip_ua_example/src/theme_provider.dart'; +import 'package:dart_sip_ua_example/src/user_state/sip_user_cubit.dart'; import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride; import 'package:flutter/material.dart'; @@ -60,11 +61,18 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: Provider.of(context).currentTheme, - initialRoute: '/', - onGenerateRoute: _onGenerateRoute, + return MultiProvider( + providers: [ + Provider.value(value: _helper), + Provider( + create: (context) => SipUserCubit(sipHelper: _helper)), + ], + child: MaterialApp( + title: 'Flutter Demo', + theme: Provider.of(context).currentTheme, + initialRoute: '/', + onGenerateRoute: _onGenerateRoute, + ), ); } } diff --git a/example/lib/src/dialpad.dart b/example/lib/src/dialpad.dart index 0b5b6e0a..fdd357fd 100644 --- a/example/lib/src/dialpad.dart +++ b/example/lib/src/dialpad.dart @@ -1,7 +1,9 @@ import 'package:dart_sip_ua_example/src/theme_provider.dart'; +import 'package:dart_sip_ua_example/src/user_state/sip_user_cubit.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:logger/logger.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -24,6 +26,9 @@ class _MyDialPadWidget extends State SIPUAHelper? get helper => widget._helper; TextEditingController? _textController; late SharedPreferences _preferences; + late SipUserCubit currentUserCubit; + + final Logger _logger = Logger(); String? receivedMsg; @@ -241,6 +246,8 @@ class _MyDialPadWidget extends State Color? textColor = Theme.of(context).textTheme.bodyMedium?.color; Color? iconColor = Theme.of(context).iconTheme.color; bool isDarkTheme = Theme.of(context).brightness == Brightness.dark; + currentUserCubit = context.watch(); + return Scaffold( appBar: AppBar( title: Text("Dart SIP UA Demo"), @@ -345,7 +352,9 @@ class _MyDialPadWidget extends State @override void registrationStateChanged(RegistrationState state) { - setState(() {}); + setState(() { + _logger.i("Registration state: ${state.state?.name}"); + }); } @override @@ -353,11 +362,27 @@ class _MyDialPadWidget extends State @override void callStateChanged(Call call, CallState callState) { - if (callState.state == CallStateEnum.CALL_INITIATION) { - Navigator.pushNamed(context, '/callscreen', arguments: call); + switch (callState.state) { + case CallStateEnum.CALL_INITIATION: + Navigator.pushNamed(context, '/callscreen', arguments: call); + break; + case CallStateEnum.FAILED: + reRegisterWithCurrentUser(); + break; + case CallStateEnum.ENDED: + reRegisterWithCurrentUser(); + break; + default: } } + void reRegisterWithCurrentUser() async { + if (currentUserCubit.state == null) return; + if (helper!.registered) await helper!.unregister(); + _logger.i("Re-registering"); + currentUserCubit.register(currentUserCubit.state!); + } + @override void onNewMessage(SIPMessageRequest msg) { //Save the incoming message to DB diff --git a/example/lib/src/register.dart b/example/lib/src/register.dart index d6071440..98b02fb4 100644 --- a/example/lib/src/register.dart +++ b/example/lib/src/register.dart @@ -1,4 +1,7 @@ +import 'package:dart_sip_ua_example/src/user_state/sip_user.dart'; +import 'package:dart_sip_ua_example/src/user_state/sip_user_cubit.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sip_ua/sip_ua.dart'; import 'package:flutter/foundation.dart' show kIsWeb; @@ -32,6 +35,8 @@ class _MyRegisterWidget extends State SIPUAHelper? get helper => widget._helper; + late SipUserCubit currentUser; + @override void initState() { super.initState(); @@ -98,47 +103,37 @@ class _MyRegisterWidget extends State barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( - title: Text('$alertFieldName is empty'), - content: Text('Please enter $alertFieldName!'), - actions: [ - TextButton( - child: Text('Ok'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); + title: Text('$alertFieldName is empty'), + content: Text('Please enter $alertFieldName!'), + actions: [ + TextButton( + child: Text('Ok'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ]); }, ); } - void _handleSave(BuildContext context) { + void _register(BuildContext context) { if (_wsUriController.text == '') { _alert(context, "WebSocket URL"); } else if (_sipUriController.text == '') { _alert(context, "SIP URI"); } - UaSettings settings = UaSettings(); - - settings.port = _portController.text; - settings.webSocketSettings.extraHeaders = _wsExtraHeaders; - settings.webSocketSettings.allowBadCertificate = true; - //settings.webSocketSettings.userAgent = 'Dart/2.8 (dart:io) for OpenSIPS.'; - settings.tcpSocketSettings.allowBadCertificate = true; - settings.transportType = _selectedTransport; - settings.uri = _sipUriController.text; - settings.webSocketUrl = _wsUriController.text; - settings.host = _sipUriController.text.split('@')[1]; - settings.authorizationUser = _authorizationUserController.text; - settings.password = _passwordController.text; - settings.displayName = _displayNameController.text; - settings.userAgent = 'Dart SIP Client v1.0.0'; - settings.dtmfMode = DtmfMode.RFC2833; - settings.contact_uri = 'sip:${_sipUriController.text}'; + _saveSettings(); - helper!.start(settings); + currentUser.register(SipUser( + selectedTransport: _selectedTransport, + wsExtraHeaders: _wsExtraHeaders, + sipUri: _sipUriController.text, + port: _portController.text, + displayName: _displayNameController.text, + password: _passwordController.text, + authUser: _authorizationUserController.text)); } @override @@ -146,6 +141,8 @@ class _MyRegisterWidget extends State Color? textColor = Theme.of(context).textTheme.bodyMedium?.color; Color? textFieldFill = Theme.of(context).buttonTheme.colorScheme?.surfaceContainerLowest; + currentUser = context.watch(); + OutlineInputBorder border = OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular(5), @@ -168,7 +165,7 @@ class _MyRegisterWidget extends State height: 40, child: ElevatedButton( child: Text('Register'), - onPressed: () => _handleSave(context), + onPressed: () => _register(context), ), ), ), diff --git a/example/lib/src/user_state/sip_user.dart b/example/lib/src/user_state/sip_user.dart new file mode 100644 index 00000000..5aed56fc --- /dev/null +++ b/example/lib/src/user_state/sip_user.dart @@ -0,0 +1,51 @@ +import 'package:sip_ua/sip_ua.dart'; + +class SipUser { + final String port; + final String displayName; + final String? wsUrl; + final String? sipUri; + final String password; + final String authUser; + final TransportType selectedTransport; + final Map? wsExtraHeaders; + + SipUser({ + required this.port, + required this.displayName, + required this.password, + required this.authUser, + required this.selectedTransport, + this.wsExtraHeaders, + this.wsUrl, + this.sipUri, + }); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is SipUser && + other.port == port && + other.displayName == displayName && + other.wsUrl == wsUrl && + other.sipUri == sipUri && + other.selectedTransport == selectedTransport && + other.wsExtraHeaders == wsExtraHeaders && + other.password == password && + other.authUser == authUser; + } + + @override + int get hashCode { + return Object.hashAll([ + port, + displayName, + wsUrl, + sipUri, + password, + wsExtraHeaders, + selectedTransport, + authUser, + ]); + } +} diff --git a/example/lib/src/user_state/sip_user_cubit.dart b/example/lib/src/user_state/sip_user_cubit.dart new file mode 100644 index 00000000..eddc61db --- /dev/null +++ b/example/lib/src/user_state/sip_user_cubit.dart @@ -0,0 +1,31 @@ +import 'package:bloc/bloc.dart'; +import 'package:dart_sip_ua_example/src/user_state/sip_user.dart'; +import 'package:sip_ua/sip_ua.dart'; + +class SipUserCubit extends Cubit { + final SIPUAHelper sipHelper; + SipUserCubit({required this.sipHelper}) : super(null); + + + void register(SipUser user) { + UaSettings settings = UaSettings(); + settings.port = user.port; + settings.webSocketSettings.extraHeaders = user.wsExtraHeaders ?? {}; + settings.webSocketSettings.allowBadCertificate = true; + //settings.webSocketSettings.userAgent = 'Dart/2.8 (dart:io) for OpenSIPS.'; + settings.tcpSocketSettings.allowBadCertificate = true; + settings.transportType = user.selectedTransport; + settings.uri = user.sipUri; + settings.webSocketUrl = user.wsUrl; + settings.host = user.sipUri?.split('@')[1]; + settings.authorizationUser = user.authUser; + settings.password = user.password; + settings.displayName = user.displayName; + settings.userAgent = 'Dart SIP Client v1.0.0'; + settings.dtmfMode = DtmfMode.RFC2833; + settings.contact_uri = 'sip:${user.sipUri}'; + + emit(user); + sipHelper.start(settings); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 12a2a9a4..07a277d8 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -19,15 +19,16 @@ environment: flutter: ">=1.10.0" dependencies: + bloc: ^9.0.0 flutter: sdk: flutter sip_ua: path: ../ shared_preferences: ^2.2.0 permission_handler: ^11.1.0 - flutter_webrtc: ^0.12.4 + flutter_webrtc: ^0.12.6 provider: 6.1.2 - logger: ^2.4.0 + logger: ^2.5.0 dev_dependencies: flutter_test: diff --git a/lib/src/config.dart b/lib/src/config.dart index 6209884a..a98db578 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -96,21 +96,14 @@ class Checks { dst!.sockets = copy; }, 'uri': (Settings src, Settings? dst) { - dynamic uri = src.uri; if (src.uri == null && dst!.uri == null) { throw Exceptions.ConfigurationError('uri', null); } - if (!uri.contains(RegExp(r'^sip:', caseSensitive: false))) { - uri = '${DartSIP_C.SIP}:$uri'; - } - dynamic parsed = URI.parse(uri); - if (parsed == null) { - throw Exceptions.ConfigurationError('uri', parsed); - } else if (parsed.user == null) { - throw Exceptions.ConfigurationError('uri', parsed); - } else { - dst!.uri = parsed; + URI uri = src.uri!; + if (!uri.toString().contains(RegExp(r'^sip:', caseSensitive: false))) { + uri.scheme = DartSIP_C.SIP; } + dst!.uri = uri; }, 'transport_type': (Settings src, Settings? dst) { dynamic transportType = src.transportType; diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 6373ee9e..bfccadfc 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -28,6 +28,7 @@ import 'transports/socket_interface.dart'; import 'transports/tcp_socket.dart'; import 'transports/web_socket.dart'; import 'ua.dart'; +import 'utils.dart' as Utils; class SIPUAHelper extends EventManager { SIPUAHelper({Logger? customLogger}) { @@ -166,7 +167,8 @@ class SIPUAHelper extends EventManager { } _settings.transportType = uaSettings.transportType!; - _settings.uri = uaSettings.uri != null ? URI.parse(uaSettings.uri!) : null; + _settings.uri = + uaSettings.uri != null ? Utils.normalizeTarget(uaSettings.uri!) : null; _settings.sip_message_delay = uaSettings.sip_message_delay; _settings.realm = uaSettings.realm; _settings.password = uaSettings.password; @@ -187,7 +189,7 @@ class SIPUAHelper extends EventManager { _settings.instance_id = uaSettings.instanceId; _settings.registrar_server = uaSettings.registrarServer; _settings.contact_uri = uaSettings.contact_uri != null - ? URI.parse(uaSettings.contact_uri!) + ? Utils.normalizeTarget(uaSettings.contact_uri!) : null; _settings.connection_recovery_max_interval = uaSettings.connectionRecoveryMaxInterval; @@ -589,6 +591,8 @@ class Call { } else { logger.d("peerConnection is null, can't stop tracks."); } + + if (state == CallStateEnum.ENDED) return; _session.terminate(options); } diff --git a/pubspec.yaml b/pubspec.yaml index b402264a..0bd986a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,6 +7,7 @@ environment: flutter: ">=2.0.0" dependencies: + bloc: ^9.0.0 collection: ^1.18.0 crypto: ^3.0.3 flutter_webrtc: ^0.12.6 From ac4f9f5cedf6d5a917a7c3e3dda58f95fe13c50f Mon Sep 17 00:00:00 2001 From: CloudWebRTC Date: Mon, 10 Feb 2025 23:58:48 +0800 Subject: [PATCH 22/35] release: 1.0.1. (#508) * release: 1.0.1. * ci. --- .github/workflows/dart.yml | 4 ++-- .github/workflows/publish.yaml | 4 ++-- CHANGELOG.md | 8 ++++++++ pubspec.yaml | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index c0d76fa5..054053b0 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -3,9 +3,9 @@ name: Build on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: test: diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 5c9d8574..c19c1aee 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -23,8 +23,8 @@ jobs: run: flutter analyze - name: Dart Test Check run: flutter test - #- name: Check Publish Warnings - # run: dart pub publish --dry-run + - name: Check Publish Warnings + run: dart pub publish --dry-run - name: Publish uses: k-paxian/dart-package-publisher@v1.5.1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e5fe8c4..0719b820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog -------------------------------------------- +[1.0.1] -2024.12.19 + +* make sure the session terminates by @victortive in https://github.com/flutter-webrtc/dart-sip-ua/pull/485 +* Hold and video upgrade fixed by @mikaelwills in https://github.com/flutter-webrtc/dart-sip-ua/pull/503 +* sip_ua_helper: add sendOptions by @eschmidbauer in https://github.com/flutter-webrtc/dart-sip-ua/pull/476 +* Update callscreen.dart by @HVaidehi in https://github.com/flutter-webrtc/dart-sip-ua/pull/427 + + [1.0.0] - 2024.08.24 * allow to change UA uri in runtime (#425) diff --git a/pubspec.yaml b/pubspec.yaml index 0bd986a2..f9f13b92 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sip_ua -version: 1.0.0 +version: 1.0.1 description: A SIP UA stack for Flutter/Dart, based on flutter-webrtc, support iOS/Android/Destkop/Web. homepage: https://github.com/cloudwebrtc/dart-sip-ua environment: From 9117018c354144e5e41e06f9105f528bc0eaeace Mon Sep 17 00:00:00 2001 From: aNOOBis <69043738+aNOOBisTheGod@users.noreply.github.com> Date: Mon, 17 Feb 2025 12:15:43 +0300 Subject: [PATCH 23/35] sessions keys toList added (#514) --- lib/src/ua.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/ua.dart b/lib/src/ua.dart index c97edde5..07c7a7e6 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -319,7 +319,7 @@ class UA extends EventManager { int num_sessions = _sessions.length; // Run _terminate_ on every Session. - _sessions.forEach((String? key, _) { + _sessions.keys.toList().forEach((String? key, _) { if (_sessions.containsKey(key)) { logger.d('closing session $key'); try { From d484932c445343243a72d3d5db2dd83201a8e9ad Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Mon, 17 Feb 2025 10:42:28 +0000 Subject: [PATCH 24/35] Build fixes --- .github/workflows/dart.yml | 2 +- .github/workflows/pushMaster.yaml | 4 ++-- lib/src/ua.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 054053b0..0e9557e2 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: java-version: '12.x' - uses: subosito/flutter-action@v1 with: - channel: 'stable' + flutter-version: '3.27.1' - name: Install project dependencies run: flutter pub get - name: Dart Format Check diff --git a/.github/workflows/pushMaster.yaml b/.github/workflows/pushMaster.yaml index 8e05950d..c4a299d8 100644 --- a/.github/workflows/pushMaster.yaml +++ b/.github/workflows/pushMaster.yaml @@ -14,13 +14,13 @@ jobs: - name: Install Flutter uses: subosito/flutter-action@v2 with: - channel: "stable" + flutter-version: "3.27.1" - name: Install project dependencies run: flutter pub get - name: Dart Format Check run: dart format lib/ test/ --set-exit-if-changed - name: Import Sorter Check - run: dart run import_sorter:main + run: dart run import_sorter:main --exit-if-changed - name: Dart Analyze Check run: flutter analyze - name: Dart Test Check diff --git a/lib/src/ua.dart b/lib/src/ua.dart index 07c7a7e6..359f4ecb 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -319,7 +319,7 @@ class UA extends EventManager { int num_sessions = _sessions.length; // Run _terminate_ on every Session. - _sessions.keys.toList().forEach((String? key, _) { + _sessions.keys.toList().forEach((String? key) { if (_sessions.containsKey(key)) { logger.d('closing session $key'); try { From 42217df0361a3bb896fbd134b7baae3e326c78ea Mon Sep 17 00:00:00 2001 From: CloudWebRTC Date: Tue, 1 Apr 2025 20:41:49 +0800 Subject: [PATCH 25/35] fix: bug for rfc2833. (#534) --- lib/src/rtc_session.dart | 20 ++++++++++++++------ lib/src/rtc_session/dtmf.dart | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 1c55cc5d..615b935a 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -2,6 +2,7 @@ import 'dart:async'; // Package imports: +import 'package:collection/collection.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; @@ -95,6 +96,9 @@ class RTCSession extends EventManager implements Owner { // The RTCPeerConnection instance (public attribute). RTCPeerConnection? _connection; + // RTPSender List + final List _senders = []; + // Incoming/Outgoing request being currently processed. dynamic _request; @@ -168,8 +172,10 @@ class RTCSession extends EventManager implements Owner { @override int get TerminatedCode => RtcSessionState.terminated.index; - RTCDTMFSender get dtmfSender => - _connection!.createDtmfSender(_localMediaStream!.getAudioTracks()[0]); + RTCDTMFSender? get dtmfSender => _senders + .firstWhereOrNull((RTCRtpSender item) => + item.track != null && item.track!.kind == 'audio') + ?.dtmfSender; String? get contact => _contact; @@ -628,8 +634,9 @@ class RTCSession extends EventManager implements Owner { if (stream != null) { switch (sdpSemantics) { case 'unified-plan': - stream.getTracks().forEach((MediaStreamTrack track) { - _connection!.addTrack(track, stream!); + stream.getTracks().forEach((MediaStreamTrack track) async { + RTCRtpSender sender = await _connection!.addTrack(track, stream!); + _senders.add(sender); }); break; case 'plan-b': @@ -2395,8 +2402,9 @@ class RTCSession extends EventManager implements Owner { if (stream != null) { switch (sdpSemantics) { case 'unified-plan': - stream.getTracks().forEach((MediaStreamTrack track) { - _connection!.addTrack(track, stream!); + stream.getTracks().forEach((MediaStreamTrack track) async { + RTCRtpSender sender = await _connection!.addTrack(track, stream!); + _senders.add(sender); }); break; case 'plan-b': diff --git a/lib/src/rtc_session/dtmf.dart b/lib/src/rtc_session/dtmf.dart index c75967e2..92a1f80d 100644 --- a/lib/src/rtc_session/dtmf.dart +++ b/lib/src/rtc_session/dtmf.dart @@ -70,8 +70,8 @@ class DTMF extends EventManager { _interToneGap = options['interToneGap']; if (_mode == DtmfMode.RFC2833) { - RTCDTMFSender dtmfSender = _session.dtmfSender; - dtmfSender.insertDTMF(_tone!, + RTCDTMFSender? dtmfSender = _session.dtmfSender; + dtmfSender?.insertDTMF(_tone!, duration: _duration!, interToneGap: _interToneGap!); } else if (_mode == DtmfMode.INFO) { extraHeaders.add('Content-Type: application/dtmf-relay'); From a31fc33457b98f5345cb4b9d7e706fa3b2589f1c Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Tue, 1 Apr 2025 13:43:33 +0100 Subject: [PATCH 26/35] chore: removal of unneccesary pushToMaster work flow and import sorting --- .github/workflows/pushMaster.yaml | 27 -------------------- lib/src/config.dart | 1 - lib/src/data.dart | 2 -- lib/src/dialog.dart | 1 - lib/src/dialog/request_sender.dart | 2 -- lib/src/digest_authentication.dart | 1 - lib/src/event_manager/call_events.dart | 2 -- lib/src/event_manager/event_manager.dart | 1 - lib/src/event_manager/internal_events.dart | 2 -- lib/src/event_manager/message_events.dart | 1 - lib/src/event_manager/notifier_events.dart | 1 - lib/src/event_manager/options_events.dart | 1 - lib/src/event_manager/refer_events.dart | 1 - lib/src/event_manager/register_events.dart | 1 - lib/src/event_manager/subscriber_events.dart | 1 - lib/src/event_manager/transport_events.dart | 1 - lib/src/exceptions.dart | 1 - lib/src/grammar.dart | 1 - lib/src/grammar_parser.dart | 2 -- lib/src/logger.dart | 2 -- lib/src/message.dart | 1 - lib/src/name_addr_header.dart | 1 - lib/src/options.dart | 1 - lib/src/parser.dart | 2 -- lib/src/parser_error.dart | 1 - lib/src/registrator.dart | 2 -- lib/src/request_sender.dart | 1 - lib/src/rtc_session.dart | 3 --- lib/src/rtc_session/dtmf.dart | 2 -- lib/src/rtc_session/info.dart | 1 - lib/src/rtc_session/refer_notifier.dart | 1 - lib/src/rtc_session/refer_subscriber.dart | 1 - lib/src/sanity_check.dart | 1 - lib/src/sip_message.dart | 3 --- lib/src/sip_ua_helper.dart | 3 --- lib/src/socket_transport.dart | 2 -- lib/src/stack_trace_nj.dart | 2 -- lib/src/subscriber.dart | 3 --- lib/src/timers.dart | 1 - lib/src/transactions/ack_client.dart | 1 - lib/src/transactions/invite_client.dart | 2 -- lib/src/transactions/invite_server.dart | 2 -- lib/src/transactions/non_invite_client.dart | 2 -- lib/src/transactions/non_invite_server.dart | 2 -- lib/src/transactions/transaction_base.dart | 1 - lib/src/transactions/transactions.dart | 1 - lib/src/transports/tcp_socket.dart | 1 - lib/src/transports/tcp_socket_impl.dart | 2 -- lib/src/transports/web_socket.dart | 1 - lib/src/transports/websocket_dart_impl.dart | 2 -- lib/src/transports/websocket_web_impl.dart | 2 -- lib/src/ua.dart | 2 -- lib/src/uri.dart | 1 - lib/src/utils.dart | 3 --- test/all_test.dart | 1 - test/test2_websocket.dart | 1 - test/test_classes.dart | 2 -- test/test_digest_authentication.dart | 2 -- test/test_normalize_target.dart | 2 -- test/test_parser.dart | 2 -- test/test_sip_message_parser.dart | 2 -- test/test_sip_ua.dart | 3 --- test/test_uri_parse.dart | 2 -- test/test_websocket.dart | 3 --- 64 files changed, 129 deletions(-) delete mode 100644 .github/workflows/pushMaster.yaml diff --git a/.github/workflows/pushMaster.yaml b/.github/workflows/pushMaster.yaml deleted file mode 100644 index c4a299d8..00000000 --- a/.github/workflows/pushMaster.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: Push To Master - -on: - push: - branches: - - main - -jobs: - build: - name: Build Checks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install Flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: "3.27.1" - - name: Install project dependencies - run: flutter pub get - - name: Dart Format Check - run: dart format lib/ test/ --set-exit-if-changed - - name: Import Sorter Check - run: dart run import_sorter:main --exit-if-changed - - name: Dart Analyze Check - run: flutter analyze - - name: Dart Test Check - run: flutter test diff --git a/lib/src/config.dart b/lib/src/config.dart index a98db578..0f901955 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -1,4 +1,3 @@ -// Project imports: import '../sip_ua.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; diff --git a/lib/src/data.dart b/lib/src/data.dart index f8dd62fd..916ce420 100644 --- a/lib/src/data.dart +++ b/lib/src/data.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:core'; -// Project imports: import 'constants.dart'; import 'uri.dart'; diff --git a/lib/src/dialog.dart b/lib/src/dialog.dart index c46c131c..1d05cea8 100644 --- a/lib/src/dialog.dart +++ b/lib/src/dialog.dart @@ -1,4 +1,3 @@ -// Project imports: import 'constants.dart'; import 'dialog/request_sender.dart'; import 'event_manager/event_manager.dart'; diff --git a/lib/src/dialog/request_sender.dart b/lib/src/dialog/request_sender.dart index 5858afc6..414ae99b 100644 --- a/lib/src/dialog/request_sender.dart +++ b/lib/src/dialog/request_sender.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:async'; -// Project imports: import '../constants.dart'; import '../dialog.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/digest_authentication.dart b/lib/src/digest_authentication.dart index c17b3ba2..4bcd5dd3 100644 --- a/lib/src/digest_authentication.dart +++ b/lib/src/digest_authentication.dart @@ -1,4 +1,3 @@ -// Project imports: import 'constants.dart'; import 'logger.dart'; import 'utils.dart' as utils; diff --git a/lib/src/event_manager/call_events.dart b/lib/src/event_manager/call_events.dart index 1d1dd49e..feb13750 100644 --- a/lib/src/event_manager/call_events.dart +++ b/lib/src/event_manager/call_events.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; -// Project imports: import '../enums.dart'; import '../rtc_session.dart'; import '../sip_message.dart'; diff --git a/lib/src/event_manager/event_manager.dart b/lib/src/event_manager/event_manager.dart index 22d16947..aa84e7b0 100644 --- a/lib/src/event_manager/event_manager.dart +++ b/lib/src/event_manager/event_manager.dart @@ -1,4 +1,3 @@ -// Project imports: import '../logger.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/internal_events.dart b/lib/src/event_manager/internal_events.dart index a4930dcd..9b5adad2 100644 --- a/lib/src/event_manager/internal_events.dart +++ b/lib/src/event_manager/internal_events.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; -// Project imports: import '../enums.dart'; import '../rtc_session.dart' show RTCSession; import '../rtc_session/dtmf.dart'; diff --git a/lib/src/event_manager/message_events.dart b/lib/src/event_manager/message_events.dart index 4e12f036..445f61f9 100644 --- a/lib/src/event_manager/message_events.dart +++ b/lib/src/event_manager/message_events.dart @@ -1,4 +1,3 @@ -// Project imports: import '../enums.dart'; import '../message.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/notifier_events.dart b/lib/src/event_manager/notifier_events.dart index 2b11b029..d1ef620d 100644 --- a/lib/src/event_manager/notifier_events.dart +++ b/lib/src/event_manager/notifier_events.dart @@ -1,4 +1,3 @@ -// Project imports: import '../sip_message.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/options_events.dart b/lib/src/event_manager/options_events.dart index 7345b686..2bfe65a7 100644 --- a/lib/src/event_manager/options_events.dart +++ b/lib/src/event_manager/options_events.dart @@ -1,4 +1,3 @@ -// Project imports: import '../enums.dart'; import '../options.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/refer_events.dart b/lib/src/event_manager/refer_events.dart index fb2565a6..ca5f38f2 100644 --- a/lib/src/event_manager/refer_events.dart +++ b/lib/src/event_manager/refer_events.dart @@ -1,4 +1,3 @@ -// Project imports: import 'events.dart'; class EventReferTrying extends EventType { diff --git a/lib/src/event_manager/register_events.dart b/lib/src/event_manager/register_events.dart index 8f9afcee..aeb931db 100644 --- a/lib/src/event_manager/register_events.dart +++ b/lib/src/event_manager/register_events.dart @@ -1,4 +1,3 @@ -// Project imports: import 'events.dart'; class EventRegistrationExpiring extends EventType { diff --git a/lib/src/event_manager/subscriber_events.dart b/lib/src/event_manager/subscriber_events.dart index bd55d863..099e7365 100644 --- a/lib/src/event_manager/subscriber_events.dart +++ b/lib/src/event_manager/subscriber_events.dart @@ -1,4 +1,3 @@ -// Project imports: import '../sip_message.dart'; import 'events.dart'; diff --git a/lib/src/event_manager/transport_events.dart b/lib/src/event_manager/transport_events.dart index edd15062..83bc9f1b 100644 --- a/lib/src/event_manager/transport_events.dart +++ b/lib/src/event_manager/transport_events.dart @@ -1,4 +1,3 @@ -// Project imports: import '../transports/socket_interface.dart'; import '../transports/web_socket.dart'; import 'events.dart'; diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart index b5dca5ae..8c3e3151 100644 --- a/lib/src/exceptions.dart +++ b/lib/src/exceptions.dart @@ -1,4 +1,3 @@ -// Project imports: import 'utils.dart'; class ErrorImpl extends Error { diff --git a/lib/src/grammar.dart b/lib/src/grammar.dart index 46f41951..fb2feb8b 100644 --- a/lib/src/grammar.dart +++ b/lib/src/grammar.dart @@ -1,4 +1,3 @@ -// Project imports: import 'grammar_parser.dart'; import 'parser_error.dart'; diff --git a/lib/src/grammar_parser.dart b/lib/src/grammar_parser.dart index 39703504..47eaebb6 100644 --- a/lib/src/grammar_parser.dart +++ b/lib/src/grammar_parser.dart @@ -1,11 +1,9 @@ // This code was generated by a tool. // Processing tool available at https://github.com/mezoni/peg -// Dart imports: import 'dart:convert'; import 'dart:core'; -// Project imports: import 'data.dart'; import 'name_addr_header.dart'; import 'uri.dart'; diff --git a/lib/src/logger.dart b/lib/src/logger.dart index 46bb2120..21169d23 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -1,8 +1,6 @@ -// Package imports: import 'package:intl/intl.dart'; import 'package:logger/logger.dart'; -// Project imports: import 'stack_trace_nj.dart'; Logger logger = Log(); diff --git a/lib/src/message.dart b/lib/src/message.dart index 01e73071..c8f6092d 100644 --- a/lib/src/message.dart +++ b/lib/src/message.dart @@ -1,4 +1,3 @@ -// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'enums.dart'; diff --git a/lib/src/name_addr_header.dart b/lib/src/name_addr_header.dart index c02abd68..8e934490 100644 --- a/lib/src/name_addr_header.dart +++ b/lib/src/name_addr_header.dart @@ -1,4 +1,3 @@ -// Project imports: import 'grammar.dart'; import 'uri.dart'; import 'utils.dart'; diff --git a/lib/src/options.dart b/lib/src/options.dart index fe5028a8..e8e1ece6 100644 --- a/lib/src/options.dart +++ b/lib/src/options.dart @@ -1,4 +1,3 @@ -// Project imports: import 'package:sip_ua/src/enums.dart'; import 'package:sip_ua/src/name_addr_header.dart'; import 'constants.dart' as DartSIP_C; diff --git a/lib/src/parser.dart b/lib/src/parser.dart index 57d6ac2a..c169bcdb 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:convert' show utf8; -// Project imports: import 'grammar.dart'; import 'logger.dart'; import 'sip_message.dart'; diff --git a/lib/src/parser_error.dart b/lib/src/parser_error.dart index 1c2cefac..bc80928f 100644 --- a/lib/src/parser_error.dart +++ b/lib/src/parser_error.dart @@ -1,4 +1,3 @@ -// Package imports: import 'package:text/text.dart'; class ParserErrorMessage { diff --git a/lib/src/registrator.dart b/lib/src/registrator.dart index f172d87b..96a84b36 100644 --- a/lib/src/registrator.dart +++ b/lib/src/registrator.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:async'; -// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'event_manager/event_manager.dart'; diff --git a/lib/src/request_sender.dart b/lib/src/request_sender.dart index a745f9ee..42ec8cb8 100644 --- a/lib/src/request_sender.dart +++ b/lib/src/request_sender.dart @@ -1,4 +1,3 @@ -// Project imports: import 'constants.dart'; import 'data.dart'; import 'digest_authentication.dart'; diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 615b935a..53034b38 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -1,12 +1,9 @@ -// Dart imports: import 'dart:async'; -// Package imports: import 'package:collection/collection.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; -// Project imports: import '../sip_ua.dart'; import 'constants.dart' as DartSIP_C; import 'constants.dart'; diff --git a/lib/src/rtc_session/dtmf.dart b/lib/src/rtc_session/dtmf.dart index 92a1f80d..5609475f 100644 --- a/lib/src/rtc_session/dtmf.dart +++ b/lib/src/rtc_session/dtmf.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; -// Project imports: import '../../sip_ua.dart'; import '../constants.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/rtc_session/info.dart b/lib/src/rtc_session/info.dart index a6d97156..5399ba23 100644 --- a/lib/src/rtc_session/info.dart +++ b/lib/src/rtc_session/info.dart @@ -1,4 +1,3 @@ -// Project imports: import '../constants.dart'; import '../enums.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/rtc_session/refer_notifier.dart b/lib/src/rtc_session/refer_notifier.dart index 9d14abd0..76f81cb3 100644 --- a/lib/src/rtc_session/refer_notifier.dart +++ b/lib/src/rtc_session/refer_notifier.dart @@ -1,4 +1,3 @@ -// Project imports: import '../constants.dart' as DartSIP_C; import '../constants.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/rtc_session/refer_subscriber.dart b/lib/src/rtc_session/refer_subscriber.dart index f2b5527e..716db186 100644 --- a/lib/src/rtc_session/refer_subscriber.dart +++ b/lib/src/rtc_session/refer_subscriber.dart @@ -1,4 +1,3 @@ -// Project imports: import '../constants.dart' as DartSIP_C; import '../constants.dart'; import '../event_manager/event_manager.dart'; diff --git a/lib/src/sanity_check.dart b/lib/src/sanity_check.dart index 1751ffd0..01d1884a 100644 --- a/lib/src/sanity_check.dart +++ b/lib/src/sanity_check.dart @@ -1,4 +1,3 @@ -// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'logger.dart'; diff --git a/lib/src/sip_message.dart b/lib/src/sip_message.dart index 5e226fc0..1c6e6eff 100644 --- a/lib/src/sip_message.dart +++ b/lib/src/sip_message.dart @@ -1,10 +1,7 @@ -// Dart imports: import 'dart:convert' show utf8; -// Package imports: import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; -// Project imports: import 'constants.dart' as DartSIP_C; import 'constants.dart'; import 'data.dart'; diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index bfccadfc..288d5e60 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -1,12 +1,9 @@ -// Dart imports: import 'dart:async'; -// Package imports: import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:logger/logger.dart'; import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; -// Project imports: import 'package:sip_ua/src/uri.dart'; import 'config.dart'; import 'constants.dart' as DartSIP_C; diff --git a/lib/src/socket_transport.dart b/lib/src/socket_transport.dart index 1f2b083e..ea82046d 100644 --- a/lib/src/socket_transport.dart +++ b/lib/src/socket_transport.dart @@ -1,8 +1,6 @@ -// Dart imports: import 'dart:async'; import 'dart:math'; -// Project imports: import './event_manager/events.dart'; import './transports/socket_interface.dart'; import 'exceptions.dart' as Exceptions; diff --git a/lib/src/stack_trace_nj.dart b/lib/src/stack_trace_nj.dart index f5626b8d..146470de 100644 --- a/lib/src/stack_trace_nj.dart +++ b/lib/src/stack_trace_nj.dart @@ -1,9 +1,7 @@ -// Dart imports: import 'dart:core' as core show StackTrace; import 'dart:core'; import 'dart:io'; -// Package imports: import 'package:path/path.dart'; class StackTraceNJ implements core.StackTrace { diff --git a/lib/src/subscriber.dart b/lib/src/subscriber.dart index 8eaabca2..45ad1d13 100644 --- a/lib/src/subscriber.dart +++ b/lib/src/subscriber.dart @@ -1,10 +1,7 @@ -// Dart imports: import 'dart:async'; -// Package imports: import 'package:collection/collection.dart'; -// Project imports: import './exceptions.dart' as exceptions; import 'constants.dart'; import 'dialog.dart'; diff --git a/lib/src/timers.dart b/lib/src/timers.dart index 67a18557..f0f52432 100644 --- a/lib/src/timers.dart +++ b/lib/src/timers.dart @@ -1,4 +1,3 @@ -// Dart imports: import 'dart:async'; class Timers { diff --git a/lib/src/transactions/ack_client.dart b/lib/src/transactions/ack_client.dart index 3c9c0ed4..c1eacc1f 100644 --- a/lib/src/transactions/ack_client.dart +++ b/lib/src/transactions/ack_client.dart @@ -1,4 +1,3 @@ -// Project imports: import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; diff --git a/lib/src/transactions/invite_client.dart b/lib/src/transactions/invite_client.dart index ae876f77..5e1290e1 100644 --- a/lib/src/transactions/invite_client.dart +++ b/lib/src/transactions/invite_client.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:async'; -// Project imports: import '../constants.dart'; import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; diff --git a/lib/src/transactions/invite_server.dart b/lib/src/transactions/invite_server.dart index 55340455..f42246b2 100644 --- a/lib/src/transactions/invite_server.dart +++ b/lib/src/transactions/invite_server.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:async'; -// Project imports: import '../event_manager/internal_events.dart'; import '../logger.dart'; import '../sip_message.dart'; diff --git a/lib/src/transactions/non_invite_client.dart b/lib/src/transactions/non_invite_client.dart index ac8da0d9..ee725543 100644 --- a/lib/src/transactions/non_invite_client.dart +++ b/lib/src/transactions/non_invite_client.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:async'; -// Project imports: import '../event_manager/event_manager.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; diff --git a/lib/src/transactions/non_invite_server.dart b/lib/src/transactions/non_invite_server.dart index 6a83776f..d801b8e3 100644 --- a/lib/src/transactions/non_invite_server.dart +++ b/lib/src/transactions/non_invite_server.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:async'; -// Project imports: import 'package:sip_ua/src/sip_message.dart'; import '../event_manager/internal_events.dart'; import '../logger.dart'; diff --git a/lib/src/transactions/transaction_base.dart b/lib/src/transactions/transaction_base.dart index 9e3b6e42..38940087 100644 --- a/lib/src/transactions/transaction_base.dart +++ b/lib/src/transactions/transaction_base.dart @@ -1,4 +1,3 @@ -// Project imports: import '../event_manager/event_manager.dart'; import '../sip_message.dart'; import '../socket_transport.dart'; diff --git a/lib/src/transactions/transactions.dart b/lib/src/transactions/transactions.dart index 53888a5c..257f29c3 100644 --- a/lib/src/transactions/transactions.dart +++ b/lib/src/transactions/transactions.dart @@ -1,4 +1,3 @@ -// Project imports: import '../constants.dart'; import '../sip_message.dart'; import '../timers.dart'; diff --git a/lib/src/transports/tcp_socket.dart b/lib/src/transports/tcp_socket.dart index 6e475b79..379be330 100644 --- a/lib/src/transports/tcp_socket.dart +++ b/lib/src/transports/tcp_socket.dart @@ -1,4 +1,3 @@ -// Project imports: import '../../sip_ua.dart'; import '../logger.dart'; import 'socket_interface.dart'; diff --git a/lib/src/transports/tcp_socket_impl.dart b/lib/src/transports/tcp_socket_impl.dart index fa91f0bd..68a4727d 100644 --- a/lib/src/transports/tcp_socket_impl.dart +++ b/lib/src/transports/tcp_socket_impl.dart @@ -1,10 +1,8 @@ -// Dart imports: import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; -// Project imports: import '../logger.dart'; import '../sip_ua_helper.dart'; diff --git a/lib/src/transports/web_socket.dart b/lib/src/transports/web_socket.dart index 80c21d97..77c7753e 100644 --- a/lib/src/transports/web_socket.dart +++ b/lib/src/transports/web_socket.dart @@ -1,4 +1,3 @@ -// Project imports: import '../../sip_ua.dart'; import '../grammar.dart'; import '../logger.dart'; diff --git a/lib/src/transports/websocket_dart_impl.dart b/lib/src/transports/websocket_dart_impl.dart index 89ac9480..1ec30b21 100644 --- a/lib/src/transports/websocket_dart_impl.dart +++ b/lib/src/transports/websocket_dart_impl.dart @@ -1,10 +1,8 @@ -// Dart imports: import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; -// Project imports: import 'package:sip_ua/src/sip_ua_helper.dart'; import '../logger.dart'; diff --git a/lib/src/transports/websocket_web_impl.dart b/lib/src/transports/websocket_web_impl.dart index 522a1375..f72ebe11 100644 --- a/lib/src/transports/websocket_web_impl.dart +++ b/lib/src/transports/websocket_web_impl.dart @@ -1,8 +1,6 @@ -// Dart imports: import 'dart:html'; import 'dart:js_util' as JSUtils; -// Project imports: import 'package:sip_ua/src/sip_ua_helper.dart'; import '../logger.dart'; diff --git a/lib/src/ua.dart b/lib/src/ua.dart index 359f4ecb..8b95aa6d 100644 --- a/lib/src/ua.dart +++ b/lib/src/ua.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:async'; -// Project imports: import 'config.dart' as config; import 'config.dart'; import 'constants.dart' as DartSIP_C; diff --git a/lib/src/uri.dart b/lib/src/uri.dart index b7b2bc12..66d30b93 100644 --- a/lib/src/uri.dart +++ b/lib/src/uri.dart @@ -1,4 +1,3 @@ -// Project imports: import 'constants.dart' as DartSIP_C; import 'grammar.dart'; import 'utils.dart' as utils; diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 737bb3ac..3365b4f6 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,14 +1,11 @@ -// Dart imports: import 'dart:convert'; import 'dart:core'; import 'dart:math' as DartMath; -// Package imports: import 'package:crypto/crypto.dart'; import 'package:random_string/random_string.dart'; import 'package:uuid/uuid.dart'; -// Project imports: import 'constants.dart' as DartSIP_C; import 'grammar.dart'; import 'uri.dart'; diff --git a/test/all_test.dart b/test/all_test.dart index 26a611c0..7667228e 100644 --- a/test/all_test.dart +++ b/test/all_test.dart @@ -1,4 +1,3 @@ -// Project imports: import 'test_classes.dart' as Classes; import 'test_digest_authentication.dart' as DigestAuthentication; import 'test_normalize_target.dart' as NormalizeTarget; diff --git a/test/test2_websocket.dart b/test/test2_websocket.dart index 722b0b4d..8b4b25f0 100644 --- a/test/test2_websocket.dart +++ b/test/test2_websocket.dart @@ -1,4 +1,3 @@ -// Dart imports: import 'dart:async'; import 'dart:io'; diff --git a/test/test_classes.dart b/test/test_classes.dart index 3650c7bd..5ef70df4 100644 --- a/test/test_classes.dart +++ b/test/test_classes.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:test/test.dart'; -// Project imports: import 'package:sip_ua/src/name_addr_header.dart'; import 'package:sip_ua/src/uri.dart'; diff --git a/test/test_digest_authentication.dart b/test/test_digest_authentication.dart index d1ac9b94..a7738209 100644 --- a/test/test_digest_authentication.dart +++ b/test/test_digest_authentication.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:test/test.dart'; -// Project imports: import 'package:sip_ua/src/constants.dart'; import 'package:sip_ua/src/digest_authentication.dart'; diff --git a/test/test_normalize_target.dart b/test/test_normalize_target.dart index b81cd903..37f7f0ed 100644 --- a/test/test_normalize_target.dart +++ b/test/test_normalize_target.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:test/test.dart'; -// Project imports: import 'package:sip_ua/src/uri.dart'; import 'package:sip_ua/src/utils.dart' as Utils; diff --git a/test/test_parser.dart b/test/test_parser.dart index 060ea248..5409d874 100644 --- a/test/test_parser.dart +++ b/test/test_parser.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:test/test.dart'; -// Project imports: import 'package:sip_ua/src/data.dart'; import 'package:sip_ua/src/grammar.dart'; import 'package:sip_ua/src/name_addr_header.dart'; diff --git a/test/test_sip_message_parser.dart b/test/test_sip_message_parser.dart index 3369247d..4b4018cd 100644 --- a/test/test_sip_message_parser.dart +++ b/test/test_sip_message_parser.dart @@ -1,7 +1,5 @@ -// Package imports: import 'package:test/test.dart'; -// Project imports: import 'package:sip_ua/src/parser.dart'; import 'data/sip_message.dart'; diff --git a/test/test_sip_ua.dart b/test/test_sip_ua.dart index 7f114dbc..1b4c8118 100644 --- a/test/test_sip_ua.dart +++ b/test/test_sip_ua.dart @@ -1,10 +1,7 @@ -// Dart imports: import 'dart:async'; -// Package imports: import 'package:test/test.dart'; -// Project imports: import 'package:sip_ua/src/config.dart' as config; import 'package:sip_ua/src/event_manager/event_manager.dart'; import 'package:sip_ua/src/transports/socket_interface.dart'; diff --git a/test/test_uri_parse.dart b/test/test_uri_parse.dart index 2d536502..6b990818 100644 --- a/test/test_uri_parse.dart +++ b/test/test_uri_parse.dart @@ -1,7 +1,5 @@ -// Dart imports: import 'dart:io'; -// Package imports: import 'package:test/test.dart'; List testFunctions = [ diff --git a/test/test_websocket.dart b/test/test_websocket.dart index aad4b275..cad2dda5 100644 --- a/test/test_websocket.dart +++ b/test/test_websocket.dart @@ -1,11 +1,8 @@ -// Dart imports: import 'dart:async'; import 'dart:io'; -// Package imports: import 'package:test/test.dart'; -// Project imports: import 'package:sip_ua/src/event_manager/events.dart'; import 'package:sip_ua/src/socket_transport.dart'; import 'package:sip_ua/src/transports/socket_interface.dart'; From 356e5d7e830cf28ab5558f4b18bf821fab1f0ef3 Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Tue, 22 Apr 2025 09:49:05 +0100 Subject: [PATCH 27/35] Handling of ice states and network dropout timer --- example/pubspec.yaml | 2 +- lib/src/rtc_session.dart | 74 +++++++++++++++++++++++++++++++++++++--- pubspec.yaml | 2 +- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 07a277d8..c8ac2718 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: path: ../ shared_preferences: ^2.2.0 permission_handler: ^11.1.0 - flutter_webrtc: ^0.12.6 + flutter_webrtc: ^0.12.7 provider: 6.1.2 logger: ^2.5.0 diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 53034b38..ac7c1cae 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -120,6 +120,9 @@ class RTCSession extends EventManager implements Owner { // Flag to indicate PeerConnection ready for actions. bool _rtcReady = true; + Timer? _iceDisconnectTimer; + bool _isAttemptingIceRestart = false; + // SIP Timers. final SIPTimers _timers = SIPTimers(); @@ -1548,6 +1551,8 @@ class RTCSession extends EventManager implements Owner { clearTimeout(_timers.invite2xxTimer); clearTimeout(_timers.userNoAnswerTimer); + _iceDisconnectTimer?.cancel(); + // Clear Session Timers. clearTimeout(_sessionTimers.timer); @@ -1635,20 +1640,79 @@ class RTCSession extends EventManager implements Owner { Map rtcConstraints) async { _connection = await createPeerConnection(pcConfig, rtcConstraints); _connection!.onIceConnectionState = (RTCIceConnectionState state) { - // TODO(cloudwebrtc): Do more with different states. + if (_state == RtcSessionState.terminated || + _state == RtcSessionState.canceled) { + logger.d( + 'ICE State change ignored, SIP session already terminated/canceled.'); + _iceDisconnectTimer?.cancel(); + return; + } + if (state == RTCIceConnectionState.RTCIceConnectionStateFailed) { + logger.e('ICE Connection State Failed.'); + _iceDisconnectTimer?.cancel(); terminate({ 'cause': DartSIP_C.CausesType.RTP_TIMEOUT, 'status_code': 408, - 'reason_phrase': DartSIP_C.CausesType.RTP_TIMEOUT + 'reason_phrase': 'ICE Connection Failed' }); } else if (state == RTCIceConnectionState.RTCIceConnectionStateDisconnected) { - if (_state == RtcSessionState.terminated) return; - _iceRestart(); + logger.w('ICE Connection State Disconnected.'); + if (_iceDisconnectTimer == null && !_isAttemptingIceRestart) { + logger.i('Starting ICE disconnect timer...'); + _iceDisconnectTimer = Timer(const Duration(seconds: 20), () { + logger.w('ICE disconnect timer fired!'); + if (_connection?.iceConnectionState == + RTCIceConnectionState.RTCIceConnectionStateDisconnected && + _state != RtcSessionState.terminated && + _state != RtcSessionState.canceled && + !_isAttemptingIceRestart) { + logger.i('Attempting ICE restart after timeout...'); + _isAttemptingIceRestart = true; + _iceRestart(); + } else { + logger.i('ICE restart aborted (state changed during timer).'); + } + _iceDisconnectTimer = null; + }); + } else { + logger.d( + 'ICE disconnect timer not started (already running or attempting restart).'); + } + } else if (state == + RTCIceConnectionState.RTCIceConnectionStateConnected || + state == RTCIceConnectionState.RTCIceConnectionStateCompleted) { + // If connection recovers, cancel timer and reset flag + if (_iceDisconnectTimer != null || _isAttemptingIceRestart) { + logger.i( + 'ICE Connection State Connected/Completed. Canceling timer/resetting flag.'); + _iceDisconnectTimer?.cancel(); + _isAttemptingIceRestart = false; + } else { + logger.i('ICE Connection State Connected/Completed.'); + } + } else if (state == RTCIceConnectionState.RTCIceConnectionStateClosed) { + // Connection closed locally, usually via _connection.close() called by terminate() + logger.i('ICE Connection State Closed.'); // Use logger.i + _iceDisconnectTimer?.cancel(); // Ensure timer is cancelled + // Ensure *SIP* session state reflects closure if not already set by terminate() + if (_state != RtcSessionState.terminated && + _state != RtcSessionState.canceled) { + logger.w( + 'ICE closed but SIP session state was not terminal. Terminating SIP session now.'); + terminate({ + 'cause': DartSIP_C.CausesType.WEBRTC_ERROR, + 'status_code': 487, + 'reason_phrase': 'ICE Connection Closed' + }); + } + } else if (state == RTCIceConnectionState.RTCIceConnectionStateChecking) { + logger.d('ICE Connection State Checking...'); // Use logger.d + } else if (state == RTCIceConnectionState.RTCIceConnectionStateNew) { + logger.d('ICE Connection State New.'); // Use logger.d } }; - // In future versions, unified-plan will be used by default String? sdpSemantics = 'unified-plan'; if (pcConfig['sdpSemantics'] != null) { diff --git a/pubspec.yaml b/pubspec.yaml index f9f13b92..aab5514f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: bloc: ^9.0.0 collection: ^1.18.0 crypto: ^3.0.3 - flutter_webrtc: ^0.12.6 + flutter_webrtc: ^0.12.7 intl: ^0.20.1 logger: ^2.0.2+1 path: ^1.6.4 From 38dbab7ce420d62276730ac34b60cc6cfc2eaefb Mon Sep 17 00:00:00 2001 From: Mikael Wills Date: Tue, 22 Apr 2025 09:50:37 +0100 Subject: [PATCH 28/35] Sip message stateless reply reason fixes #539 --- lib/src/sip_message.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/sip_message.dart b/lib/src/sip_message.dart index 1c6e6eff..e13763c2 100644 --- a/lib/src/sip_message.dart +++ b/lib/src/sip_message.dart @@ -656,8 +656,6 @@ class IncomingRequest extends IncomingMessage { // Validate code and reason values. if (code == null || (code < 100 || code > 699)) { throw Exceptions.TypeError('Invalid status_code: $code'); - } else if (reason != null) { - throw Exceptions.TypeError('Invalid reason_phrase: $reason'); } reason = reason ?? DartSIP_C.REASON_PHRASE[code] ?? ''; From afc80cc963f4a61cd05d88faa3e78ccc481fa248 Mon Sep 17 00:00:00 2001 From: mechtech-mind <85521656+mechtech-mind@users.noreply.github.com> Date: Fri, 26 Sep 2025 19:17:41 +0530 Subject: [PATCH 29/35] fix: persist 'ws_uri' in _saveSettings to enable proper registration (#542) --- example/lib/src/register.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/example/lib/src/register.dart b/example/lib/src/register.dart index 98b02fb4..c96332d7 100644 --- a/example/lib/src/register.dart +++ b/example/lib/src/register.dart @@ -127,6 +127,13 @@ class _MyRegisterWidget extends State _saveSettings(); currentUser.register(SipUser( + wsUrl: _wsUriController.text, + //this is the websocket url which was missing in the original code hence it + //was showing null in the register method of sip_user_cubit.dart and always + //redirected to 'wss://tryit.jssip.net:10443', present inside sip_ua_helper.dart + //161: uaSettings.webSocketUrl ?? 'wss://tryit.jssip.net:10443', + //this will help people trying out the example to register with the correct url + //without changing the code in sip_ua_helper.dart selectedTransport: _selectedTransport, wsExtraHeaders: _wsExtraHeaders, sipUri: _sipUriController.text, From 2fbdfb2a9f5b13852054acc91c824c4b6dd30dd6 Mon Sep 17 00:00:00 2001 From: Emmanuel Schmidbauer Date: Fri, 26 Sep 2025 09:49:14 -0400 Subject: [PATCH 30/35] session-timers fix (#545) --- lib/src/rtc_session.dart | 27 ++++++++++++++------------- lib/src/sip_ua_helper.dart | 13 +++++++++++-- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index ac7c1cae..d156e85f 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -3281,19 +3281,20 @@ class RTCSession extends EventManager implements Owner { // I'm the refresher. if (_sessionTimers.refresher) { - _sessionTimers.timer = setTimeout(() { - if (_state == RtcSessionState.terminated) { - return; - } - - logger.d('runSessionTimer() | sending session refresh request'); - - if (_sessionTimers.refreshMethod == SipMethod.UPDATE) { - _sendUpdate(); - } else { - _sendReinvite(); - } - }, expires! * 500); // Half the given interval (as the RFC states). + final int delayMs = expires! * 500; + _sessionTimers.timer = Timer.periodic( + Duration(milliseconds: delayMs), + (_) { + if (_state == RtcSessionState.terminated) return; + logger.d( + 'runSessionTimer() | sending session refresh request with expires=$expires, delayMs=$delayMs'); + if (_sessionTimers.refreshMethod == SipMethod.UPDATE) { + _sendUpdate(); + } else { + _sendReinvite(); + } + }, + ); } // I'm not the refresher. else { diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 288d5e60..28920915 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -182,7 +182,7 @@ class SIPUAHelper extends EventManager { _settings.session_timers = uaSettings.sessionTimers; _settings.ice_gathering_timeout = uaSettings.iceGatheringTimeout; _settings.session_timers_refresh_method = - uaSettings.sessionTimersRefreshMethod; + uaSettings.sessionTimersRefreshMethodEnum; _settings.instance_id = uaSettings.instanceId; _settings.registrar_server = uaSettings.registrarServer; _settings.contact_uri = uaSettings.contact_uri != null @@ -921,5 +921,14 @@ class UaSettings { /// Controls which kind of messages are to be sent to keep a SIP session /// alive. /// Defaults to "UPDATE" - DartSIP_C.SipMethod sessionTimersRefreshMethod = DartSIP_C.SipMethod.UPDATE; + String sessionTimersRefreshMethod = 'UPDATE'; + DartSIP_C.SipMethod get sessionTimersRefreshMethodEnum { + switch (sessionTimersRefreshMethod.toUpperCase()) { + case 'INVITE': + return DartSIP_C.SipMethod.INVITE; + case 'UPDATE': + default: + return DartSIP_C.SipMethod.UPDATE; + } + } } From 2109dd271d568a4ad0ae983980b26f5b1521ca8e Mon Sep 17 00:00:00 2001 From: Volsavr Date: Fri, 26 Sep 2025 16:50:10 +0300 Subject: [PATCH 31/35] Setting for call statistics logs (#549) * writing call stats to log * Update rtc_session.dart * fix code format --------- Co-authored-by: Volodymyr B --- lib/src/config.dart | 6 ++++ lib/src/rtc_session.dart | 66 +++++++++++++++++++++++++++++++++++++- lib/src/sip_ua_helper.dart | 4 +++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/lib/src/config.dart b/lib/src/config.dart index 0f901955..7dc05c9c 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -64,6 +64,9 @@ class Settings { /// ICE Gathering Timeout (in millisecond). int ice_gathering_timeout = 500; + /// Call statistics in the log + bool log_call_statistics = false; + bool terminateOnAudioMediaPortZero = false; /// Sip Message Delay (in millisecond) ( default 0 ). @@ -255,6 +258,9 @@ class Checks { }, 'ice_gathering_timeout': (Settings src, Settings? dst) { dst!.ice_gathering_timeout = src.ice_gathering_timeout; + }, + 'log_call_statistics': (Settings src, Settings? dst) { + dst!.log_call_statistics = src.log_call_statistics; } }; } diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index d156e85f..0d760843 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -726,7 +726,7 @@ class RTCSession extends EventManager implements Owner { /** * Terminate the call. */ - void terminate([Map? options]) { + void terminate([Map? options]) async { logger.d('terminate()'); options = options ?? {}; @@ -844,6 +844,9 @@ class RTCSession extends EventManager implements Owner { } }); + //write call statistics to the log + await _logCallStat(); + _ended( Originator.local, null, @@ -863,6 +866,10 @@ class RTCSession extends EventManager implements Owner { {'extraHeaders': extraHeaders, 'body': body}); reason_phrase = reason_phrase ?? 'Terminated by local'; status_code = status_code ?? 200; + + //write call statistics to the log + await _logCallStat(); + _ended( Originator.local, null, @@ -1359,6 +1366,10 @@ class RTCSession extends EventManager implements Owner { case SipMethod.BYE: if (_state == RtcSessionState.confirmed) { request.reply(200); + + //write call statistics to the log + await _logCallStat(); + _ended( Originator.remote, request, @@ -1369,6 +1380,10 @@ class RTCSession extends EventManager implements Owner { } else if (_state == RtcSessionState.inviteReceived) { request.reply(200); _request.reply(487, 'BYE Received'); + + //write call statistics to the log + await _logCallStat(); + _ended( Originator.remote, request, @@ -3442,4 +3457,53 @@ class RTCSession extends EventManager implements Owner { logger.d('emit "unmuted"'); emit(EventCallUnmuted(session: this, audio: audio, video: video)); } + + Future _logCallStat() async { + if (!ua.configuration.log_call_statistics) return; + + try { + List? senders = await connection?.senders; + List? receivers = await connection?.receivers; + + RTCRtpReceiver? receiver = receivers?.firstOrNull; + RTCRtpSender? sender = senders?.firstOrNull; + + List senderStats = []; + List receiverStats = []; + + if (sender != null) { + senderStats = await sender.getStats(); + } + + if (receiver != null) { + receiverStats = await receiver.getStats(); + } + + String senderStat = 'Sender stats: \n'; + + for (StatsReport s in senderStats) { + senderStat += ' ${s.timestamp} ${s.id} ${s.type}:\n'; + s.values.forEach((key, value) { + senderStat += ' $key: $value\n'; + }); + senderStat += '\r'; + } + + logger.d(senderStat); + + String receiverStat = 'Receiver stats: \n'; + + for (StatsReport s in receiverStats) { + receiverStat += ' ${s.timestamp} ${s.id} ${s.type}\n'; + s.values.forEach((key, value) { + receiverStat += ' $key: $value\n'; + }); + receiverStat += '\r'; + } + + logger.d(receiverStat); + } catch (e) { + return; + } + } } diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 28920915..57c160f7 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -194,6 +194,7 @@ class SIPUAHelper extends EventManager { uaSettings.connectionRecoveryMinInterval; _settings.terminateOnAudioMediaPortZero = uaSettings.terminateOnMediaPortZero; + _settings.log_call_statistics = uaSettings.logCallStatistics; try { _ua = UA(_settings); @@ -899,6 +900,9 @@ class UaSettings { /// Min interval between recovery connection, default 2 sec int connectionRecoveryMinInterval = 2; + /// Allows to write advanced call statistics in the log after the call ends + bool logCallStatistics = false; + bool terminateOnMediaPortZero = false; /// Sip Message Delay (in millisecond) (default 0). From ffd9fd109e9e96ea2fb21f15b5627f2e09ebd9d1 Mon Sep 17 00:00:00 2001 From: Volsavr Date: Fri, 26 Sep 2025 16:50:50 +0300 Subject: [PATCH 32/35] Extend configuration for ice gathering process (#550) * extend configuration for ice gathering process * fix code format --------- Co-authored-by: Volodymyr B --- lib/src/sip_ua_helper.dart | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 57c160f7..3ef28651 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -373,7 +373,11 @@ class SIPUAHelper extends EventManager { 'iceTransportPolicy': (_uaSettings?.iceTransportPolicy ?? IceTransportPolicy.ALL) .toParameterString(), - 'iceServers': _uaSettings?.iceServers + 'iceServers': _uaSettings?.iceServers, + 'tcpCandidatePolicy': + (_uaSettings?.tcpCandidatePolicy ?? TcpCandidatePolicy.ENABLED) + .toParameterString(), + 'iceCandidatePoolSize': _uaSettings?.iceCandidatePoolSize }, 'mediaConstraints': { 'audio': true, @@ -854,6 +858,19 @@ extension _IceTransportPolicyEncoding on IceTransportPolicy { } } +enum TcpCandidatePolicy { ENABLED, DISABLED } + +extension _TcpCandidatePolicyEncoding on TcpCandidatePolicy { + String toParameterString() { + switch (this) { + case TcpCandidatePolicy.ENABLED: + return 'enabled'; + case TcpCandidatePolicy.DISABLED: + return 'disabled'; + } + } +} + class UaSettings { WebSocketSettings webSocketSettings = WebSocketSettings(); TcpSocketSettings tcpSocketSettings = TcpSocketSettings(); @@ -922,6 +939,18 @@ class UaSettings { /// Will default to [IceTransportPolicy.ALL] if not specified. IceTransportPolicy? iceTransportPolicy; + /// Allows to disable tcp candidates gathering + /// Will default to [TcpCandidatePolicy.ENABLED] if not specified. + TcpCandidatePolicy? tcpCandidatePolicy; + + /// An unsigned 16-bit integer value which specifies the size of the prefetched + /// ICE candidate pool. The default value is 0 (meaning no candidate prefetching will occur). + /// You may find in some cases that connections can be established more quickly + /// by allowing the ICE agent to start fetching ICE candidates before you start + /// trying to connect, so that they're already available for inspection + /// when RTCPeerConnection.setLocalDescription() is called. + int iceCandidatePoolSize = 0; + /// Controls which kind of messages are to be sent to keep a SIP session /// alive. /// Defaults to "UPDATE" From b35839c35f90a41468b40efd6975ffb67de664a9 Mon Sep 17 00:00:00 2001 From: Emmanuel Schmidbauer Date: Fri, 26 Sep 2025 09:51:17 -0400 Subject: [PATCH 33/35] allow setting call-id (#552) --- lib/src/rtc_session.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 0d760843..e8d9872b 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -308,6 +308,7 @@ class RTCSession extends EventManager implements Owner { Map requestParams = { 'from_tag': _from_tag, 'to_display_name': options['to_display_name'] ?? '', + 'call_id': options['call_id'] ?? null, }; _ua.contact!.anonymous = anonymous; _ua.contact!.outbound = true; From 02edd390ece6122a7aeaddbabcdbb47dad9cfa64 Mon Sep 17 00:00:00 2001 From: Md Jihanur Rahman <64974221+jihanurrahman33@users.noreply.github.com> Date: Fri, 26 Sep 2025 19:54:47 +0600 Subject: [PATCH 34/35] docs(readme): replace pub.dartlang.org with pub.dev; normalize macOS; update Flutter docs link (#555) Co-authored-by: jihanurrahman33 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07b867a4..3d375545 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # dart-sip-ua -[![Financial Contributors on Open Collective](https://opencollective.com/flutter-webrtc/all/badge.svg?label=financial+contributors)](https://opencollective.com/flutter-webrtc) [![pub package](https://img.shields.io/pub/v/sip_ua.svg)](https://pub.dartlang.org/packages/sip_ua) [![slack](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen)](https://join.slack.com/t/flutterwebrtc/shared_invite/zt-q83o7y1s-FExGLWEvtkPKM8ku_F8cEQ) +[![Financial Contributors on Open Collective](https://opencollective.com/flutter-webrtc/all/badge.svg?label=financial+contributors)](https://opencollective.com/flutter-webrtc) [![pub package](https://img.shields.io/pub/v/sip_ua.svg)](https://pub.dev/packages/sip_ua) [![slack](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen)](https://join.slack.com/t/flutterwebrtc/shared_invite/zt-q83o7y1s-FExGLWEvtkPKM8ku_F8cEQ) A dart-lang version of the SIP UA stack, ported from [JsSIP](https://github.com/versatica/JsSIP). From f88abac7932479a98b52bf96e18a05a69ed660e4 Mon Sep 17 00:00:00 2001 From: CloudWebRTC Date: Fri, 26 Sep 2025 22:22:55 +0800 Subject: [PATCH 35/35] chore: bump verson for flutter-webrtc and update examples. (#558) * chore: bump verson for flutter-webrtc and update examples. * bump version to 1.1.0. --- CHANGELOG.md | 15 +++ example/.metadata | 45 +++++++ example/android/.gitignore | 3 +- example/android/app/build.gradle | 58 --------- example/android/app/build.gradle.kts | 44 +++++++ .../dart_sip_ua_example/MainActivity.java | 6 - .../dart_sip_ua_example/MainActivity.kt | 5 + example/android/build.gradle | 18 --- example/android/build.gradle.kts | 24 ++++ example/android/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 25 ---- example/android/settings.gradle.kts | 26 ++++ example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Podfile | 4 +- example/ios/Podfile.lock | 49 -------- example/ios/Runner.xcodeproj/project.pbxproj | 118 +++++++++--------- .../xcshareddata/xcschemes/Runner.xcscheme | 5 +- example/ios/Runner/AppDelegate.h | 6 - example/ios/Runner/AppDelegate.m | 13 -- example/ios/Runner/AppDelegate.swift | 13 ++ example/ios/Runner/Runner-Bridging-Header.h | 1 + example/ios/Runner/main.m | 9 -- example/ios/RunnerTests/RunnerTests.m | 16 --- example/ios/RunnerTests/RunnerTests.swift | 12 ++ example/linux/CMakeLists.txt | 23 +--- example/linux/runner/CMakeLists.txt | 26 ++++ example/linux/{ => runner}/main.cc | 0 example/linux/{ => runner}/my_application.cc | 22 +++- example/linux/{ => runner}/my_application.h | 0 example/macos/Podfile | 3 +- .../macos/Runner.xcodeproj/project.pbxproj | 30 ++++- .../xcshareddata/xcschemes/Runner.xcscheme | 1 + example/macos/Runner/AppDelegate.swift | 6 +- example/macos/Runner/Configs/AppInfo.xcconfig | 2 +- .../macos/Runner/DebugProfile.entitlements | 6 - example/macos/Runner/Release.entitlements | 6 - example/pubspec.yaml | 1 - example/web/index.html | 2 +- example/windows/runner/Runner.rc | 2 +- example/windows/runner/runner.exe.manifest | 6 - pubspec.yaml | 4 +- 42 files changed, 343 insertions(+), 318 deletions(-) create mode 100644 example/.metadata delete mode 100644 example/android/app/build.gradle create mode 100644 example/android/app/build.gradle.kts delete mode 100644 example/android/app/src/main/java/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.java create mode 100644 example/android/app/src/main/kotlin/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.kt delete mode 100644 example/android/build.gradle create mode 100644 example/android/build.gradle.kts delete mode 100644 example/android/settings.gradle create mode 100644 example/android/settings.gradle.kts delete mode 100644 example/ios/Podfile.lock delete mode 100644 example/ios/Runner/AppDelegate.h delete mode 100644 example/ios/Runner/AppDelegate.m create mode 100644 example/ios/Runner/AppDelegate.swift create mode 100644 example/ios/Runner/Runner-Bridging-Header.h delete mode 100644 example/ios/Runner/main.m delete mode 100644 example/ios/RunnerTests/RunnerTests.m create mode 100644 example/ios/RunnerTests/RunnerTests.swift create mode 100644 example/linux/runner/CMakeLists.txt rename example/linux/{ => runner}/main.cc (100%) rename example/linux/{ => runner}/my_application.cc (82%) rename example/linux/{ => runner}/my_application.h (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0719b820..736418b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # Changelog -------------------------------------------- +[1.1.0] -2025-09-26 + +* Bump flutter-webrtc to 1.2.0 +* Docs(readme): replace pub.dartlang.org with pub.dev; normalize macOS; update Flutter docs link (#555) +* Allow setting call-id (#552) +* Extend configuration for ice gathering process (#550) +* Setting for call statistics logs (#549) +* Session-timers fix (#545) +* Fix: persist 'ws_uri' in `_saveSettings` to enable proper registration (#542) +* Sip message stateless reply reason fixes #539 +* Chore: removal of unneccesary pushToMaster work flow and import sorting +* Fix bug for rfc2833. (#534) +* Sessions keys toList added (#514) +* Build fixes + [1.0.1] -2024.12.19 * make sure the session terminates by @victortive in https://github.com/flutter-webrtc/dart-sip-ua/pull/485 diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 00000000..1101d3f3 --- /dev/null +++ b/example/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "d693b4b9dbac2acd4477aea4555ca6dcbea44ba2" + channel: "[user-branch]" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + - platform: android + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + - platform: ios + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + - platform: linux + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + - platform: macos + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + - platform: web + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + - platform: windows + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/android/.gitignore b/example/android/.gitignore index 6f568019..be3943c9 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -5,9 +5,10 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java +.cxx/ # Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +# See https://flutter.dev/to/reference-keystore key.properties **/*.keystore **/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index 721f4f4d..00000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,58 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file("local.properties") -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader("UTF-8") { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty("flutter.versionCode") -if (flutterVersionCode == null) { - flutterVersionCode = "1" -} - -def flutterVersionName = localProperties.getProperty("flutter.versionName") -if (flutterVersionName == null) { - flutterVersionName = "1.0" -} - -android { - namespace = "com.github.cloudwebrtc.dart_sip_ua_example" - compileSdk = flutter.compileSdkVersion - ndkVersion = flutter.ndkVersion - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.github.cloudwebrtc.dart_sip_ua_example" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = flutter.minSdkVersion - targetSdk = flutter.targetSdkVersion - versionCode = flutterVersionCode.toInteger() - versionName = flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.debug - } - } -} - -flutter { - source = "../.." -} diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts new file mode 100644 index 00000000..dadc2379 --- /dev/null +++ b/example/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.github.cloudwebrtc.dart_sip_ua_example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.github.cloudwebrtc.dart_sip_ua_example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/example/android/app/src/main/java/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.java b/example/android/app/src/main/java/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.java deleted file mode 100644 index 2aa4a9c5..00000000 --- a/example/android/app/src/main/java/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.github.cloudwebrtc.dart_sip_ua_example; - -import io.flutter.embedding.android.FlutterActivity; - -public class MainActivity extends FlutterActivity { -} diff --git a/example/android/app/src/main/kotlin/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.kt new file mode 100644 index 00000000..cf3b481c --- /dev/null +++ b/example/android/app/src/main/kotlin/com/github/cloudwebrtc/dart_sip_ua_example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.github.cloudwebrtc.dart_sip_ua_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index d2ffbffa..00000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = "../build" -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(":app") -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/example/android/build.gradle.kts b/example/android/build.gradle.kts new file mode 100644 index 00000000..dbee657b --- /dev/null +++ b/example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 3b5b324f..f018a618 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 8bc9958a..ac3b4792 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index b9e43bd3..00000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,25 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.1.0" apply false - id "org.jetbrains.kotlin.android" version "1.8.22" apply false -} - -include ":app" diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts new file mode 100644 index 00000000..fb605bc8 --- /dev/null +++ b/example/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.9.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 7c569640..1dc6cf76 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 13.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index ed164703..620e46eb 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '13.0' +# platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -28,6 +28,8 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do + use_frameworks! + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock deleted file mode 100644 index 9cc4716d..00000000 --- a/example/ios/Podfile.lock +++ /dev/null @@ -1,49 +0,0 @@ -PODS: - - Flutter (1.0.0) - - flutter_webrtc (0.12.2): - - Flutter - - WebRTC-SDK (= 125.6422.06) - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - permission_handler_apple (9.3.0): - - Flutter - - shared_preferences_foundation (0.0.1): - - Flutter - - FlutterMacOS - - WebRTC-SDK (125.6422.06) - -DEPENDENCIES: - - Flutter (from `Flutter`) - - flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`) - - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - -SPEC REPOS: - trunk: - - WebRTC-SDK - -EXTERNAL SOURCES: - Flutter: - :path: Flutter - flutter_webrtc: - :path: ".symlinks/plugins/flutter_webrtc/ios" - path_provider_foundation: - :path: ".symlinks/plugins/path_provider_foundation/darwin" - permission_handler_apple: - :path: ".symlinks/plugins/permission_handler_apple/ios" - shared_preferences_foundation: - :path: ".symlinks/plugins/shared_preferences_foundation/darwin" - -SPEC CHECKSUMS: - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563 - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db - -PODFILE CHECKSUM: a5dd15803c05a42a9ae1068254e24631f03bf853 - -COCOAPODS: 1.15.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index a50780a3..e2ebb6e6 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,17 +8,16 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 331C80F3294D02FB00263BE5 /* RunnerTests.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */ = { + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 97C146E61CF9000F007C117D /* Project object */; proxyType = 1; @@ -43,16 +42,15 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 331C80F3294D02FB00263BE5 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -60,13 +58,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 331C80EE294D02FB00263BE5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -77,10 +68,10 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 331C80F2294D02FB00263BE5 /* RunnerTests */ = { + 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( - 331C80F3294D02FB00263BE5 /* RunnerTests.m */, + 331C807B294A618700263BE5 /* RunnerTests.swift */, ); path = RunnerTests; sourceTree = ""; @@ -101,8 +92,8 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - 331C80F2294D02FB00263BE5 /* RunnerTests */, 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, ); sourceTree = ""; }; @@ -110,7 +101,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -118,46 +109,36 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 331C80F0294D02FB00263BE5 /* RunnerTests */ = { + 331C8080294A63A400263BE5 /* RunnerTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 331C80ED294D02FB00263BE5 /* Sources */, - 331C80EE294D02FB00263BE5 /* Frameworks */, - 331C80EF294D02FB00263BE5 /* Resources */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, ); buildRules = ( ); dependencies = ( - 331C80F6294D02FB00263BE5 /* PBXTargetDependency */, + 331C8086294A63A400263BE5 /* PBXTargetDependency */, ); name = RunnerTests; productName = RunnerTests; - productReference = 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 97C146ED1CF9000F007C117D /* Runner */ = { @@ -190,12 +171,13 @@ LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { - 331C80F0294D02FB00263BE5 = { + 331C8080294A63A400263BE5 = { CreatedOnToolsVersion = 14.0; TestTargetID = 97C146ED1CF9000F007C117D; }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; }; }; }; @@ -213,13 +195,13 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - 331C80F0294D02FB00263BE5 /* RunnerTests */, + 331C8080294A63A400263BE5 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 331C80EF294D02FB00263BE5 /* Resources */ = { + 331C807F294A63A400263BE5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -274,11 +256,11 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 331C80ED294D02FB00263BE5 /* Sources */ = { + 331C807D294A63A400263BE5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */, + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -286,8 +268,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -295,10 +276,10 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 331C80F6294D02FB00263BE5 /* PBXTargetDependency */ = { + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -365,7 +346,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -379,8 +360,8 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 954G8NSFLG; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -389,45 +370,55 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.github.cloudwebrtc.dartSipUaExample; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; }; - 331C80F8294D02FB00263BE5 /* Debug */ = { + 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.github.cloudwebrtc.dartSipUaExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; }; name = Debug; }; - 331C80F9294D02FB00263BE5 /* Release */ = { + 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.github.cloudwebrtc.dartSipUaExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; }; name = Release; }; - 331C80FA294D02FB00263BE5 /* Profile */ = { + 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.github.cloudwebrtc.dartSipUaExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; }; name = Profile; @@ -481,7 +472,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -532,10 +523,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -546,8 +539,8 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 954G8NSFLG; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -556,6 +549,9 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.github.cloudwebrtc.dartSipUaExample; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -565,8 +561,8 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 954G8NSFLG; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -575,6 +571,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.github.cloudwebrtc.dartSipUaExample; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -582,12 +580,12 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 331C80F8294D02FB00263BE5 /* Debug */, - 331C80F9294D02FB00263BE5 /* Release */, - 331C80FA294D02FB00263BE5 /* Profile */, + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3d0fb007..e3773d42 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> @@ -54,11 +55,13 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 36e21bbf..00000000 --- a/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 70e83933..00000000 --- a/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,13 +0,0 @@ -#import "AppDelegate.h" -#import "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..62666446 --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m deleted file mode 100644 index dff6597e..00000000 --- a/example/ios/Runner/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/ios/RunnerTests/RunnerTests.m b/example/ios/RunnerTests/RunnerTests.m deleted file mode 100644 index 6d8b0bde..00000000 --- a/example/ios/RunnerTests/RunnerTests.m +++ /dev/null @@ -1,16 +0,0 @@ -#import -#import -#import - -@interface RunnerTests : XCTestCase - -@end - -@implementation RunnerTests - -- (void)testExample { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. -} - -@end diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt index 0eb7fd0f..f7843d93 100644 --- a/example/linux/CMakeLists.txt +++ b/example/linux/CMakeLists.txt @@ -1,5 +1,5 @@ # Project-level configuration. -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.13) project(runner LANGUAGES CXX) # The name of the executable created for the application. Change this to change @@ -54,25 +54,8 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) find_package(PkgConfig REQUIRED) pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") # Run the Flutter tool portions of the build. This must not be removed. add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/example/linux/runner/CMakeLists.txt b/example/linux/runner/CMakeLists.txt new file mode 100644 index 00000000..e97dabc7 --- /dev/null +++ b/example/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/example/linux/main.cc b/example/linux/runner/main.cc similarity index 100% rename from example/linux/main.cc rename to example/linux/runner/main.cc diff --git a/example/linux/my_application.cc b/example/linux/runner/my_application.cc similarity index 82% rename from example/linux/my_application.cc rename to example/linux/runner/my_application.cc index 9cc6c558..ff70ee17 100644 --- a/example/linux/my_application.cc +++ b/example/linux/runner/my_application.cc @@ -14,6 +14,12 @@ struct _MyApplication { G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) +// Called when first Flutter frame received. +static void first_frame_cb(MyApplication* self, FlView *view) +{ + gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); +} + // Implements GApplication::activate. static void my_application_activate(GApplication* application) { MyApplication* self = MY_APPLICATION(application); @@ -48,15 +54,23 @@ static void my_application_activate(GApplication* application) { } gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new(); fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); FlView* view = fl_view_new(project); + GdkRGBA background_color; + // Background defaults to black, override it here if necessary, e.g. #00000000 for transparent. + gdk_rgba_parse(&background_color, "#000000"); + fl_view_set_background_color(view, &background_color); gtk_widget_show(GTK_WIDGET(view)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + // Show the window when Flutter renders. + // Requires the view to be realized so we can start rendering. + g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), self); + gtk_widget_realize(GTK_WIDGET(view)); + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); gtk_widget_grab_focus(GTK_WIDGET(view)); @@ -117,6 +131,12 @@ static void my_application_class_init(MyApplicationClass* klass) { static void my_application_init(MyApplication* self) {} MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + return MY_APPLICATION(g_object_new(my_application_get_type(), "application-id", APPLICATION_ID, "flags", G_APPLICATION_NON_UNIQUE, diff --git a/example/linux/my_application.h b/example/linux/runner/my_application.h similarity index 100% rename from example/linux/my_application.h rename to example/linux/runner/my_application.h diff --git a/example/macos/Podfile b/example/macos/Podfile index c795730d..ff5ddb3b 100644 --- a/example/macos/Podfile +++ b/example/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.14' +platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -28,7 +28,6 @@ flutter_macos_podfile_setup target 'Runner' do use_frameworks! - use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 4fc25999..91bbd10b 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -64,7 +64,7 @@ 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* dart_sip_ua_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "dart_sip_ua_example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* dart_sip_ua_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dart_sip_ua_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -461,7 +461,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -478,6 +478,17 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + ENABLE_APP_SANDBOX = YES; + ENABLE_INCOMING_NETWORK_CONNECTIONS = NO; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; + ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = YES; + ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO; + ENABLE_RESOURCE_ACCESS_CALENDARS = NO; + ENABLE_RESOURCE_ACCESS_CAMERA = YES; + ENABLE_RESOURCE_ACCESS_CONTACTS = NO; + ENABLE_RESOURCE_ACCESS_LOCATION = NO; + ENABLE_RESOURCE_ACCESS_PRINTING = NO; + ENABLE_RESOURCE_ACCESS_USB = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -543,7 +554,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -593,7 +604,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -610,6 +621,17 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + ENABLE_APP_SANDBOX = YES; + ENABLE_INCOMING_NETWORK_CONNECTIONS = NO; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; + ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = YES; + ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO; + ENABLE_RESOURCE_ACCESS_CALENDARS = NO; + ENABLE_RESOURCE_ACCESS_CAMERA = YES; + ENABLE_RESOURCE_ACCESS_CONTACTS = NO; + ENABLE_RESOURCE_ACCESS_LOCATION = NO; + ENABLE_RESOURCE_ACCESS_PRINTING = NO; + ENABLE_RESOURCE_ACCESS_USB = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index d7739d4e..041a88dd 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -59,6 +59,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift index d53ef643..b3c17614 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/example/macos/Runner/AppDelegate.swift @@ -1,9 +1,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/example/macos/Runner/Configs/AppInfo.xcconfig index 3f865707..01b99e08 100644 --- a/example/macos/Runner/Configs/AppInfo.xcconfig +++ b/example/macos/Runner/Configs/AppInfo.xcconfig @@ -11,4 +11,4 @@ PRODUCT_NAME = dart_sip_ua_example PRODUCT_BUNDLE_IDENTIFIER = com.github.cloudwebrtc.dartSipUaExample // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2024 com.github.cloudwebrtc. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2025 com.github.cloudwebrtc. All rights reserved. diff --git a/example/macos/Runner/DebugProfile.entitlements b/example/macos/Runner/DebugProfile.entitlements index ab97dbc1..dddb8a30 100644 --- a/example/macos/Runner/DebugProfile.entitlements +++ b/example/macos/Runner/DebugProfile.entitlements @@ -8,11 +8,5 @@ com.apple.security.network.server - com.apple.security.device.camera - - com.apple.security.device.microphone - - com.apple.security.network.client - diff --git a/example/macos/Runner/Release.entitlements b/example/macos/Runner/Release.entitlements index 84c4851e..852fa1a4 100644 --- a/example/macos/Runner/Release.entitlements +++ b/example/macos/Runner/Release.entitlements @@ -4,11 +4,5 @@ com.apple.security.app-sandbox - com.apple.security.device.camera - - com.apple.security.device.microphone - - com.apple.security.network.client - diff --git a/example/pubspec.yaml b/example/pubspec.yaml index c8ac2718..e8679f6f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -26,7 +26,6 @@ dependencies: path: ../ shared_preferences: ^2.2.0 permission_handler: ^11.1.0 - flutter_webrtc: ^0.12.7 provider: 6.1.2 logger: ^2.5.0 diff --git a/example/web/index.html b/example/web/index.html index ea6e27f9..b11d2d84 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -21,7 +21,7 @@ - + diff --git a/example/windows/runner/Runner.rc b/example/windows/runner/Runner.rc index 55598ef5..282226a5 100644 --- a/example/windows/runner/Runner.rc +++ b/example/windows/runner/Runner.rc @@ -93,7 +93,7 @@ BEGIN VALUE "FileDescription", "dart_sip_ua_example" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "dart_sip_ua_example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2024 com.github.cloudwebrtc. All rights reserved." "\0" + VALUE "LegalCopyright", "Copyright (C) 2025 com.github.cloudwebrtc. All rights reserved." "\0" VALUE "OriginalFilename", "dart_sip_ua_example.exe" "\0" VALUE "ProductName", "dart_sip_ua_example" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" diff --git a/example/windows/runner/runner.exe.manifest b/example/windows/runner/runner.exe.manifest index a42ea768..153653e8 100644 --- a/example/windows/runner/runner.exe.manifest +++ b/example/windows/runner/runner.exe.manifest @@ -9,12 +9,6 @@ - - - - - - diff --git a/pubspec.yaml b/pubspec.yaml index aab5514f..16ddc1ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sip_ua -version: 1.0.1 +version: 1.1.0 description: A SIP UA stack for Flutter/Dart, based on flutter-webrtc, support iOS/Android/Destkop/Web. homepage: https://github.com/cloudwebrtc/dart-sip-ua environment: @@ -10,7 +10,7 @@ dependencies: bloc: ^9.0.0 collection: ^1.18.0 crypto: ^3.0.3 - flutter_webrtc: ^0.12.7 + flutter_webrtc: ^1.2.0 intl: ^0.20.1 logger: ^2.0.2+1 path: ^1.6.4