diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index b4ad5b3..5b1bfee 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,11 +1,13 @@ import UIKit +import PushKit import Flutter import Firebase +import flutter_callkit_incoming import flutter_local_notifications - +// PKPushRegistryDelegate @UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { +@objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? @@ -18,6 +20,51 @@ import flutter_local_notifications UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate } GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } + + //Setup VOIP + let mainQueue = DispatchQueue.main + let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue) + voipRegistry.delegate = self + voipRegistry.desiredPushTypes = [PKPushType.voIP] + + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + // Handle updated push credentials + func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) { + print(credentials.token) + let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined() + print(deviceToken) + print("deviceToken From Swift") + //Save deviceToken to your server + SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken) + } + + func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) { + print("didInvalidatePushTokenFor") + SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP("") + } + + // Handle incoming pushes + func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { + print("didReceiveIncomingPushWith") + guard type == .voIP else { return } + print(payload.dictionaryPayload) +// let id = payload.dictionaryPayload["id"] as? String ?? "" +// let nameCaller = payload.dictionaryPayload["nameCaller"] as? String ?? "" +// let handle = payload.dictionaryPayload["handle"] as? String ?? "" + let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false +// +// + let data = flutter_callkit_incoming.Data(id: "1", nameCaller: "Mohemm", handle: "handle", type: isVideo ? 1 : 0) +// //set more data +// data.extra = ["user": "abc@123", "platform": "ios"] +// //data.iconName = ... +// //data..... +// data.appName = "Mohemm" +// data.iconName = "Mohemm" + SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true) + } + + } diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index a62656f..50ff832 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -94,5 +94,7 @@ TAG + UIApplicationSupportsIndirectInputEvents + diff --git a/lib/classes/notifications.dart b/lib/classes/notifications.dart index 30d90f5..db59171 100644 --- a/lib/classes/notifications.dart +++ b/lib/classes/notifications.dart @@ -5,7 +5,6 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -// import 'package:huawei_hmsavailability/huawei_hmsavailability.dart'; 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'; diff --git a/lib/classes/voip_chat_call.dart b/lib/classes/voip_chat_call.dart index 5454eff..89468cb 100644 --- a/lib/classes/voip_chat_call.dart +++ b/lib/classes/voip_chat_call.dart @@ -23,8 +23,8 @@ class ChatVoipCall { factory ChatVoipCall() => _instance; - late ChatProviderModel chatProvider; - late ChatCallProvider callProvider; + late ChatProviderModel prov; + late ChatCallProvider cProv; dynamic inCallData; Future showCallkitIncoming({required String uuid, RemoteMessage? data, bool isOnline = false, CallDataModel? incomingCallData}) async { @@ -46,6 +46,7 @@ class ChatVoipCall { 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"])); + data!.data["callType"] == "video" ? cProv.isVideoCall = true : cProv.isVideoCall = false; CallKitParams params = CallKitParams( id: uuid, nameCaller: callerData.targetUserName, @@ -108,8 +109,8 @@ class ChatVoipCall { } Future initProviders() async { - callProvider = Provider.of(AppRoutes.navigatorKey.currentContext!, listen: false); - chatProvider = Provider.of(AppRoutes.navigatorKey.currentContext!, listen: false); + cProv = Provider.of(AppRoutes.navigatorKey.currentContext!, listen: false); + prov = Provider.of(AppRoutes.navigatorKey.currentContext!, listen: false); } void connection({required data}) async { @@ -119,10 +120,10 @@ class ChatVoipCall { response: Response.fromJson(callData[0]["loginDetails"]), errorResponses: null, ); - callProvider.startIncomingCallViaKit(inCallData: inCallData); + cProv.startIncomingCallViaKit(inCallData: data); try { - await chatProvider.buildHubConnection(context: AppRoutes.navigatorKey.currentContext!, ccProvider: callProvider).whenComplete(() { - callProvider.init(); + await prov.buildHubConnection(context: AppRoutes.navigatorKey.currentContext!, ccProvider: cProv).whenComplete(() { + cProv.init(); }); } catch (e) { logger.w(e); @@ -143,13 +144,13 @@ class ChatVoipCall { break; case Event.ACTION_CALL_ACCEPT: Navigator.pushNamedAndRemoveUntil(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall, (_) => false); - // Navigator.pushNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall); + // Navigator.pushNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall); break; case Event.ACTION_CALL_DECLINE: - callProvider.isIncomingCall = true; + cProv.isIncomingCall = true; Utils.saveStringFromPrefs("isIncomingCall", "false"); Utils.saveStringFromPrefs("inComingCallData", "null"); - callProvider.endCall(); + cProv.endCall(); break; case Event.ACTION_CALL_ENDED: Utils.saveStringFromPrefs("isIncomingCall", "false"); diff --git a/lib/provider/chat_call_provider.dart b/lib/provider/chat_call_provider.dart index 89a23fb..decbdd0 100644 --- a/lib/provider/chat_call_provider.dart +++ b/lib/provider/chat_call_provider.dart @@ -15,6 +15,7 @@ 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/start_call_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; +import 'package:signalr_netcore/hub_connection.dart'; import 'package:uuid/uuid.dart'; class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { @@ -136,9 +137,13 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { Future endCall() async { 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(); + if (chatHubConnection.state == HubConnectionState.Connected) { + 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); + } + if (_pc.connectionState == RTCPeerConnectionState.RTCPeerConnectionStateConnected) { + _pc.dispose(); + } isCallStarted = false; isVideoCall = false; isCamOff = false; @@ -149,9 +154,13 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { 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(); + if (chatHubConnection.state == HubConnectionState.Connected) { + await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1); + await invoke(invokeMethod: "HangUpAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1); + } + if (_pc.connectionState == RTCPeerConnectionState.RTCPeerConnectionStateConnected) { + _pc.dispose(); + } isCallStarted = false; isVideoCall = false; isCamOff = false; @@ -421,16 +430,35 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { logger.i("rtc ice gathering state: " + state.name); }; pc!.onIceConnectionState = (RTCIceConnectionState state) { - // invoke( - // invokeMethod: "InvokeMobile", - // currentUserID: AppState().getchatUserDetails!.response!.id!, - // targetUserID: incomingCallData.targetUserId!, - // debugData: {"location": "onIceConnection", "parms": state.name}); + if (RTCIceConnectionState.RTCIceConnectionStateFailed == state || + RTCIceConnectionState.RTCIceConnectionStateDisconnected == state || + RTCIceConnectionState.RTCIceConnectionStateClosed == state) { + logger.i("Ice Connection State:" + state.name); + endCall(); + } }; - pc!.onRenegotiationNeeded = () {}; + pc!.onRenegotiationNeeded = _onRenegotiate; return pc; } + void _onRenegotiate() async { + try { + print('onRenegotiationNeeded start'); + // makingOffer = true; + await _pc.setLocalDescription(await _pc.createOffer(videoConstraints)); + print('onRenegotiationNeeded state after setLocalDescription: ' + _pc.signalingState.toString()); + // send offer via callManager + var localDesc = await _pc.getLocalDescription(); + // callManager.sendCallMessage(MsgType.rtc_offer, RtcOfferAnswer(localDesc.sdp, localDesc.type)); + print('onRenegotiationNeeded; offer sent'); + } catch (e) { + print("onRenegotiationNeeded error: " + e.toString()); + } finally { + // makingOffer = false; + print('onRenegotiationNeeded done'); + } + } + void playRingtone() async { player.stop(); await player.setVolume(1.0); diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index c536376..4e44ea9 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -457,7 +457,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } if (isChatScreenActive && data.first.currentUserId == receiverID) { int index = userChatHistory.indexWhere((SingleUserChatModel element) => element.userChatHistoryId == 0); - logger.d(index); userChatHistory[index] = data.first; } diff --git a/pubspec.yaml b/pubspec.yaml index adcab03..5782482 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -119,7 +119,7 @@ dependencies: dependency_overrides: - firebase_core_platform_interface: 4.5.1 + #firebase_core_platform_interface: 4.5.3 dev_dependencies: