From 43a4fe869c4bc73050e3858cacc691f448fc69a7 Mon Sep 17 00:00:00 2001 From: ftsef <131373548+ftsef@users.noreply.github.com> Date: Sun, 25 Jun 2023 09:30:16 +0200 Subject: [PATCH 1/7] expose rtc_session to/from-tag (#378) Co-authored-by: Fabian Fest --- lib/src/rtc_session.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index cef21ac5..95529750 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -88,7 +88,9 @@ class RTCSession extends EventManager implements Owner { final Map _earlyDialogs = {}; String? _contact; String? _from_tag; + String? get from_tag => _from_tag; String? _to_tag; + String? get to_tag => _to_tag; // The RTCPeerConnection instance (public attribute). RTCPeerConnection? _connection; From f8aa84aa7900ec390f125176a34a096a33fbb43d Mon Sep 17 00:00:00 2001 From: Matthias Schicker Date: Sun, 25 Jun 2023 09:30:53 +0200 Subject: [PATCH 2/7] =?UTF-8?q?#376:=20Added=20`=5Fsettings.session=5Ftime?= =?UTF-8?q?rs=5Frefresh=5Fmethod`=20to=20UaSettings=20t=E2=80=A6=20(#377)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #376: Added `_settings.session_timers_refresh_method` to UaSettings to expose this feature for users of SipUaHelper * Reformatted sip_ua_helper to get build pipeline to run --------- Co-authored-by: Matthias Schicker --- lib/src/sip_ua_helper.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index 0de7c63d..aaf6e56f 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -139,6 +139,8 @@ class SIPUAHelper extends EventManager { _settings.dtmf_mode = uaSettings.dtmfMode; _settings.session_timers = uaSettings.sessionTimers; _settings.ice_gathering_timeout = uaSettings.iceGatheringTimeout; + _settings.session_timers_refresh_method = + uaSettings.sessionTimersRefreshMethod; try { _ua = UA(_settings); @@ -728,4 +730,9 @@ class UaSettings { // 'credential': 'change_to_real_secret' // }, ]; + + /// 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; } From a183f1babd74d522b5441a48622d06b2beea9510 Mon Sep 17 00:00:00 2001 From: CloudWebRTC Date: Tue, 22 Aug 2023 20:15:45 -0500 Subject: [PATCH 3/7] Update dart.yml --- .github/workflows/dart.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 57c33126..ffb5fb69 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -22,7 +22,6 @@ jobs: java-version: '12.x' - uses: subosito/flutter-action@v1 with: - flutter-version: '3.7.3' channel: 'stable' - run: flutter packages get - run: dart format lib/ test/ --set-exit-if-changed From f1f8bb82821135783e96e589551f51bad1ea09e6 Mon Sep 17 00:00:00 2001 From: Victor Uvarov <35702719+VictorUvarov@users.noreply.github.com> Date: Tue, 22 Aug 2023 18:18:33 -0700 Subject: [PATCH 4/7] expose instance_id (#395) --- lib/src/sip_ua_helper.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index aaf6e56f..dc0994be 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -141,6 +141,7 @@ class SIPUAHelper extends EventManager { _settings.ice_gathering_timeout = uaSettings.iceGatheringTimeout; _settings.session_timers_refresh_method = uaSettings.sessionTimersRefreshMethod; + _settings.instance_id = uaSettings.instanceId; try { _ua = UA(_settings); @@ -709,6 +710,7 @@ class UaSettings { String? password; String? ha1; String? displayName; + String? instanceId; /// DTMF mode, in band (rfc2833) or out of band (sip info) DtmfMode dtmfMode = DtmfMode.INFO; From 6a87d71e7f8bc778d88c42daa9707824337f9ae2 Mon Sep 17 00:00:00 2001 From: Victor Uvarov <35702719+VictorUvarov@users.noreply.github.com> Date: Tue, 22 Aug 2023 18:20:22 -0700 Subject: [PATCH 5/7] Example app cleanup (#394) * Example app cleanup - mirror local video when front facing camera - run dart format - update dependencies * try a newer version of stable flutter --------- Co-authored-by: CloudWebRTC --- example/lib/main.dart | 13 +- example/lib/src/about.dart | 28 +-- example/lib/src/callscreen.dart | 152 +++++++----- example/lib/src/dialpad.dart | 276 +++++++++++---------- example/lib/src/register.dart | 248 +++++++----------- example/lib/src/widgets/action_button.dart | 2 +- example/pubspec.yaml | 11 +- 7 files changed, 350 insertions(+), 380 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index f21e9260..e1c9b8e5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:sip_ua/sip_ua.dart'; - import 'src/about.dart'; import 'src/callscreen.dart'; import 'src/dialpad.dart'; @@ -57,6 +56,18 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, fontFamily: 'Roboto', + inputDecorationTheme: InputDecorationTheme( + hintStyle: TextStyle(color: Colors.grey), + contentPadding: EdgeInsets.all(10.0), + border: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.black12)), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.all(16), + textStyle: TextStyle(fontSize: 18), + ), + ), ), initialRoute: '/', onGenerateRoute: _onGenerateRoute, diff --git a/example/lib/src/about.dart b/example/lib/src/about.dart index 7109cdf8..f4eacedc 100644 --- a/example/lib/src/about.dart +++ b/example/lib/src/about.dart @@ -4,21 +4,21 @@ class AboutWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text("About"), - ), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(18.0), - child: Center( - child: Column( - children: [ - Text( - 'GitHub:\nhttps://github.com/cloudwebrtc/dart-sip-ua.git') - ], - ), + appBar: AppBar( + title: Text("About"), + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(18.0), + child: Center( + child: Column( + children: [ + Text('GitHub:\nhttps://github.com/cloudwebrtc/dart-sip-ua.git'), + ], ), ), - )); + ), + ), + ); } } diff --git a/example/lib/src/callscreen.dart b/example/lib/src/callscreen.dart index 0bfa394b..1777efe3 100644 --- a/example/lib/src/callscreen.dart +++ b/example/lib/src/callscreen.dart @@ -10,9 +10,11 @@ import 'widgets/action_button.dart'; class CallScreenWidget extends StatefulWidget { final SIPUAHelper? _helper; final Call? _call; + CallScreenWidget(this._helper, this._call, {Key? key}) : super(key: key); + @override - _MyCallScreenWidget createState() => _MyCallScreenWidget(); + State createState() => _MyCallScreenWidget(); } class _MyCallScreenWidget extends State @@ -27,13 +29,17 @@ class _MyCallScreenWidget extends State bool _showNumPad = false; String _timeLabel = '00:00'; - late Timer _timer; bool _audioMuted = false; bool _videoMuted = false; bool _speakerOn = false; bool _hold = false; + bool _mirror = true; String? _holdOriginator; CallStateEnum _state = CallStateEnum.NONE; + + late String _transferTarget; + late Timer _timer; + SIPUAHelper? get helper => widget._helper; bool get voiceOnly => @@ -237,6 +243,9 @@ class _MyCallScreenWidget extends State void _switchCamera() { if (_localStream != null) { Helper.switchCamera(_localStream!.getVideoTracks()[0]); + setState(() { + _mirror = !_mirror; + }); } } @@ -264,7 +273,6 @@ class _MyCallScreenWidget extends State } } - late String _transferTarget; void _handleTransfer() { showDialog( context: context, @@ -324,7 +332,7 @@ class _MyCallScreenWidget extends State } List _buildNumPad() { - var labels = [ + final labels = [ [ {'1': ''}, {'2': 'abc'}, @@ -364,22 +372,22 @@ class _MyCallScreenWidget extends State } Widget _buildActionButtons() { - var hangupBtn = ActionButton( + final hangupBtn = ActionButton( title: "hangup", onPressed: () => _handleHangup(), icon: Icons.call_end, fillColor: Colors.red, ); - var hangupBtnInactive = ActionButton( + final hangupBtnInactive = ActionButton( title: "hangup", onPressed: () {}, icon: Icons.call_end, fillColor: Colors.grey, ); - var basicActions = []; - var advanceActions = []; + final basicActions = []; + final advanceActions = []; switch (_state) { case CallStateEnum.NONE: @@ -472,67 +480,83 @@ class _MyCallScreenWidget extends State break; } - var actionWidgets = []; + final actionWidgets = []; if (_showNumPad) { actionWidgets.addAll(_buildNumPad()); } else { if (advanceActions.isNotEmpty) { - actionWidgets.add(Padding( + actionWidgets.add( + Padding( padding: const EdgeInsets.all(3), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: advanceActions))); + children: advanceActions), + ), + ); } } - actionWidgets.add(Padding( + actionWidgets.add( + Padding( padding: const EdgeInsets.all(3), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: basicActions))); + children: basicActions), + ), + ); return Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: actionWidgets); + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: actionWidgets, + ); } Widget _buildContent() { - var stackWidgets = []; + final stackWidgets = []; if (!voiceOnly && _remoteStream != null) { - stackWidgets.add(Center( - child: RTCVideoView(_remoteRenderer!), - )); + stackWidgets.add( + Center( + child: RTCVideoView( + _remoteRenderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, + ), + ), + ); } if (!voiceOnly && _localStream != null) { - stackWidgets.add(Container( - child: AnimatedContainer( - child: RTCVideoView(_localRenderer!), + stackWidgets.add( + AnimatedContainer( + child: RTCVideoView( + _localRenderer!, + mirror: _mirror, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, + ), height: _localVideoHeight, width: _localVideoWidth, alignment: Alignment.topRight, duration: Duration(milliseconds: 300), margin: _localVideoMargin, ), - alignment: Alignment.topRight, - )); + ); } - stackWidgets.addAll([ - Positioned( - top: voiceOnly ? 48 : 6, - left: 0, - right: 0, - child: Center( + stackWidgets.addAll( + [ + Positioned( + top: voiceOnly ? 48 : 6, + left: 0, + right: 0, + child: Center( child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Center( - child: Padding( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: Padding( padding: const EdgeInsets.all(6), child: Text( (voiceOnly ? 'VOICE CALL' : 'VIDEO CALL') + @@ -540,23 +564,33 @@ class _MyCallScreenWidget extends State ? ' PAUSED BY ${_holdOriginator!.toUpperCase()}' : ''), style: TextStyle(fontSize: 24, color: Colors.black54), - ))), - Center( - child: Padding( + ), + ), + ), + Center( + child: Padding( padding: const EdgeInsets.all(6), child: Text( '$remoteIdentity', style: TextStyle(fontSize: 18, color: Colors.black54), - ))), - Center( - child: Padding( + ), + ), + ), + Center( + child: Padding( padding: const EdgeInsets.all(6), - child: Text(_timeLabel, - style: TextStyle(fontSize: 14, color: Colors.black54)))) - ], - )), - ), - ]); + child: Text( + _timeLabel, + style: TextStyle(fontSize: 14, color: Colors.black54), + ), + ), + ) + ], + ), + ), + ), + ], + ); return Stack( children: stackWidgets, @@ -566,16 +600,18 @@ class _MyCallScreenWidget extends State @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - title: Text('[$direction] ${EnumHelper.getName(_state)}')), - body: Container( - child: _buildContent(), - ), - floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - floatingActionButton: Padding( - padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 24.0), - child: Container(width: 320, child: _buildActionButtons()))); + appBar: AppBar( + automaticallyImplyLeading: false, + title: Text('[$direction] ${EnumHelper.getName(_state)}'), + ), + body: _buildContent(), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + floatingActionButton: Container( + width: 320, + padding: EdgeInsets.only(bottom: 24.0), + child: _buildActionButtons(), + ), + ); } @override diff --git a/example/lib/src/dialpad.dart b/example/lib/src/dialpad.dart index 3eeecbfe..5a27b879 100644 --- a/example/lib/src/dialpad.dart +++ b/example/lib/src/dialpad.dart @@ -9,9 +9,11 @@ import 'widgets/action_button.dart'; class DialPadWidget extends StatefulWidget { final SIPUAHelper? _helper; + DialPadWidget(this._helper, {Key? key}) : super(key: key); + @override - _MyDialPadWidget createState() => _MyDialPadWidget(); + State createState() => _MyDialPadWidget(); } class _MyDialPadWidget extends State @@ -46,7 +48,7 @@ class _MyDialPadWidget extends State Future _handleCall(BuildContext context, [bool voiceOnly = false]) async { - var dest = _textController?.text; + final dest = _textController?.text; if (defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS) { await Permission.microphone.request(); @@ -74,7 +76,14 @@ class _MyDialPadWidget extends State return null; } - final mediaConstraints = {'audio': true, 'video': true}; + final mediaConstraints = { + 'audio': true, + 'video': { + 'width': '1280', + 'height': '720', + 'facingMode': 'user', + } + }; MediaStream mediaStream; @@ -84,9 +93,14 @@ class _MyDialPadWidget extends State mediaConstraints['video'] = false; MediaStream userStream = await navigator.mediaDevices.getUserMedia(mediaConstraints); - mediaStream.addTrack(userStream.getAudioTracks()[0], addToNative: true); + final audioTracks = userStream.getAudioTracks(); + if (audioTracks.isNotEmpty) { + mediaStream.addTrack(audioTracks.first, addToNative: true); + } } else { - mediaConstraints['video'] = !voiceOnly; + if (voiceOnly) { + mediaConstraints['video'] = !voiceOnly; + } mediaStream = await navigator.mediaDevices.getUserMedia(mediaConstraints); } @@ -112,7 +126,7 @@ class _MyDialPadWidget extends State } List _buildNumPad() { - var labels = [ + final labels = [ [ {'1': ''}, {'2': 'abc'}, @@ -153,145 +167,137 @@ class _MyDialPadWidget extends State List _buildDialPad() { return [ + Align( + alignment: AlignmentDirectional.centerStart, + child: Text('Destination URL'), + ), + const SizedBox(height: 8), Container( - width: 360, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 360, - child: TextField( - keyboardType: TextInputType.text, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 24, color: Colors.black54), - decoration: InputDecoration( - border: InputBorder.none, - ), - controller: _textController, - )), - ])), + width: 500, + child: TextField( + keyboardType: TextInputType.text, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18, color: Colors.black54), + maxLines: 3, + decoration: InputDecoration( + border: OutlineInputBorder(), + ), + controller: _textController, + ), + ), Container( - width: 300, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildNumPad())), + width: 500, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildNumPad(), + ), + ), Container( - width: 300, - child: Padding( - padding: const EdgeInsets.all(12), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ActionButton( - icon: Icons.videocam, - onPressed: () => _handleCall(context), - ), - ActionButton( - icon: Icons.dialer_sip, - fillColor: Colors.green, - onPressed: () => _handleCall(context, true), - ), - ActionButton( - icon: Icons.keyboard_arrow_left, - onPressed: () => _handleBackSpace(), - onLongPress: () => _handleBackSpace(true), - ), - ], - ))) + width: 500, + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ActionButton( + icon: Icons.videocam, + onPressed: () => _handleCall(context), + ), + ActionButton( + icon: Icons.dialer_sip, + fillColor: Colors.green, + onPressed: () => _handleCall(context, true), + ), + ActionButton( + icon: Icons.keyboard_arrow_left, + onPressed: () => _handleBackSpace(), + onLongPress: () => _handleBackSpace(true), + ), + ], + ), + ), + ), ]; } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text("Dart SIP UA Demo"), - actions: [ - PopupMenuButton( - onSelected: (String value) { - switch (value) { - case 'account': - Navigator.pushNamed(context, '/register'); - break; - case 'about': - Navigator.pushNamed(context, '/about'); - break; - default: - break; - } - }, - icon: Icon(Icons.menu), - itemBuilder: (BuildContext context) => >[ - PopupMenuItem( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - child: Icon( - Icons.account_circle, - color: Colors.black38, - ), - ), - SizedBox( - child: Text('Account'), - width: 64, - ) - ], - ), - value: 'account', + appBar: AppBar( + title: Text("Dart SIP UA Demo"), + actions: [ + PopupMenuButton( + onSelected: (String value) { + switch (value) { + case 'account': + Navigator.pushNamed(context, '/register'); + break; + case 'about': + Navigator.pushNamed(context, '/about'); + break; + default: + break; + } + }, + icon: Icon(Icons.menu), + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + child: Row( + children: [ + Icon( + Icons.account_circle, + color: Colors.black54, + ), + SizedBox(width: 12), + Text('Account'), + ], ), - PopupMenuItem( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Icon( - Icons.info, - color: Colors.black38, - ), - SizedBox( - child: Text('About'), - width: 64, - ) - ], - ), - value: 'about', - ) - ]), - ], - ), - body: Align( - alignment: Alignment(0, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.all(6.0), - child: Center( - child: Text( - 'Status: ${EnumHelper.getName(helper!.registerState.state)}', - style: TextStyle(fontSize: 14, color: Colors.black54), - )), - ), - Padding( - padding: const EdgeInsets.all(6.0), - child: Center( - child: Text( - 'Received Message: $receivedMsg', - style: TextStyle(fontSize: 14, color: Colors.black54), - )), - ), - Container( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildDialPad(), - )), - ]))); + value: 'account', + ), + PopupMenuItem( + child: Row( + children: [ + Icon( + Icons.info, + color: Colors.black54, + ), + SizedBox(width: 12), + Text('About'), + ], + ), + value: 'about', + ) + ]), + ], + ), + body: ListView( + padding: EdgeInsets.symmetric(horizontal: 12), + children: [ + SizedBox(height: 20), + Center( + child: Text( + 'Register Status: ${EnumHelper.getName(helper!.registerState.state)}', + style: TextStyle(fontSize: 18, color: Colors.black54), + ), + ), + SizedBox(height: 12), + Center( + child: Text( + 'Received Message: $receivedMsg', + style: TextStyle(fontSize: 16, color: Colors.black54), + ), + ), + SizedBox(height: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildDialPad(), + ), + ], + ), + ); } @override diff --git a/example/lib/src/register.dart b/example/lib/src/register.dart index 91f5dc5e..62c6de7c 100644 --- a/example/lib/src/register.dart +++ b/example/lib/src/register.dart @@ -4,9 +4,11 @@ import 'package:sip_ua/sip_ua.dart'; class RegisterWidget extends StatefulWidget { final SIPUAHelper? _helper; + RegisterWidget(this._helper, {Key? key}) : super(key: key); + @override - _MyRegisterWidget createState() => _MyRegisterWidget(); + State createState() => _MyRegisterWidget(); } class _MyRegisterWidget extends State @@ -27,7 +29,7 @@ class _MyRegisterWidget extends State SIPUAHelper? get helper => widget._helper; @override - initState() { + void initState() { super.initState(); _registerState = helper!.registerState; helper!.addSipUaHelperListener(this); @@ -35,7 +37,17 @@ class _MyRegisterWidget extends State } @override - deactivate() { + void dispose() { + _passwordController.dispose(); + _wsUriController.dispose(); + _sipUriController.dispose(); + _displayNameController.dispose(); + _authorizationUserController.dispose(); + super.dispose(); + } + + @override + void deactivate() { super.deactivate(); helper!.removeSipUaHelperListener(this); _saveSettings(); @@ -119,167 +131,75 @@ class _MyRegisterWidget extends State @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text("SIP Account"), - ), - body: Align( - alignment: Alignment(0, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Column( - children: [ - Padding( - padding: - const EdgeInsets.fromLTRB(48.0, 18.0, 48.0, 18.0), - child: Center( - child: Text( - 'Register Status: ${EnumHelper.getName(_registerState.state)}', - style: TextStyle(fontSize: 18, color: Colors.black54), - )), - ), - Padding( - padding: const EdgeInsets.fromLTRB(48.0, 18.0, 48.0, 0), - child: Align( - child: Text('WebSocket:'), - alignment: Alignment.centerLeft, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(48.0, 0.0, 48.0, 0), - child: TextFormField( - controller: _wsUriController, - keyboardType: TextInputType.text, - textAlign: TextAlign.center, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(10.0), - border: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.black12)), - ), - ), - ), - ], - ), - Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(46.0, 18.0, 48.0, 0), - child: Align( - child: Text('SIP URI:'), - alignment: Alignment.centerLeft, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(48.0, 0.0, 48.0, 0), - child: TextFormField( - controller: _sipUriController, - keyboardType: TextInputType.text, - textAlign: TextAlign.center, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(10.0), - border: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.black12)), - ), - ), - ), - ], - ), - Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(46.0, 18.0, 48.0, 0), - child: Align( - child: Text('Authorization User:'), - alignment: Alignment.centerLeft, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(48.0, 0.0, 48.0, 0), - child: TextFormField( - controller: _authorizationUserController, - keyboardType: TextInputType.text, - textAlign: TextAlign.center, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(10.0), - border: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.black12)), - hintText: _authorizationUserController.text.isEmpty - ? '[Empty]' - : null, - ), - ), - ), - ], - ), - Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(46.0, 18.0, 48.0, 0), - child: Align( - child: Text('Password:'), - alignment: Alignment.centerLeft, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(48.0, 0.0, 48.0, 0), - child: TextFormField( - controller: _passwordController, - keyboardType: TextInputType.text, - textAlign: TextAlign.center, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(10.0), - border: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.black12)), - hintText: _passwordController.text.isEmpty - ? '[Empty]' - : null, - ), - ), - ), - ], - ), - Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(46.0, 18.0, 48.0, 0), - child: Align( - child: Text('Display Name:'), - alignment: Alignment.centerLeft, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(48.0, 0.0, 48.0, 0), - child: TextFormField( - controller: _displayNameController, - keyboardType: TextInputType.text, - textAlign: TextAlign.center, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(10.0), - border: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.black12)), - ), - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.fromLTRB(0.0, 18.0, 0.0, 0.0), - child: Container( - height: 48.0, - width: 160.0, - child: MaterialButton( - child: Text( - 'Register', - style: - TextStyle(fontSize: 16.0, color: Colors.white), - ), - color: Colors.blue, - textColor: Colors.white, - onPressed: () => _handleSave(context), - ), - )) - ]))); + appBar: AppBar( + title: Text("SIP Account"), + ), + body: ListView( + padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 20), + children: [ + Center( + child: Text( + 'Register Status: ${EnumHelper.getName(_registerState.state)}', + style: TextStyle(fontSize: 18, color: Colors.black54), + ), + ), + SizedBox(height: 40), + Text('WebSocket:'), + TextFormField( + controller: _wsUriController, + keyboardType: TextInputType.text, + autocorrect: false, + textAlign: TextAlign.center, + ), + SizedBox(height: 20), + Text('SIP URI:'), + TextFormField( + controller: _sipUriController, + keyboardType: TextInputType.text, + autocorrect: false, + textAlign: TextAlign.center, + ), + SizedBox(height: 20), + Text('Authorization User:'), + TextFormField( + controller: _authorizationUserController, + keyboardType: TextInputType.text, + autocorrect: false, + textAlign: TextAlign.center, + decoration: InputDecoration( + hintText: + _authorizationUserController.text.isEmpty ? '[Empty]' : null, + ), + ), + SizedBox(height: 20), + Text('Password:'), + TextFormField( + controller: _passwordController, + keyboardType: TextInputType.text, + autocorrect: false, + textAlign: TextAlign.center, + decoration: InputDecoration( + hintText: _passwordController.text.isEmpty ? '[Empty]' : null, + ), + ), + SizedBox(height: 20), + Text('Display Name:'), + TextFormField( + controller: _displayNameController, + keyboardType: TextInputType.text, + textAlign: TextAlign.center, + decoration: InputDecoration( + hintText: _displayNameController.text.isEmpty ? '[Empty]' : null, + ), + ), + const SizedBox(height: 40), + ElevatedButton( + child: Text('Register'), + onPressed: () => _handleSave(context), + ), + ], + ), + ); } @override diff --git a/example/lib/src/widgets/action_button.dart b/example/lib/src/widgets/action_button.dart index ca2bf08f..d2a2cec3 100644 --- a/example/lib/src/widgets/action_button.dart +++ b/example/lib/src/widgets/action_button.dart @@ -23,7 +23,7 @@ class ActionButton extends StatefulWidget { : super(key: key); @override - _ActionButtonState createState() => _ActionButtonState(); + State createState() => _ActionButtonState(); } class _ActionButtonState extends State { diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e94de160..611b9d42 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -21,19 +21,16 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 sip_ua: path: ../ - shared_preferences: ^2.0.5 - permission_handler: ^9.2.0 + shared_preferences: ^2.2.0 + permission_handler: ^10.4.3 + flutter_webrtc: ^0.9.40 dev_dependencies: flutter_test: sdk: flutter - lints: ^1.0.1 + lints: ^2.1.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 4a073aae2af4e85c33b2fcddba9f8980dfe59416 Mon Sep 17 00:00:00 2001 From: CloudWebRTC Date: Wed, 23 Aug 2023 08:43:51 -0500 Subject: [PATCH 6/7] fix: parse expires from string header. (#396) * fix: parse expires from string header. * update. --- lib/src/rtc_session.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/src/rtc_session.dart b/lib/src/rtc_session.dart index 95529750..49d4d152 100644 --- a/lib/src/rtc_session.dart +++ b/lib/src/rtc_session.dart @@ -362,7 +362,15 @@ class RTCSession extends EventManager implements Owner { // Get the Expires header value if exists. if (request.hasHeader('expires')) { - expires = request.getHeader('expires') * 1000; + try { + expires = (request.getHeader('expires') is num + ? request.getHeader('expires') + : num.tryParse(request.getHeader('expires'))!) * + 1000; + } catch (e) { + logger.e( + 'Invalid Expires header value: ${request.getHeader('expires')}, error $e'); + } } /* Set the to_tag before From 0d7fb500753a5b78acaaeb7494d63ddd9db19298 Mon Sep 17 00:00:00 2001 From: Victor Uvarov <35702719+VictorUvarov@users.noreply.github.com> Date: Wed, 13 Sep 2023 07:20:14 -0700 Subject: [PATCH 7/7] add support for UAConfiguration registrar_server (#397) - https://github.com/versatica/JsSIP/blob/004d160968ec71982beca46d021e3cab746ac55d/lib/UA.d.ts#L43C3-L43C19 --- lib/src/sip_ua_helper.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/sip_ua_helper.dart b/lib/src/sip_ua_helper.dart index dc0994be..f5980a66 100644 --- a/lib/src/sip_ua_helper.dart +++ b/lib/src/sip_ua_helper.dart @@ -142,6 +142,7 @@ class SIPUAHelper extends EventManager { _settings.session_timers_refresh_method = uaSettings.sessionTimersRefreshMethod; _settings.instance_id = uaSettings.instanceId; + _settings.registrar_server = uaSettings.registrarServer; try { _ua = UA(_settings); @@ -711,6 +712,7 @@ class UaSettings { String? ha1; String? displayName; String? instanceId; + String? registrarServer; /// DTMF mode, in band (rfc2833) or out of band (sip info) DtmfMode dtmfMode = DtmfMode.INFO;