diff --git a/lib/classes/notifications.dart b/lib/classes/notifications.dart index 69264a7..30d90f5 100644 --- a/lib/classes/notifications.dart +++ b/lib/classes/notifications.dart @@ -10,6 +10,7 @@ import 'package:huawei_push/huawei_push.dart' as huawei_push; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/classes/voip_chat_call.dart'; +import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:uuid/uuid.dart'; @@ -116,10 +117,18 @@ class AppNotifications { void _handleMessage(RemoteMessage message) { Utils.saveStringFromPrefs("isAppOpendByChat", "false"); + logger.d("Onlyyyyy Msg"); + + if (message.data.isNotEmpty && message.data["messageType"] == 'chat') { + Utils.saveStringFromPrefs("isAppOpendByChat", "true"); + Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString()); + } else if (message.data.isNotEmpty && message.data["messageType"] == 'call') { + ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message); + } } void _handleOpenApp(RemoteMessage message) { - if (message.data.isNotEmpty && message.data["type"] == 'chat') { + if (message.data.isNotEmpty && message.data["messageType"] == 'chat') { Utils.saveStringFromPrefs("isAppOpendByChat", "true"); Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString()); } @@ -134,9 +143,10 @@ AndroidNotificationChannel channel = const AndroidNotificationChannel( Future backgroundMessageHandler(RemoteMessage message) async { await Firebase.initializeApp(); - Utils.saveStringFromPrefs("isAppOpendByChat", "false"); - Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString()); - if (message.data.isNotEmpty && message.data["type"] == 'call') { + if (message.data.isNotEmpty && message.data["messageType"] == 'chat') { + Utils.saveStringFromPrefs("isAppOpendByChat", "false"); + Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString()); + } else if (message.data.isNotEmpty && message.data["messageType"] == 'call') { ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message); } } diff --git a/lib/classes/voip_chat_call.dart b/lib/classes/voip_chat_call.dart index f591f1c..5454eff 100644 --- a/lib/classes/voip_chat_call.dart +++ b/lib/classes/voip_chat_call.dart @@ -6,9 +6,15 @@ import 'package:flutter_callkit_incoming/entities/entities.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/call.dart'; import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as ALM; +import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart'; +import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; +import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; +import 'package:provider/provider.dart'; class ChatVoipCall { static final ChatVoipCall _instance = ChatVoipCall._internal(); @@ -17,58 +23,110 @@ class ChatVoipCall { factory ChatVoipCall() => _instance; - Future showCallkitIncoming({required String uuid, required RemoteMessage data}) async { + late ChatProviderModel chatProvider; + late ChatCallProvider callProvider; + dynamic inCallData; + + Future showCallkitIncoming({required String uuid, RemoteMessage? data, bool isOnline = false, CallDataModel? incomingCallData}) async { await ChatVoipCall().listenerEvent(); - SingleUserChatModel callerData = SingleUserChatModel.fromJson(jsonDecode(data.data["user_chat_history_response"])); - ALM.Response autoLoginData = ALM.Response.fromJson(jsonDecode(data.data["user_token_response"])); - var values = { - "loginDetails": autoLoginData.toJson(), - "callerDetails": callerData.toJson(), - }; - CallKitParams params = CallKitParams( - id: uuid, - nameCaller: callerData.targetUserName, - appName: 'Mohemm', - handle: '', - type: 0, - duration: 25000, - textAccept: 'Accept', - textDecline: 'Decline', - textMissedCall: 'Missed call', - textCallback: 'Call back', - extra: values, - android: const AndroidParams( - isCustomNotification: true, - isShowLogo: true, - isShowCallback: false, - isShowMissedCallNotification: true, - ringtonePath: 'system_ringtone_default', - backgroundColor: '#0955fa', - backgroundUrl: 'assets/test.png', - actionColor: '#4CAF50', - ), - ios: IOSParams( - iconName: 'Mohemm', - handleType: '', - supportsVideo: true, - maximumCallGroups: 2, - maximumCallsPerCallGroup: 1, - audioSessionMode: 'default', - audioSessionActive: true, - audioSessionPreferredSampleRate: 38000.0, - audioSessionPreferredIOBufferDuration: 0.005, - supportsDTMF: true, - supportsHolding: true, - supportsGrouping: false, - supportsUngrouping: false, - ringtonePath: 'system_ringtone_default', - ), - ); - if (callerData.chatEventId == 3) { - await Utils.saveStringFromPrefs("isIncomingCall", "true"); - await Utils.saveStringFromPrefs("inComingCallData",jsonEncode(values)); - await FlutterCallkitIncoming.showCallkitIncoming(params); - } + if (isOnline) { + // if (incomingCallData.chatEventId == 3) { + // await Utils.saveStringFromPrefs("isIncomingCall", "true"); + // await Utils.saveStringFromPrefs( + // "inComingCallData", + // jsonEncode([ + // { + // "loginDetails": autoLoginData.toJson(), + // "callerDetails": callerData.toJson(), + // } + // ])); + // await FlutterCallkitIncoming.showCallkitIncoming(params); + // } + } else { + await initProviders(); + SingleUserChatModel callerData = SingleUserChatModel.fromJson(jsonDecode(data!.data["user_chat_history_response"])); + ALM.Response autoLoginData = ALM.Response.fromJson(jsonDecode(data.data["user_token_response"])); + CallKitParams params = CallKitParams( + id: uuid, + nameCaller: callerData.targetUserName, + appName: 'Mohemm', + handle: '', + type: 0, + duration: 25000, + textAccept: 'Accept', + textDecline: 'Decline', + textMissedCall: 'Missed call', + textCallback: 'Call back', + extra: { + "loginDetails": autoLoginData.toJson(), + "callerDetails": callerData.toJson(), + }, + android: const AndroidParams( + isCustomNotification: true, + isShowLogo: false, + isShowCallback: false, + isShowMissedCallNotification: true, + ringtonePath: 'system_ringtone_default', + backgroundColor: '#0955fa', + backgroundUrl: 'assets/test.png', + actionColor: '#4CAF50', + ), + ios: IOSParams( + iconName: 'Mohemm', + handleType: '', + supportsVideo: true, + maximumCallGroups: 2, + maximumCallsPerCallGroup: 1, + audioSessionMode: 'default', + audioSessionActive: true, + audioSessionPreferredSampleRate: 38000.0, + audioSessionPreferredIOBufferDuration: 0.005, + supportsDTMF: true, + supportsHolding: true, + supportsGrouping: false, + supportsUngrouping: false, + ringtonePath: 'system_ringtone_default', + ), + ); + if (callerData.chatEventId == 3) { + await Utils.saveStringFromPrefs("isIncomingCall", "true"); + // await Utils.saveStringFromPrefs( + // "inComingCallData", + // jsonEncode([ + // { + // "loginDetails": autoLoginData.toJson(), + // "callerDetails": callerData.toJson(), + // } + // ])); + connection( + data: jsonEncode([ + {"loginDetails": autoLoginData.toJson(), "callerDetails": callerData.toJson()} + ])); + await FlutterCallkitIncoming.showCallkitIncoming(params); + } + } + } + + Future initProviders() async { + callProvider = Provider.of(AppRoutes.navigatorKey.currentContext!, listen: false); + chatProvider = Provider.of(AppRoutes.navigatorKey.currentContext!, listen: false); + } + + void connection({required data}) async { + // inCallData = await Utils.getStringFromPrefs("inComingCallData"); + dynamic callData = await jsonDecode(data); + AppState().setchatUserDetails = UserAutoLoginModel( + response: Response.fromJson(callData[0]["loginDetails"]), + errorResponses: null, + ); + callProvider.startIncomingCallViaKit(inCallData: inCallData); + try { + await chatProvider.buildHubConnection(context: AppRoutes.navigatorKey.currentContext!, ccProvider: callProvider).whenComplete(() { + callProvider.init(); + }); + } catch (e) { + logger.w(e); + } } //Function(CallEvent) callback @@ -84,17 +142,18 @@ class ChatVoipCall { // TODO: show screen calling in Flutter break; case Event.ACTION_CALL_ACCEPT: - + Navigator.pushNamedAndRemoveUntil(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall, (_) => false); + // Navigator.pushNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall); break; case Event.ACTION_CALL_DECLINE: + callProvider.isIncomingCall = true; Utils.saveStringFromPrefs("isIncomingCall", "false"); Utils.saveStringFromPrefs("inComingCallData", "null"); + callProvider.endCall(); break; case Event.ACTION_CALL_ENDED: Utils.saveStringFromPrefs("isIncomingCall", "false"); Utils.saveStringFromPrefs("inComingCallData", "null"); - - // TODO: ended an incoming/outgoing call break; case Event.ACTION_CALL_TIMEOUT: Utils.saveStringFromPrefs("isIncomingCall", "false"); diff --git a/lib/provider/chat_call_provider.dart b/lib/provider/chat_call_provider.dart index 53def54..89a23fb 100644 --- a/lib/provider/chat_call_provider.dart +++ b/lib/provider/chat_call_provider.dart @@ -7,12 +7,12 @@ import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:just_audio/just_audio.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/classes/voip_chat_call.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/call.dart'; -import 'package:mohem_flutter_app/models/chat/incomingCall.dart'; +import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/webrtc_payloads.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; -import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart'; import 'package:mohem_flutter_app/ui/chat/call/start_call_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:uuid/uuid.dart'; @@ -27,7 +27,6 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { final AudioPlayer player = AudioPlayer(); late MediaStream localStream; late CallDataModel outGoingCallData; - late IncomingCallDataPayload inComingCallData; bool isMicOff = false; bool isLoudSpeaker = false; bool isCamOff = false; @@ -36,10 +35,12 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { bool isCallStarted = false; bool isFrontCamera = true; bool isOnIncomingCallPage = false; + late SingleUserChatModel incomingCallData; /// WebRTC Connection Variables - bool _offer = false; + //bool _offer = false; bool isIncomingCallLoader = true; + bool isIncomingCall = false; late BuildContext providerContext; /// Temp Variable Remove After Development @@ -90,7 +91,10 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { }; Future init() async { - creatOfferWithCon(); + _pc = await creatOfferWithCon(); + Future.delayed(const Duration(seconds: 2), () { + connectIncomingCall(); + }); } Future initLocalCamera({required ChatProviderModel chatProvmodel, required callData, required BuildContext context, bool isIncomingCall = false}) async { @@ -131,19 +135,34 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { } Future endCall() async { - await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1); - await invoke(invokeMethod: "HangUpAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1); - _pc.dispose(); - isCallStarted = false; - isVideoCall = false; - isCamOff = false; - isMicOff = false; - isLoudSpeaker = false; - localVideoRenderer.srcObject = null; - remoteRenderer.srcObject = null; - //player.stop(); - _offer = false; - return true; + if (isIncomingCall) { + await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 1); + await invoke(invokeMethod: "HangUpAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 1); + _pc.dispose(); + isCallStarted = false; + isVideoCall = false; + isCamOff = false; + isMicOff = false; + isLoudSpeaker = false; + localVideoRenderer.srcObject = null; + remoteRenderer.srcObject = null; + isIncomingCall = false; + return true; + } else { + await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1); + await invoke(invokeMethod: "HangUpAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1); + _pc.dispose(); + isCallStarted = false; + isVideoCall = false; + isCamOff = false; + isMicOff = false; + isLoudSpeaker = false; + localVideoRenderer.srcObject = null; + remoteRenderer.srcObject = null; + //player.stop(); + // _offer = false; + return true; + } } // OutGoing Listeners @@ -157,52 +176,71 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { } Future onIceCandidateAsync(List? params) async { - print("--------------------- onIceCandidateAsync ---------------------------------------"); var items = params!.toList(); - if (kDebugMode) { - logger.i("res: " + items.toString()); - } - RemoteIceCandidatePayLoad data = RemoteIceCandidatePayLoad.fromJson(jsonDecode(items.first.toString())); - if (_pc != null) { - await _pc.addCandidate(RTCIceCandidate(data.candidate!.candidate, data.candidate!.sdpMid, data.candidate!.sdpMLineIndex)); - if (!isCallStarted) { - isCallStarted = true; - if (isCallStarted) { - Navigator.push( - providerContext, - MaterialPageRoute( - builder: (BuildContext context) => StartCallPage(localRenderer: localVideoRenderer, remoteRenderer: remoteRenderer), - allowSnapshotting: false, - )).then((value) { - Navigator.of(providerContext).pop(); - }); + if (isIncomingCall) { + RemoteIceCandidatePayLoad data = RemoteIceCandidatePayLoad.fromJson(jsonDecode(items.first.toString())); + if (_pc != null) { + await _pc.addCandidate(RTCIceCandidate(data.candidate!.candidate, data.candidate!.sdpMid, data.candidate!.sdpMLineIndex)); + } + } else { + if (kDebugMode) { + logger.i("res: " + items.toString()); + } + RemoteIceCandidatePayLoad data = RemoteIceCandidatePayLoad.fromJson(jsonDecode(items.first.toString())); + if (_pc != null) { + await _pc.addCandidate(RTCIceCandidate(data.candidate!.candidate, data.candidate!.sdpMid, data.candidate!.sdpMLineIndex)); + if (!isCallStarted) { + isCallStarted = true; + if (isCallStarted) { + Navigator.push( + providerContext, + MaterialPageRoute( + builder: (BuildContext context) => StartCallPage(), + allowSnapshotting: false, + )).then((value) { + Navigator.of(providerContext).pop(); + }); + } } } + notifyListeners(); } - notifyListeners(); } Future onOfferAsync(List? params) async { - // tempPayLoad = {"values": params!.toList()}; dynamic items = params!.toList(); - RTCSessionDescription description = await _createAnswer(); - await _pc.setLocalDescription(description); - var payload = {"target": items[0]["id"], "caller": outGoingCallData.callerId, "sdp": description.toMap()}; - invoke(invokeMethod: "AnswerOffer", currentUserID: outGoingCallData.callerId!, targetUserID: items[0]["id"], data: jsonEncode(payload)); + var data = jsonDecode(items.toString()); + if (isIncomingCall) { + _pc.setRemoteDescription(RTCSessionDescription(data[0]["sdp"]["sdp"], data[0]["sdp"]["type"])); + RTCSessionDescription description = await _createAnswer(); + await _pc.setLocalDescription(description); + var payload = {"target": data[0]["caller"], "caller": AppState().chatDetails!.response!.id, "sdp": description.toMap()}; + invoke(invokeMethod: "AnswerOfferAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, data: jsonEncode(payload)); + } + // else { + // RTCSessionDescription description = await _createAnswer(); + // await _pc.setLocalDescription(description); + // var payload = {"target": items[0]["id"], "caller": outGoingCallData.callerId, "sdp": description.toMap()}; + // invoke(invokeMethod: "AnswerOffer", currentUserID: outGoingCallData.callerId!, targetUserID: items[0]["id"], data: jsonEncode(payload)); + // } notifyListeners(); } // Incoming Listeners void onAnswerOffer(List? payload) async { - print("--------------------- On Answer Offer Async ---------------------------------------"); - var items = payload!.toList(); - if (kDebugMode) { - logger.i("res: " + items.toString()); + if (isIncomingCall) { + // print("--------------------- On Answer Offer Async ---------------------------------------"); + //await invoke(invokeMethod: "InvokeMobile", currentUserID: AppState().getchatUserDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, debugData: {"On Answer Offer Async"}); + } else { + var items = payload!.toList(); + if (kDebugMode) { + logger.i("res: " + items.toString()); + } + CallSessionPayLoad data = CallSessionPayLoad.fromJson(jsonDecode(items.first.toString())); + RTCSessionDescription description = RTCSessionDescription(data.sdp!.sdp, 'answer'); + _pc.setRemoteDescription(description); } - CallSessionPayLoad data = CallSessionPayLoad.fromJson(jsonDecode(items.first.toString())); - RTCSessionDescription description = RTCSessionDescription(data.sdp!.sdp, 'answer'); - _pc.setRemoteDescription(description); } void onHangUpAsync(List? params) { @@ -217,37 +255,53 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { print("--------------------- On Incoming Call ---------------------------------------"); dynamic items = params!.toList(); logger.d(items); - if (!isOnIncomingCallPage) { - Map json = { - "callerID": items[0]["id"], - "callerName": items[0]["userName"], - "callerEmail": items[0]["email"], - "callerTitle": items[0]["title"], - "callerPhone": null, - "receiverID": AppState().chatDetails!.response!.id, - "receiverName": AppState().chatDetails!.response!.userName, - "receiverEmail": AppState().chatDetails!.response!.email, - "receiverTitle": AppState().chatDetails!.response!.title, - "receiverPhone": AppState().chatDetails!.response!.phone, - "title": AppState().chatDetails!.response!.userName!.replaceAll(".", " "), - "callType": items[1] ? "Video" : "Audio", - }; - CallDataModel callData = CallDataModel.fromJson(json); - await Navigator.push( - providerContext, - MaterialPageRoute( - builder: (BuildContext context) => IncomingCall( - isVideoCall: items[1] ? true : false, - outGoingCallData: callData, - ), - ), - ); - isOnIncomingCallPage = true; - } + // Map json = { + // "callerID": items[0]["id"], + // "callerName": items[0]["userName"], + // "callerEmail": items[0]["email"], + // "callerTitle": items[0]["title"], + // "callerPhone": null, + // "receiverID": AppState().chatDetails!.response!.id, + // "receiverName": AppState().chatDetails!.response!.userName, + // "receiverEmail": AppState().chatDetails!.response!.email, + // "receiverTitle": AppState().chatDetails!.response!.title, + // "receiverPhone": AppState().chatDetails!.response!.phone, + // "title": AppState().chatDetails!.response!.userName!.replaceAll(".", " "), + // "callType": items[1] ? "Video" : "Audio", + // }; + // CallDataModel callData = CallDataModel.fromJson(json); + // ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), isOnline: true, incomingCallData: callData); + // + // if (!isOnIncomingCallPage) { + // Map json = { + // "callerID": items[0]["id"], + // "callerName": items[0]["userName"], + // "callerEmail": items[0]["email"], + // "callerTitle": items[0]["title"], + // "callerPhone": null, + // "receiverID": AppState().chatDetails!.response!.id, + // "receiverName": AppState().chatDetails!.response!.userName, + // "receiverEmail": AppState().chatDetails!.response!.email, + // "receiverTitle": AppState().chatDetails!.response!.title, + // "receiverPhone": AppState().chatDetails!.response!.phone, + // "title": AppState().chatDetails!.response!.userName!.replaceAll(".", " "), + // "callType": items[1] ? "Video" : "Audio", + // }; + // CallDataModel callData = CallDataModel.fromJson(json); + // await Navigator.push( + // providerContext, + // MaterialPageRoute( + // builder: (BuildContext context) => IncomingCall( + // isVideoCall: items[1] ? true : false, + // outGoingCallData: callData, + // ), + // ), + // ); + // isOnIncomingCallPage = true; + // } } void onCallDeclinedAsync(List? params) { - print("--------------------- on Call Declined ---------------------------------------"); endCall().then((bool value) { if (value) { isCallEnded = true; @@ -258,7 +312,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { //// Invoke Methods - Future invoke({required String invokeMethod, required int currentUserID, required int targetUserID, var data, int userStatus = 1}) async { + Future invoke({required String invokeMethod, required int currentUserID, required int targetUserID, var data, int userStatus = 1, var debugData}) async { List args = []; // Utils.showToast(currentUserID.toString() + " -- " + targetUserID.toString() + " -- " + isVideoCall.toString()); if (invokeMethod == "CallUserAsync") { @@ -276,8 +330,9 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { args = [currentUserID, userStatus]; } else if (invokeMethod == "HangUpAsync") { args = [currentUserID, targetUserID]; + } else if (invokeMethod == "InvokeMobile") { + args = [debugData]; } - logger.d(args); try { await chatHubConnection.invoke("$invokeMethod", args: args); } catch (e) { @@ -319,7 +374,9 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { }, 'optional': [] }; + RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints); + logger.w(pc.connectionState); await pc!.addStream(localStream!); pc?.onConnectionState = (RTCPeerConnectionState state) {}; pc?.onAddStream = (MediaStream stream) { @@ -327,10 +384,19 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); }; pc!.onIceCandidate = (RTCIceCandidate e) async { - if (e.candidate != null) { - var payload = {"target": outGoingCallData.callerId, "candidate": e.toMap()}; - logger.i("Candidate:" + e.toMap().toString()); - await invoke(invokeMethod: "IceCandidateAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, data: jsonEncode(payload)); + if (isIncomingCall) { + if (e.candidate != null) { + var payload = {"target": incomingCallData.targetUserId, "candidate": e.toMap()}; + logger.i("Candidate:" + e.toMap().toString()); + invoke(invokeMethod: "IceCandidateAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, data: jsonEncode(payload)); + notifyListeners(); + } + } else { + if (e.candidate != null) { + var payload = {"target": outGoingCallData.callerId, "candidate": e.toMap()}; + logger.i("Candidate:" + e.toMap().toString()); + invoke(invokeMethod: "IceCandidateAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, data: jsonEncode(payload)); + } } }; // pc!.onTrack = (RTCTrackEvent event) async { @@ -345,12 +411,21 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { // }; pc!.onSignalingState = (RTCSignalingState state) { logger.i("signaling state: " + state.name); + invoke( + invokeMethod: "InvokeMobile", + currentUserID: AppState().getchatUserDetails!.response!.id!, + targetUserID: incomingCallData.targetUserId!, + debugData: {"location": "Signaling", "parms": state.name}); }; pc!.onIceGatheringState = (RTCIceGatheringState state) { logger.i("rtc ice gathering state: " + state.name); }; pc!.onIceConnectionState = (RTCIceConnectionState state) { - logger.i("rtc ice connection state: " + state.name); + // invoke( + // invokeMethod: "InvokeMobile", + // currentUserID: AppState().getchatUserDetails!.response!.id!, + // targetUserID: incomingCallData.targetUserId!, + // debugData: {"location": "onIceConnection", "parms": state.name}); }; pc!.onRenegotiationNeeded = () {}; return pc; @@ -376,13 +451,13 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { Future _createOffer() async { RTCSessionDescription description = await _pc!.createOffer(); - _offer = true; + // _offer = true; return description; } Future _createAnswer() async { RTCSessionDescription description = await _pc!.createAnswer(); - _offer = false; + // _offer = false; return description; } @@ -421,7 +496,6 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { void switchCamera() { isFrontCamera = !isFrontCamera; - print("================= Camera Switch Triggered ==================="); Helper.switchCamera(localStream.getVideoTracks()[0]); notifyListeners(); } @@ -431,11 +505,22 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { localStream = await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints); localVideoRenderer.srcObject = localStream; await remoteRenderer.initialize(); - _pc = await creatOfferWithCon(); } - Future startIncomingCallViaKit() async { + Future startIncomingCallViaKit({bool isVCall = true, required var inCallData}) async { + Utils.saveStringFromPrefs("isIncomingCall", "false"); + isVideoCall = isVCall; await startIncomingCall(); - await invoke(invokeMethod: "answerCallAsync", currentUserID: inComingCallData.extra!.loginDetails!.id!, targetUserID: inComingCallData.extra!.callerDetails!.targetUserId!); + isIncomingCall = true; + dynamic callData = await jsonDecode(inCallData); + incomingCallData = SingleUserChatModel.fromJson(callData[0]["callerDetails"]); + notifyListeners(); + } + + void connectIncomingCall() { + invoke(invokeMethod: "answerCallAsync", currentUserID: AppState().getchatUserDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!); + isIncomingCallLoader = false; + isVideoCall = true; + notifyListeners(); } } diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index c23765a..c536376 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -139,15 +139,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); - // Calling - - // chatHubConnection.on("OnCallAcceptedAsync", callP.onCallAcceptedAsync); - // chatHubConnection.on("OnIceCandidateAsync", callP.onIceCandidateAsync); - // chatHubConnection.on("OnOfferAsync", callP.onOfferAsync); - // chatHubConnection.on("OnAnswerOffer", callP.onAnswerOffer); - // chatHubConnection.on("OnHangUpAsync", callP.onHangUpAsync); - // chatHubConnection.on("OnCallDeclinedAsync", callP.onCallDeclinedAsync); - if (kDebugMode) { logger.i("All listeners registered"); } @@ -1478,21 +1469,4 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } return Material.TextDirection.ltr; } - - void openChatByNoti(BuildContext context) async { - SingleUserChatModel nUser = SingleUserChatModel(); - Utils.saveStringFromPrefs("isAppOpendByChat", "false"); - if (await Utils.getStringFromPrefs("notificationData") != "null") { - nUser = SingleUserChatModel.fromJson(jsonDecode(await Utils.getStringFromPrefs("notificationData"))); - Utils.saveStringFromPrefs("notificationData", "null"); - Future.delayed(const Duration(seconds: 2)); - for (ChatUser user in searchedChats!) { - if (user.id == nUser.targetUserId) { - Navigator.pushNamed(context, AppRoutes.chatDetailed, arguments: ChatDetailedScreenParams(user, false)); - return; - } - } - } - Utils.saveStringFromPrefs("notificationData", "null"); - } } diff --git a/lib/ui/chat/call/start_call_screen.dart b/lib/ui/chat/call/start_call_screen.dart index e09c304..ab29ffa 100644 --- a/lib/ui/chat/call/start_call_screen.dart +++ b/lib/ui/chat/call/start_call_screen.dart @@ -9,6 +9,8 @@ import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart'; import 'package:mohem_flutter_app/models/chat/incomingCall.dart'; import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; @@ -17,27 +19,22 @@ import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:provider/provider.dart'; class StartCallPage extends StatefulWidget { - RTCVideoRenderer? localRenderer; - RTCVideoRenderer? remoteRenderer; - - StartCallPage({this.localRenderer, this.remoteRenderer}); - @override _StartCallPageState createState() => _StartCallPageState(); } class _StartCallPageState extends State { final dragController = DragController(); - late ChatProviderModel cPro; - late ChatCallProvider callPro; + late ChatProviderModel chatProvider; + late ChatCallProvider callProvider; var inCallData; - bool isIncomingCall = false; //userChatDetails @override void initState() { - callPro = Provider.of(context, listen: false); - cPro = Provider.of(context, listen: false); + // callProvider = Provider.of(context, listen: false); + // chatProvider = Provider.of(context, listen: false); + // connection(); super.initState(); } @@ -49,278 +46,260 @@ class _StartCallPageState extends State { super.dispose(); } - void connection() async { - isIncomingCall = (Utils.getStringFromPrefs("isIncomingCall") == "true" ? true : false); - Utils.saveStringFromPrefs("isIncomingCall", "false"); - callPro.tempPayLoad = inCallData; - // callPro.isIncomingCallLoader = false; - // callPro.notifyListeners(); - cPro.userLoginData = UserAutoLoginModel( - response: Response.fromJson(jsonDecode(inCallData["loginDetails"])), - errorResponses: null, - ); - AppState().setchatUserDetails = cPro.userLoginData; - await cPro.buildHubConnection(context: context, ccProvider: callPro); - //callPro.inComingCallData = inCallData; - callPro.isIncomingCallLoader = false; - Future.delayed(const Duration(seconds: 2)).then((value) { - callPro.startIncomingCallViaKit(); - //Utils.showToast(inCallData.extra!.loginDetails!.toRawJson(), longDuration: false); - callPro.notifyListeners(); - }); - } + // void connection() async { + // inCallData = await Utils.getStringFromPrefs("inComingCallData"); + // dynamic callData = await jsonDecode(inCallData); + // AppState().setchatUserDetails = UserAutoLoginModel( + // response: Response.fromJson(callData[0]["loginDetails"]), + // errorResponses: null, + // ); + // callProvider.startIncomingCallViaKit(inCallData: inCallData); + // try { + // await chatProvider.buildHubConnection(context: context, ccProvider: callProvider).whenComplete(() { + // callProvider.init(); + // }); + // } catch (e) { + // logger.w(e); + // } + // + // // callProvider.afterHub(); + // } @override Widget build(BuildContext context) { - inCallData = ModalRoute.of(context)!.settings.arguments; - if (inCallData != null) { - connection(); - } - return Consumer2(builder: (BuildContext context, ChatCallProvider provider, ChatProviderModel cpm, Widget? child) { - return SizedBox( - width: double.infinity, - height: double.infinity, - child: provider.isVideoCall - ? Stack( - alignment: FractionalOffset.center, - children: [ - if (provider.isVideoCall) - Positioned.fill( - child: RTCVideoView( - widget.remoteRenderer!, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, - key: const Key('remote'), - ), - ), - if (provider.isVideoCall) - DraggableWidget( - bottomMargin: 20, - topMargin: 40, - intialVisibility: true, - horizontalSpace: 20, - shadowBorderRadius: 50, - initialPosition: AnchoringPosition.topLeft, - dragController: dragController, - normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), - draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), - child: SizedBox( - height: 200, - width: 140, - child: RTCVideoView( - widget.localRenderer!, - mirror: true, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, + return Scaffold( + extendBody: true, + body: Consumer2(builder: (BuildContext context, ChatCallProvider provider, ChatProviderModel cpm, Widget? child) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: provider.isVideoCall + ? Stack( + alignment: FractionalOffset.center, + children: [ + if (provider.isVideoCall) + Positioned.fill( + child: RTCVideoView( + provider.remoteRenderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, + key: const Key('remote'), + ), ), - ), - ), - if (!provider.isVideoCall) - Positioned.fill( - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), - child: Container( - decoration: BoxDecoration( - color: MyColors.grey57Color.withOpacity( - 0.3, - ), + if (provider.isVideoCall) + DraggableWidget( + bottomMargin: 20, + topMargin: 40, + intialVisibility: true, + horizontalSpace: 20, + shadowBorderRadius: 50, + initialPosition: AnchoringPosition.topLeft, + dragController: dragController, + normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), + draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), + child: SizedBox( + height: 200, + width: 140, + child: RTCVideoView( + provider.localVideoRenderer, + mirror: true, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - 40.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, + ), + ), + if (!provider.isVideoCall) + Positioned.fill( + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), + child: Container( + decoration: BoxDecoration( + color: MyColors.grey57Color.withOpacity( + 0.3, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, children: [ - Container( - margin: const EdgeInsets.all(21.0), - child: Container( - margin: const EdgeInsets.only( - left: 10.0, - right: 10.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 70, - width: 70, - fit: BoxFit.cover, - ), - 10.height, - Text( - callPro.outGoingCallData.receiverName!, - style: const TextStyle( - fontSize: 21, - decoration: TextDecoration.none, - fontWeight: FontWeight.bold, - color: MyColors.white, - letterSpacing: -1.26, - height: 23 / 12, - ), + 40.height, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: const EdgeInsets.all(21.0), + child: Container( + margin: const EdgeInsets.only( + left: 10.0, + right: 10.0, ), - const Text( - "On Call", - style: TextStyle( - fontSize: 16, - decoration: TextDecoration.none, - fontWeight: FontWeight.w600, - color: Color( - 0xffC6C6C6, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + SvgPicture.asset( + "assets/images/user.svg", + height: 70, + width: 70, + fit: BoxFit.cover, ), - letterSpacing: -0.48, - height: 23 / 24, - ), - ), - const SizedBox( - height: 2, + 10.height, + Text( + provider.outGoingCallData.receiverName!, + style: const TextStyle( + fontSize: 21, + decoration: TextDecoration.none, + fontWeight: FontWeight.bold, + color: MyColors.white, + letterSpacing: -1.26, + height: 23 / 12, + ), + ), + const Text( + "On Call", + style: TextStyle( + fontSize: 16, + decoration: TextDecoration.none, + fontWeight: FontWeight.w600, + color: Color( + 0xffC6C6C6, + ), + letterSpacing: -0.48, + height: 23 / 24, + ), + ), + const SizedBox( + height: 2, + ), + ], ), - ], + ), ), - ), + ], ), ], ), - ], + ), ), ), ), - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Container( - padding: const EdgeInsets.only( - bottom: 20, - left: 40, - right: 40, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // if (provider.isVideoCall) - RawMaterialButton( - constraints: const BoxConstraints(), - onPressed: () { - callPro.loudOn(); - }, - elevation: 2.0, - fillColor: callPro.isLoudSpeaker ? MyColors.green2DColor : Colors.grey, - padding: const EdgeInsets.all( - 10.0, - ), - shape: const CircleBorder(), - child: const Icon( - Icons.volume_up, - color: MyColors.white, - size: 30.0, - ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + padding: const EdgeInsets.only( + bottom: 20, + left: 40, + right: 40, ), - RawMaterialButton( - constraints: const BoxConstraints(), - onPressed: () { - provider.camOff(); - }, - elevation: 2.0, - fillColor: provider.isCamOff ? MyColors.green2DColor : Colors.grey, - padding: const EdgeInsets.all( - 10.0, - ), - shape: const CircleBorder(), - child: Icon( - provider.isCamOff ? Icons.videocam_off : Icons.videocam, - color: MyColors.white, - size: 30.0, - ), - ), - RawMaterialButton( - constraints: const BoxConstraints(), - onPressed: () { - provider.switchCamera(); - }, - elevation: 2.0, - fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor, - padding: const EdgeInsets.all( - 10.0, - ), - shape: const CircleBorder(), - child: Icon( - provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera, - color: MyColors.white, - size: 30.0, - ), - ), - RawMaterialButton( - constraints: const BoxConstraints(), - onPressed: () { - provider.micOff(); - }, - elevation: 2.0, - fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey, - padding: const EdgeInsets.all( - 10.0, - ), - shape: const CircleBorder(), - child: Icon( - provider.isMicOff ? Icons.mic_off : Icons.mic, - color: MyColors.white, - size: 30.0, - ), - ), - RawMaterialButton( - constraints: const BoxConstraints(), - onPressed: () { - provider.endCall().then((value) { - if (value) { - Navigator.of(context).pop(); - } - }); - }, - elevation: 2.0, - fillColor: MyColors.redA3Color, - padding: const EdgeInsets.all( - 10.0, - ), - shape: const CircleBorder(), - child: const Icon( - Icons.call_end, - color: MyColors.white, - size: 30.0, - ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // if (provider.isVideoCall) + RawMaterialButton( + constraints: const BoxConstraints(), + onPressed: () { + provider.loudOn(); + }, + elevation: 2.0, + fillColor: provider.isLoudSpeaker ? MyColors.green2DColor : Colors.grey, + padding: const EdgeInsets.all( + 10.0, + ), + shape: const CircleBorder(), + child: const Icon( + Icons.volume_up, + color: MyColors.white, + size: 30.0, + ), + ), + RawMaterialButton( + constraints: const BoxConstraints(), + onPressed: () { + provider.camOff(); + }, + elevation: 2.0, + fillColor: provider.isCamOff ? MyColors.green2DColor : Colors.grey, + padding: const EdgeInsets.all( + 10.0, + ), + shape: const CircleBorder(), + child: Icon( + provider.isCamOff ? Icons.videocam_off : Icons.videocam, + color: MyColors.white, + size: 30.0, + ), + ), + RawMaterialButton( + constraints: const BoxConstraints(), + onPressed: () { + provider.switchCamera(); + }, + elevation: 2.0, + fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor, + padding: const EdgeInsets.all( + 10.0, + ), + shape: const CircleBorder(), + child: Icon( + provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera, + color: MyColors.white, + size: 30.0, + ), + ), + RawMaterialButton( + constraints: const BoxConstraints(), + onPressed: () { + provider.micOff(); + }, + elevation: 2.0, + fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey, + padding: const EdgeInsets.all( + 10.0, + ), + shape: const CircleBorder(), + child: Icon( + provider.isMicOff ? Icons.mic_off : Icons.mic, + color: MyColors.white, + size: 30.0, + ), + ), + RawMaterialButton( + constraints: const BoxConstraints(), + onPressed: () { + provider.endCall().then((value) { + if (value) { + Navigator.of(context).pop(); + } + }); + }, + elevation: 2.0, + fillColor: MyColors.redA3Color, + padding: const EdgeInsets.all( + 10.0, + ), + shape: const CircleBorder(), + child: const Icon( + Icons.call_end, + color: MyColors.white, + size: 30.0, + ), + ), + ], ), - ], + ), ), - ), - ), - ], - ) - : provider.isIncomingCallLoader - ? SizedBox( + ], + ) + : const SizedBox( width: double.infinity, height: 500, child: Center( child: CircularProgressIndicator(), ), - ) - : ListView( - padding: EdgeInsets.fromLTRB(20,40,20,20), - children: [ - Consumer( - builder: (BuildContext cxt, ChatCallProvider data, Widget? child) { - return Text( - data.tempPayLoad.toString(),style: TextStyle(fontSize: 12, color: Colors.white, decoration: TextDecoration.none,), - ); - }, - ), - - ], - ), - ); - }); + )); + }), + ); } } diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index a0fc1ad..0586bcc 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -101,7 +101,7 @@ class _LoginScreenState extends State { Future checkFirebaseToken() async { if (await Utils.getStringFromPrefs("isIncomingCall") == "true") { Utils.hideLoading(context); - Navigator.pushNamed(context, AppRoutes.chatStartCall, arguments: await Utils.getStringFromPrefs("inComingCallData")); + Navigator.pushNamed(context, AppRoutes.chatStartCall); } else { try { Utils.showLoading(context);