import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_svg/svg.dart'; import 'package:intl/intl.dart'; import 'package:job_app/config/palette.dart'; import 'package:job_app/config/size.dart'; import 'package:job_app/models/meeting.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:job_app/services/webRTC/signaling.dart'; import 'package:job_app/singletons/user_info.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; typedef void StreamStateCallback(MediaStream stream); class Interview extends StatefulWidget { final MeetingModel passedMeeting; Interview({Key key, this.passedMeeting}) : super(key: key); @override _InterviewState createState() => _InterviewState(); } class _InterviewState extends State { MeetingModel meeting ; String imageUrl = "https://server.thejob.net"; final formatDate= new DateFormat('dd/MM/yyyy'); final formatTime = new DateFormat('hh:mm a'); DateTime dateDate ; String date; String time; Map configuration = { 'iceServers': [ { 'urls': [ 'stun:stun.stunprotocol.org:3478', 'stun:stun.l.google.com:19302' , ] } ] }; Map offerSdpConstraints = { "mandatory": { "OfferToReceiveAudio": true, "OfferToReceiveVideo": true, }, "optional": [], }; WebSocketChannel channel; RTCPeerConnection _peerConnection; MediaStream _localStream; RTCVideoRenderer _localRenderer = new RTCVideoRenderer(); RTCVideoRenderer _remoteRenderer = new RTCVideoRenderer(); initRenderer() async { await _localRenderer.initialize(); await _remoteRenderer.initialize(); } _getUserMedia()async{ final Map constraints ={ 'audio': true, 'video':{ 'facingMode':'user', } }; MediaStream stream = await navigator.mediaDevices.getUserMedia(constraints); _localRenderer.srcObject=stream; return stream; } _createPeerConnection()async{ _localStream= await _getUserMedia(); RTCPeerConnection pc = await createPeerConnection(configuration,offerSdpConstraints); pc.addStream(_localStream); pc.onIceCandidate = (e){ if(e.candidate!=null){ channel.sink.add('{"fromuser":"153825","too":"153803","sdp":null,"candidate":"${e.candidate.toString()+"#"+e.sdpMid+"#"+e.sdpMlineIndex.toString()}","type":"ice","data":"1538031640620283243"}'); } }; pc.onIceCandidate =(state){ print(state); }; pc.onAddStream= (stream){ print('addStream: '+stream.id); _remoteRenderer.srcObject=stream; }; return pc; } creatAnswerAndDescription(Map sockRes)async{ Map desc = jsonDecode(sockRes["sdp"]); await _peerConnection.setRemoteDescription( RTCSessionDescription(desc['sdp'], 'offer'), ); var answer = await _peerConnection.createAnswer(); await _peerConnection.setLocalDescription(answer); channel.sink.add('{"fromuser":"${userInfo.user.id}","too":"153803","sdp":${jsonEncode(answer.sdp)},"candidate":null,"type":"answer","data":"1538031640620283243"}'); } setIceCandidates(Map sockRes)async{ String jsonString = sockRes["candidate"]; dynamic session = await jsonDecode(jsonString); _peerConnection.addCandidate( RTCIceCandidate( session['candidate'], session['sdpMid'], session['sdpMLineIndex'], ), ); } @override void initState() { // TODO: implement initState super.initState(); channel = WebSocketChannel.connect( Uri.parse('wss://server.thejob.net/signaling'), ); initRenderer(); } void dispose() { // TODO: implement dispose super.dispose(); } @override Widget build(BuildContext context) { meeting=widget.passedMeeting; dateDate = DateTime.fromMicrosecondsSinceEpoch(meeting.date*1000); date = formatDate.format(dateDate); time = formatTime.format(dateDate); return LayoutBuilder( builder: (context,constraints) { return OrientationBuilder( builder: (context,orientation) { SizeConfig().init(constraints, orientation); return Scaffold( body: SingleChildScrollView( child: StreamBuilder( stream: channel.stream, builder: (context,snapshot) { Map sockRes; if(snapshot.data!=null){ sockRes=jsonDecode(snapshot.data); switch (sockRes["type"]){ case "join" : channel.sink.add('{"fromuser":"${userInfo.user.id}","too":"153803","sdp":null,"candidate":null,"type":"join","data":"1538031640620283243"}'); _createPeerConnection().then((pc){ _peerConnection = pc; }); break; case "new_offer": creatAnswerAndDescription(sockRes); break; case "new_ice": setIceCandidates(sockRes); break; } } return Column( children: [ Padding( padding: EdgeInsets.only( top: SizeConfig.heightMultiplier*3, left: SizeConfig.widthMultiplier*7, right: SizeConfig.widthMultiplier*7, ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only( right: SizeConfig.widthMultiplier*22 ), child: SvgPicture.asset( "assets/icons/arrow_left.svg", color: Colors.black, ), ), Text( "Notifications", style: TextStyle( fontFamily: 'MontserratSemiBold', fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black), ), ], ), SizedBox(height: SizeConfig.heightMultiplier*5,), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( height: SizeConfig.heightMultiplier*5, width: SizeConfig.heightMultiplier*6, decoration: BoxDecoration( border: Border.all(color: Colors.grey.withOpacity(0.1)), borderRadius: BorderRadius.circular(10), image: DecorationImage( fit: BoxFit.fill, image: NetworkImage(imageUrl+meeting.company.image.url), ), ), ), SizedBox(width: SizeConfig.widthMultiplier*1), Text( meeting.company.name, style: TextStyle( fontFamily: 'Montserrat', fontSize: SizeConfig.textMultiplier*1.9 ), ), ], ), Row( children: [ SvgPicture.asset("assets/icons/work_time.svg",height: SizeConfig.heightMultiplier*1.5,), SizedBox( width: SizeConfig.widthMultiplier*2.0, ), Text( "$date / $time" , style: TextStyle( fontFamily: 'MontserratRegular', fontSize: SizeConfig.textMultiplier*1.2, ), ), ], ), ], ), SizedBox(height: SizeConfig.heightMultiplier*2.5,), Row( children: [ Flexible(child: Text(meeting.subject,style: TextStyle( height:SizeConfig.heightMultiplier*0.25, fontFamily: 'MontserratRegular' ),)), ], ), SizedBox(height: SizeConfig.heightMultiplier*5), Text("Client"), Row( children: [ TextButton(onPressed:(){ }, child: Text("c&m")) ], ), Container( width: double.maxFinite, height: 37.9*SizeConfig.heightMultiplier, decoration: BoxDecoration( borderRadius: BorderRadius.circular(13), border: Border.all(color: Palette.primary,width: SizeConfig.heightMultiplier*1.7) ), child: RTCVideoView(_localRenderer) ), SizedBox(height: 50,), Text("Expeditor"), Container( width: double.maxFinite, height: 37.9*SizeConfig.heightMultiplier, decoration: BoxDecoration( borderRadius: BorderRadius.circular(13), border: Border.all(color: Palette.primary,width: SizeConfig.heightMultiplier*1.7) ), child: RTCVideoView( _remoteRenderer) ), SizedBox(height : 5*SizeConfig.heightMultiplier,), Text("connection state : ${snapshot.connectionState}"), Text("Socket data : ${snapshot.data}"), TextButton(onPressed: null, child: Text("test")) ], ), ), // Flexible( // child: Column( // mainAxisAlignment: MainAxisAlignment.end, // children: [ // Container( // height: SizeConfig.heightMultiplier*20, // width: double.maxFinite, // child: Image( // fit: BoxFit.fitWidth, // image: AssetImage('assets/images/notification_ad.png'), // ), // ), // ], // ), // ), ], ); } ), ), ); } ); } ); } }