diff --git a/assets/audio/ring_30Sec.caf b/assets/audio/ring_30Sec.caf
new file mode 100644
index 0000000..b5bb4b8
Binary files /dev/null and b/assets/audio/ring_30Sec.caf differ
diff --git a/assets/audio/ring_60Sec.mp3 b/assets/audio/ring_60Sec.mp3
new file mode 100644
index 0000000..ac32394
Binary files /dev/null and b/assets/audio/ring_60Sec.mp3 differ
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 75c680b..50ff832 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -57,8 +57,11 @@
This app requires microphone access to for call.
UIBackgroundModes
+ processing
fetch
remote-notification
+ voip
+
FirebaseAppDelegateProxyEnabled
@@ -91,5 +94,7 @@
TAG
+ UIApplicationSupportsIndirectInputEvents
+
diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart
index f727c7d..275f10b 100644
--- a/lib/api/chat/chat_api_client.dart
+++ b/lib/api/chat/chat_api_client.dart
@@ -71,9 +71,7 @@ class ChatApiClient {
if (!kReleaseMode) {
logger.i("res: " + response.body);
}
- return ChatUserModel.fromJson(
- json.decode(response.body),
- );
+ return ChatUserModel.fromJson(json.decode(response.body));
} catch (e) {
throw e;
}
diff --git a/lib/classes/chat_call_kit.dart b/lib/classes/chat_call_kit.dart
new file mode 100644
index 0000000..2059eec
--- /dev/null
+++ b/lib/classes/chat_call_kit.dart
@@ -0,0 +1,203 @@
+import 'dart:convert';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/cupertino.dart';
+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/navigationService.dart';
+import 'package:mohem_flutter_app/classes/notifications.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();
+
+ ChatVoipCall._internal();
+
+ factory ChatVoipCall() => _instance;
+
+ late ChatProviderModel prov;
+ late ChatCallProvider cProv;
+ dynamic inCallData;
+ bool isUserOnline = false;
+ dynamic callData;
+
+ Future showCallkitIncoming({required String uuid, RemoteMessage? data, CallDataModel? incomingCallData, bool background = false}) async {
+ await ChatVoipCall().listenerEvent();
+ await FlutterCallkitIncoming.endAllCalls();
+ ALM.Response autoLoginData;
+ SingleUserChatModel callerData;
+ if (data!.data["user_token_response"] == null || data.data["user_token_response"].isEmpty) {
+ // Online & App Logged In
+ ALM.Response sharedDetails = ALM.Response.fromJson(jsonDecode(await Utils.getStringFromPrefs("userLoginChatDetails")));
+
+ autoLoginData = ALM.Response.fromJson(AppState().getchatUserDetails == null ? sharedDetails.toJson() : AppState().getchatUserDetails!.response!.toJson());
+ dynamic items = jsonDecode(data!.data["user_chat_history_response"]);
+ callerData = SingleUserChatModel(
+ targetUserId: items["CurrentUserId"],
+ targetUserEmail: items["CurrentUserEmail"],
+ targetUserName: items["CurrentUserName"].split("@").first,
+ currentUserId: autoLoginData.id,
+ currentUserEmail: autoLoginData.email,
+ currentUserName: autoLoginData.userName,
+ chatEventId: 3);
+ isUserOnline = true;
+ } else {
+ // Offline or App in Background or App is At Verify Screen
+ autoLoginData = ALM.Response.fromJson(jsonDecode(data.data["user_token_response"]));
+ callerData = SingleUserChatModel.fromJson(json.decode(data!.data["user_chat_history_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(),
+ 'isIncomingCall': true,
+ },
+ 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 FlutterCallkitIncoming.showCallkitIncoming(params);
+ }
+ }
+ Future getCurrentCall() async {
+ var calls = await FlutterCallkitIncoming.activeCalls();
+ if (calls is List) {
+ if (calls.isNotEmpty) {
+ return calls[0];
+ } else {
+ return null;
+ }
+ }
+ }
+
+ void checkAndNavigationCallingPage() async {
+ var currentCall = await getCurrentCall();
+ if (currentCall != null) {
+ Future.delayed(const Duration(seconds: 2)).whenComplete(() {
+ Navigator.pushNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall);
+ });
+ }
+ }
+
+ Future isCall() async {
+ var calls = await FlutterCallkitIncoming.activeCalls();
+ if (calls is List) {
+ if (calls.isNotEmpty) {
+ NavigationService.navigateTo(AppRoutes.chatStartCall);
+ }
+ }
+ }
+
+//Function(CallEvent) callback
+ Future listenerEvent() async {
+ try {
+ FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async {
+ switch (event!.event) {
+ case Event.ACTION_CALL_INCOMING:
+ break;
+ case Event.ACTION_CALL_START:
+ break;
+ case Event.ACTION_CALL_ACCEPT:
+ if (isUserOnline) {
+ checkAndNavigationCallingPage();
+ } else {
+ isCall();
+ }
+ break;
+ case Event.ACTION_CALL_DECLINE:
+ // cProv.isIncomingCall = true;
+ Utils.saveStringFromPrefs("isIncomingCall", "false");
+ Utils.saveStringFromPrefs("inComingCallData", "null");
+
+ // cProv.endCall(isUserOnline: true);
+ FlutterCallkitIncoming.endAllCalls();
+ break;
+ case Event.ACTION_CALL_ENDED:
+ Utils.saveStringFromPrefs("isIncomingCall", "false");
+ Utils.saveStringFromPrefs("inComingCallData", "null");
+ FlutterCallkitIncoming.endAllCalls();
+ break;
+ case Event.ACTION_CALL_TIMEOUT:
+ Utils.saveStringFromPrefs("isIncomingCall", "false");
+ Utils.saveStringFromPrefs("inComingCallData", "null");
+ break;
+ }
+ });
+ } on Exception {}
+ }
+
+ void nothing() {
+ // if (!background) {
+ // await initProviders();
+ // if (data!.data["callType"] == "video") {
+ // cProv.isVideoCall = true;
+ // } else {
+ // cProv.isAudioCall = true;
+ // cProv.isVideoCall = false;
+ // }
+ // }
+ //
+ // // if(!background){}
+ // await initProviders();
+
+ // callData = jsonEncode([
+ // {
+ // "loginDetails": autoLoginData.toJson(),
+ // "callerDetails": callerData.toJson(),
+ // }
+ // ]);
+
+ // connection(data: callData, isUserOnline: isUserOnline).whenComplete(() => {});
+ // if (isUserOnline) {
+ // cProv.init();
+ // }
+ // if (!isUserOnline) {
+ // initProviders();
+ // }
+ // Navigator.pushNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall);
+ }
+}
diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart
index 32b3fc7..422afea 100644
--- a/lib/classes/consts.dart
+++ b/lib/classes/consts.dart
@@ -3,8 +3,8 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
class ApiConsts {
//static String baseUrl = "http://10.200.204.20:2801/"; // Local server
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server
- static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
- // static String baseUrl = "https://hmgwebservices.com"; // Live server
+ // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
+ static String baseUrl = "https://hmgwebservices.com"; // Live server
static String baseUrlServices = baseUrl + "/Services/"; // server
// static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server
static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/";
diff --git a/lib/classes/navigationService.dart b/lib/classes/navigationService.dart
new file mode 100644
index 0000000..d18a945
--- /dev/null
+++ b/lib/classes/navigationService.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/material.dart';
+import 'package:get_it/get_it.dart';
+import 'package:mohem_flutter_app/app_state/app_state.dart';
+import 'package:mohem_flutter_app/config/routes.dart';
+
+final locator = GetIt.instance;
+
+class NavigationService {
+ final GlobalKey navigatorKey = AppRoutes.navigatorKey;
+
+ static Future navigateTo(String routeName) {
+ var key = locator().navigatorKey;
+ return key.currentState!.pushNamed(routeName);
+ }
+
+ static Future navigateToPage(Widget page) {
+ var key = locator().navigatorKey;
+ var pageRoute = MaterialPageRoute(builder: (context) => page);
+ return Navigator.push(key.currentContext!, pageRoute);
+ }
+}
+
+void setupLocator() {
+ locator.registerLazySingleton( ()=> NavigationService());
+}
diff --git a/lib/classes/notifications.dart b/lib/classes/notifications.dart
index 9528616..c573e4b 100644
--- a/lib/classes/notifications.dart
+++ b/lib/classes/notifications.dart
@@ -2,20 +2,22 @@ import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/cupertino.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/chat_call_kit.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
+import 'package:mohem_flutter_app/main.dart';
import 'package:permission_handler/permission_handler.dart';
+import 'package:uuid/uuid.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
class AppNotifications {
static final AppNotifications _instance = AppNotifications._internal();
-
AppNotifications._internal();
factory AppNotifications() => _instance;
@@ -57,7 +59,7 @@ class AppNotifications {
if (initialMessage != null) _handleMessage(initialMessage);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
- if (message.notification != null) _handleMessage(message);
+ if (message != null) _handleMessage(message);
});
FirebaseMessaging.onMessageOpenedApp.listen(_handleOpenApp);
@@ -113,6 +115,12 @@ class AppNotifications {
void _handleMessage(RemoteMessage message) {
Utils.saveStringFromPrefs("isAppOpendByChat", "false");
+ 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) {
@@ -131,9 +139,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') {
- // ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message);
+ 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, background: true);
}
}
diff --git a/lib/config/routes.dart b/lib/config/routes.dart
index 7ccb6ce..2667494 100644
--- a/lib/config/routes.dart
+++ b/lib/config/routes.dart
@@ -4,6 +4,7 @@ import 'package:mohem_flutter_app/ui/attendance/add_vacation_rule_screen.dart';
import 'package:mohem_flutter_app/ui/attendance/monthly_attendance_screen.dart';
import 'package:mohem_flutter_app/ui/attendance/vacation_rule_screen.dart';
import 'package:mohem_flutter_app/ui/bottom_sheets/attendence_details_bottom_sheet.dart';
+import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_home.dart';
import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart';
@@ -184,6 +185,7 @@ class AppRoutes {
static const String chat = "/chat";
static const String chatDetailed = "/chatDetailed";
static const String chatFavoriteUsers = "/chatFavoriteUsers";
+ static const String chatStartCall = "/chatStartCall";
//Marathon
static const String marathonIntroScreen = "/marathonIntroScreen";
@@ -296,6 +298,8 @@ class AppRoutes {
chat: (BuildContext context) => ChatHome(),
chatDetailed: (BuildContext context) => ChatDetailScreen(),
chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(),
+ chatStartCall: (BuildContext context) => StartCallPage(),
+
// Marathon
marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(),
diff --git a/lib/generated_plugin_registrant.dart b/lib/generated_plugin_registrant.dart
index 642c8ec..bcf8ba5 100644
--- a/lib/generated_plugin_registrant.dart
+++ b/lib/generated_plugin_registrant.dart
@@ -7,7 +7,6 @@
// ignore_for_file: depend_on_referenced_packages
import 'package:audio_session/audio_session_web.dart';
-import 'package:camera_web/camera_web.dart';
import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:firebase_messaging_web/firebase_messaging_web.dart';
@@ -16,7 +15,6 @@ import 'package:geolocator_web/geolocator_web.dart';
import 'package:google_maps_flutter_web/google_maps_flutter_web.dart';
import 'package:image_picker_for_web/image_picker_for_web.dart';
import 'package:just_audio_web/just_audio_web.dart';
-import 'package:record_web/record_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:video_player_web/video_player_web.dart';
@@ -26,7 +24,6 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
// ignore: public_member_api_docs
void registerPlugins(Registrar registrar) {
AudioSessionWeb.registerWith(registrar);
- CameraPlugin.registerWith(registrar);
FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar);
@@ -35,7 +32,6 @@ void registerPlugins(Registrar registrar) {
GoogleMapsPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar);
JustAudioPlugin.registerWith(registrar);
- RecordPluginWeb.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
VideoPlayerPlugin.registerWith(registrar);
diff --git a/lib/main.dart b/lib/main.dart
index 07590f3..a4cdf94 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
import 'package:logger/logger.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
+import 'package:mohem_flutter_app/classes/navigationService.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/generated/codegen_loader.g.dart';
import 'package:mohem_flutter_app/models/post_params_model.dart';
@@ -28,6 +29,8 @@ Logger logger = Logger(
// output: null, // U
);
+late BuildContext callGlobalContext;
+
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
@@ -45,6 +48,7 @@ Future main() async {
]);
await EasyLocalization.ensureInitialized();
AppState().setPostParamsInitConfig();
+ setupLocator();
HttpOverrides.global = MyHttpOverrides();
isTablet = MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.shortestSide >= ApiConsts.tabletMinLength;
@@ -70,9 +74,9 @@ Future main() async {
ChangeNotifierProvider(
create: (_) => MarathonProvider(),
),
- // ChangeNotifierProvider(
- // create: (_) => ChatCallProvider(),
- // ),
+ ChangeNotifierProvider(
+ create: (_) => ChatCallProvider(),
+ ),
],
child: const MyApp(),
),
@@ -91,6 +95,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ callGlobalContext = context;
return Sizer(
builder: (
BuildContext context,
@@ -122,6 +127,7 @@ class MyApp extends StatelessWidget {
locale: context.locale,
initialRoute: AppRoutes.initialRoute,
routes: AppRoutes.routes,
+
);
},
);
diff --git a/lib/models/chat/call.dart b/lib/models/chat/call.dart
index ce58ae3..0f0e5a6 100644
--- a/lib/models/chat/call.dart
+++ b/lib/models/chat/call.dart
@@ -7,19 +7,31 @@ import 'dart:convert';
class CallDataModel {
CallDataModel({
this.callerId,
- this.callerDetails,
+ this.callerName,
+ this.callerEmail,
+ this.callerTitle,
+ this.callerPhone,
this.receiverId,
- this.receiverDetails,
+ this.receiverName,
+ this.receiverEmail,
+ this.receiverTitle,
+ this.receiverPhone,
this.title,
- this.calltype,
+ this.callType,
});
- String? callerId;
- CallerDetails? callerDetails;
- String? receiverId;
- ReceiverDetails? receiverDetails;
- dynamic title;
- String? calltype;
+ int? callerId;
+ String? callerName;
+ String? callerEmail;
+ String? callerTitle;
+ dynamic callerPhone;
+ int? receiverId;
+ String? receiverName;
+ String? receiverEmail;
+ dynamic receiverTitle;
+ dynamic receiverPhone;
+ String? title;
+ String? callType;
factory CallDataModel.fromRawJson(String str) => CallDataModel.fromJson(json.decode(str));
@@ -27,171 +39,92 @@ class CallDataModel {
factory CallDataModel.fromJson(Map json) => CallDataModel(
callerId: json["callerID"],
- callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]),
+ callerName: json["callerName"],
+ callerEmail: json["callerEmail"],
+ callerTitle: json["callerTitle"],
+ callerPhone: json["callerPhone"],
receiverId: json["receiverID"],
- receiverDetails: json["receiverDetails"] == null ? null : ReceiverDetails.fromJson(json["receiverDetails"]),
+ receiverName: json["receiverName"],
+ receiverEmail: json["receiverEmail"],
+ receiverTitle: json["receiverTitle"],
+ receiverPhone: json["receiverPhone"],
title: json["title"],
- calltype: json["calltype"],
+ callType: json["callType"],
);
Map toJson() => {
"callerID": callerId,
- "callerDetails": callerDetails?.toJson(),
+ "callerName": callerName,
+ "callerEmail": callerEmail,
+ "callerTitle": callerTitle,
+ "callerPhone": callerPhone,
"receiverID": receiverId,
- "receiverDetails": receiverDetails?.toJson(),
+ "receiverName": receiverName,
+ "receiverEmail": receiverEmail,
+ "receiverTitle": receiverTitle,
+ "receiverPhone": receiverPhone,
"title": title,
- "calltype": calltype,
+ "callType": callType,
};
}
-class CallerDetails {
- CallerDetails({
- this.response,
- this.errorResponses,
- });
- Response? response;
- dynamic errorResponses;
- factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str));
- String toRawJson() => json.encode(toJson());
- factory CallerDetails.fromJson(Map json) => CallerDetails(
- response: json["response"] == null ? null : Response.fromJson(json["response"]),
- errorResponses: json["errorResponses"],
- );
+// To parse this JSON data, do
+//
+// final callSessionPayLoad = callSessionPayLoadFromJson(jsonString);
- Map toJson() => {
- "response": response?.toJson(),
- "errorResponses": errorResponses,
- };
-}
-class Response {
- Response({
- this.id,
- this.userName,
- this.email,
- this.phone,
- this.title,
- this.token,
- this.isDomainUser,
- this.isActiveCode,
- this.encryptedUserId,
- this.encryptedUserName,
+class CallSessionPayLoad {
+ CallSessionPayLoad({
+ this.target,
+ this.caller,
+ this.sdp,
});
- int? id;
- String? userName;
- String? email;
- dynamic phone;
- String? title;
- String? token;
- bool? isDomainUser;
- bool? isActiveCode;
- String? encryptedUserId;
- String? encryptedUserName;
+ int? target;
+ int? caller;
+ Sdp? sdp;
- factory Response.fromRawJson(String str) => Response.fromJson(json.decode(str));
+ factory CallSessionPayLoad.fromRawJson(String str) => CallSessionPayLoad.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
- factory Response.fromJson(Map json) => Response(
- id: json["id"],
- userName: json["userName"],
- email: json["email"],
- phone: json["phone"],
- title: json["title"],
- token: json["token"],
- isDomainUser: json["isDomainUser"],
- isActiveCode: json["isActiveCode"],
- encryptedUserId: json["encryptedUserId"],
- encryptedUserName: json["encryptedUserName"],
+ factory CallSessionPayLoad.fromJson(Map json) => CallSessionPayLoad(
+ target: json["target"],
+ caller: json["caller"],
+ sdp: json["sdp"] == null ? null : Sdp.fromJson(json["sdp"]),
);
Map toJson() => {
- "id": id,
- "userName": userName,
- "email": email,
- "phone": phone,
- "title": title,
- "token": token,
- "isDomainUser": isDomainUser,
- "isActiveCode": isActiveCode,
- "encryptedUserId": encryptedUserId,
- "encryptedUserName": encryptedUserName,
+ "target": target,
+ "caller": caller,
+ "sdp": sdp?.toJson(),
};
}
-class ReceiverDetails {
- ReceiverDetails({
- this.id,
- this.userName,
- this.email,
- this.phone,
- this.title,
- this.userStatus,
- this.image,
- this.unreadMessageCount,
- this.userAction,
- this.isPin,
- this.isFav,
- this.isAdmin,
- this.rKey,
- this.totalCount,
+class Sdp {
+ Sdp({
+ this.type,
+ this.sdp,
});
- int? id;
- String? userName;
- String? email;
- dynamic phone;
- dynamic title;
- int? userStatus;
- String? image;
- int? unreadMessageCount;
- dynamic userAction;
- bool? isPin;
- bool? isFav;
- bool? isAdmin;
- String? rKey;
- int? totalCount;
-
- factory ReceiverDetails.fromRawJson(String str) => ReceiverDetails.fromJson(json.decode(str));
+ String? type;
+ String? sdp;
+
+ factory Sdp.fromRawJson(String str) => Sdp.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
- factory ReceiverDetails.fromJson(Map json) => ReceiverDetails(
- id: json["id"],
- userName: json["userName"],
- email: json["email"],
- phone: json["phone"],
- title: json["title"],
- userStatus: json["userStatus"],
- image: json["image"],
- unreadMessageCount: json["unreadMessageCount"],
- userAction: json["userAction"],
- isPin: json["isPin"],
- isFav: json["isFav"],
- isAdmin: json["isAdmin"],
- rKey: json["rKey"],
- totalCount: json["totalCount"],
+ factory Sdp.fromJson(Map json) => Sdp(
+ type: json["type"],
+ sdp: json["sdp"],
);
Map toJson() => {
- "id": id,
- "userName": userName,
- "email": email,
- "phone": phone,
- "title": title,
- "userStatus": userStatus,
- "image": image,
- "unreadMessageCount": unreadMessageCount,
- "userAction": userAction,
- "isPin": isPin,
- "isFav": isFav,
- "isAdmin": isAdmin,
- "rKey": rKey,
- "totalCount": totalCount,
+ "type": type,
+ "sdp": sdp,
};
}
diff --git a/lib/models/chat/incoming_call_model.dart b/lib/models/chat/incoming_call_model.dart
new file mode 100644
index 0000000..f49144f
--- /dev/null
+++ b/lib/models/chat/incoming_call_model.dart
@@ -0,0 +1,329 @@
+// To parse this JSON data, do
+//
+// final incomingCallModel = incomingCallModelFromJson(jsonString);
+
+import 'dart:convert';
+
+class IncomingCallModel {
+ String? actionColor;
+ String? appName;
+ Args? args;
+ String? avatar;
+ String? backgroundColor;
+ String? backgroundUrl;
+ int? duration;
+ Extra? extra;
+ String? from;
+ String? handle;
+ Args? headers;
+ String? id;
+ bool? isAccepted;
+ bool? isCustomNotification;
+ bool? isCustomSmallExNotification;
+ bool? isShowCallback;
+ bool? isShowLogo;
+ bool? isShowMissedCallNotification;
+ String? nameCaller;
+ String? ringtonePath;
+ String? textAccept;
+ String? textCallback;
+ String? textDecline;
+ String? textMissedCall;
+ int? type;
+ String? uuid;
+
+ IncomingCallModel({
+ this.actionColor,
+ this.appName,
+ this.args,
+ this.avatar,
+ this.backgroundColor,
+ this.backgroundUrl,
+ this.duration,
+ this.extra,
+ this.from,
+ this.handle,
+ this.headers,
+ this.id,
+ this.isAccepted,
+ this.isCustomNotification,
+ this.isCustomSmallExNotification,
+ this.isShowCallback,
+ this.isShowLogo,
+ this.isShowMissedCallNotification,
+ this.nameCaller,
+ this.ringtonePath,
+ this.textAccept,
+ this.textCallback,
+ this.textDecline,
+ this.textMissedCall,
+ this.type,
+ this.uuid,
+ });
+
+ factory IncomingCallModel.fromRawJson(String str) => IncomingCallModel.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory IncomingCallModel.fromJson(Map json) => IncomingCallModel(
+ actionColor: json["actionColor"],
+ appName: json["appName"],
+ args: json["args"] == null ? null : Args.fromJson(json["args"]),
+ avatar: json["avatar"],
+ backgroundColor: json["backgroundColor"],
+ backgroundUrl: json["backgroundUrl"],
+ duration: json["duration"] == null ? null : json["duration"].toInt(),
+ extra: json["extra"] == null ? null : Extra.fromJson(json["extra"]),
+ from: json["from"],
+ handle: json["handle"],
+ headers: json["headers"] == null ? null : Args.fromJson(json["headers"]),
+ id: json["id"],
+ isAccepted: json["isAccepted"],
+ isCustomNotification: json["isCustomNotification"],
+ isCustomSmallExNotification: json["isCustomSmallExNotification"],
+ isShowCallback: json["isShowCallback"],
+ isShowLogo: json["isShowLogo"],
+ isShowMissedCallNotification: json["isShowMissedCallNotification"],
+ nameCaller: json["nameCaller"],
+ ringtonePath: json["ringtonePath"],
+ textAccept: json["textAccept"],
+ textCallback: json["textCallback"],
+ textDecline: json["textDecline"],
+ textMissedCall: json["textMissedCall"],
+ type: json["type"] == null ? null : json["type"].toInt(),
+ uuid: json["uuid"],
+ );
+
+ Map toJson() => {
+ "actionColor": actionColor,
+ "appName": appName,
+ "args": args?.toJson(),
+ "avatar": avatar,
+ "backgroundColor": backgroundColor,
+ "backgroundUrl": backgroundUrl,
+ "duration": duration,
+ "extra": extra?.toJson(),
+ "from": from,
+ "handle": handle,
+ "headers": headers?.toJson(),
+ "id": id,
+ "isAccepted": isAccepted,
+ "isCustomNotification": isCustomNotification,
+ "isCustomSmallExNotification": isCustomSmallExNotification,
+ "isShowCallback": isShowCallback,
+ "isShowLogo": isShowLogo,
+ "isShowMissedCallNotification": isShowMissedCallNotification,
+ "nameCaller": nameCaller,
+ "ringtonePath": ringtonePath,
+ "textAccept": textAccept,
+ "textCallback": textCallback,
+ "textDecline": textDecline,
+ "textMissedCall": textMissedCall,
+ "type": type,
+ "uuid": uuid,
+ };
+}
+
+class Args {
+ Args();
+
+ factory Args.fromRawJson(String str) => Args.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory Args.fromJson(Map json) => Args();
+
+ Map toJson() => {};
+}
+
+class Extra {
+ LoginDetails? loginDetails;
+ bool? isIncomingCall;
+ CallerDetails? callerDetails;
+
+ Extra({
+ this.loginDetails,
+ this.isIncomingCall,
+ this.callerDetails,
+ });
+
+ factory Extra.fromRawJson(String str) => Extra.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory Extra.fromJson(Map json) => Extra(
+ loginDetails: json["loginDetails"] == null ? null : LoginDetails.fromJson(json["loginDetails"]),
+ isIncomingCall: json["isIncomingCall"],
+ callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]),
+ );
+
+ Map toJson() => {
+ "loginDetails": loginDetails?.toJson(),
+ "isIncomingCall": isIncomingCall,
+ "callerDetails": callerDetails?.toJson(),
+ };
+}
+
+class CallerDetails {
+ int? userChatHistoryId;
+ String? contant;
+ FileTypeResponse? fileTypeResponse;
+ String? currentUserName;
+ String? targetUserEmail;
+ String? conversationId;
+ String? encryptedTargetUserId;
+ int? targetUserId;
+ bool? isSeen;
+ int? userChatHistoryLineId;
+ bool? isDelivered;
+ String? targetUserName;
+ int? currentUserId;
+ DateTime? createdDate;
+ String? currentUserEmail;
+ String? contantNo;
+ int? chatEventId;
+ String? encryptedTargetUserName;
+ int? chatSource;
+
+ CallerDetails({
+ this.userChatHistoryId,
+ this.contant,
+ this.fileTypeResponse,
+ this.currentUserName,
+ this.targetUserEmail,
+ this.conversationId,
+ this.encryptedTargetUserId,
+ this.targetUserId,
+ this.isSeen,
+ this.userChatHistoryLineId,
+ this.isDelivered,
+ this.targetUserName,
+ this.currentUserId,
+ this.createdDate,
+ this.currentUserEmail,
+ this.contantNo,
+ this.chatEventId,
+ this.encryptedTargetUserName,
+ this.chatSource,
+ });
+
+ factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory CallerDetails.fromJson(Map json) => CallerDetails(
+ userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"].toInt(),
+ contant: json["contant"],
+ fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]),
+ currentUserName: json["currentUserName"],
+ targetUserEmail: json["targetUserEmail"],
+ conversationId: json["conversationId"],
+ encryptedTargetUserId: json["encryptedTargetUserId"],
+ targetUserId: json["targetUserId"] == null ? null : json["targetUserId"].toInt(),
+ isSeen: json["isSeen"],
+ userChatHistoryLineId: json["userChatHistoryLineId"] == null ? null : json["userChatHistoryLineId"].toInt(),
+ isDelivered: json["isDelivered"],
+ targetUserName: json["targetUserName"],
+ currentUserId: json["currentUserId"] == null ? null : json["currentUserId"].toInt(),
+ createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]),
+ currentUserEmail: json["currentUserEmail"],
+ contantNo: json["contantNo"],
+ chatEventId: json["chatEventId"] == null ? null : json["chatEventId"].toInt(),
+ encryptedTargetUserName: json["encryptedTargetUserName"],
+ chatSource: json["chatSource"] == null ? null : json["chatSource"].toInt(),
+ );
+
+ Map toJson() => {
+ "userChatHistoryId": userChatHistoryId,
+ "contant": contant,
+ "fileTypeResponse": fileTypeResponse?.toJson(),
+ "currentUserName": currentUserName,
+ "targetUserEmail": targetUserEmail,
+ "conversationId": conversationId,
+ "encryptedTargetUserId": encryptedTargetUserId,
+ "targetUserId": targetUserId,
+ "isSeen": isSeen,
+ "userChatHistoryLineId": userChatHistoryLineId,
+ "isDelivered": isDelivered,
+ "targetUserName": targetUserName,
+ "currentUserId": currentUserId,
+ "createdDate": createdDate?.toIso8601String(),
+ "currentUserEmail": currentUserEmail,
+ "contantNo": contantNo,
+ "chatEventId": chatEventId,
+ "encryptedTargetUserName": encryptedTargetUserName,
+ "chatSource": chatSource,
+ };
+}
+
+class FileTypeResponse {
+ int? fileTypeId;
+
+ FileTypeResponse({
+ this.fileTypeId,
+ });
+
+ factory FileTypeResponse.fromRawJson(String str) => FileTypeResponse.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory FileTypeResponse.fromJson(Map json) => FileTypeResponse(
+ fileTypeId: json["fileTypeId"].toInt(),
+ );
+
+ Map toJson() => {
+ "fileTypeId": fileTypeId,
+ };
+}
+
+class LoginDetails {
+ bool? isActiveCode;
+ int? id;
+ String? encryptedUserName;
+ String? userName;
+ String? title;
+ String? encryptedUserId;
+ String? email;
+ bool? isDomainUser;
+ String? token;
+
+ LoginDetails({
+ this.isActiveCode,
+ this.id,
+ this.encryptedUserName,
+ this.userName,
+ this.title,
+ this.encryptedUserId,
+ this.email,
+ this.isDomainUser,
+ this.token,
+ });
+
+ factory LoginDetails.fromRawJson(String str) => LoginDetails.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory LoginDetails.fromJson(Map json) => LoginDetails(
+ isActiveCode: json["isActiveCode"],
+ id: json["id"] == null ? null : json["id"].toInt(),
+ encryptedUserName: json["encryptedUserName"],
+ userName: json["userName"],
+ title: json["title"],
+ encryptedUserId: json["encryptedUserId"],
+ email: json["email"],
+ isDomainUser: json["isDomainUser"],
+ token: json["token"],
+ );
+
+ Map toJson() => {
+ "isActiveCode": isActiveCode,
+ "id": id,
+ "encryptedUserName": encryptedUserName,
+ "userName": userName,
+ "title": title,
+ "encryptedUserId": encryptedUserId,
+ "email": email,
+ "isDomainUser": isDomainUser,
+ "token": token,
+ };
+}
diff --git a/lib/models/chat/webrtc_payloads.dart b/lib/models/chat/webrtc_payloads.dart
new file mode 100644
index 0000000..1a3463f
--- /dev/null
+++ b/lib/models/chat/webrtc_payloads.dart
@@ -0,0 +1,61 @@
+// To parse this JSON data, do
+//
+// final remoteIceCandidatePayLoad = remoteIceCandidatePayLoadFromJson(jsonString);
+
+import 'dart:convert';
+
+class RemoteIceCandidatePayLoad {
+ RemoteIceCandidatePayLoad({
+ this.target,
+ this.candidate,
+ });
+
+ int? target;
+ Candidate? candidate;
+
+ factory RemoteIceCandidatePayLoad.fromRawJson(String str) => RemoteIceCandidatePayLoad.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory RemoteIceCandidatePayLoad.fromJson(Map json) => RemoteIceCandidatePayLoad(
+ target: json["target"],
+ candidate: json["candidate"] == null ? null : Candidate.fromJson(json["candidate"]),
+ );
+
+ Map toJson() => {
+ "target": target,
+ "candidate": candidate?.toJson(),
+ };
+}
+
+class Candidate {
+ Candidate({
+ this.candidate,
+ this.sdpMid,
+ this.sdpMLineIndex,
+ this.usernameFragment,
+ });
+
+ String? candidate;
+ String? sdpMid;
+ int? sdpMLineIndex;
+ String? usernameFragment;
+
+ factory Candidate.fromRawJson(String str) => Candidate.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory Candidate.fromJson(Map json) => Candidate(
+ candidate: json["candidate"],
+ sdpMid: json["sdpMid"],
+ sdpMLineIndex: json["sdpMLineIndex"],
+ usernameFragment: json["usernameFragment"],
+ );
+
+ Map toJson() => {
+ "candidate": candidate,
+ "sdpMid": sdpMid,
+ "sdpMLineIndex": sdpMLineIndex,
+ "usernameFragment": usernameFragment,
+ };
+}
diff --git a/lib/provider/chat_call_provider.dart b/lib/provider/chat_call_provider.dart
index 45205df..3660668 100644
--- a/lib/provider/chat_call_provider.dart
+++ b/lib/provider/chat_call_provider.dart
@@ -1,52 +1,87 @@
import 'dart:convert';
-import 'dart:ui';
-
-import 'package:flutter/cupertino.dart';
+import 'dart:io';
import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
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/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/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/landing/dashboard_screen.dart';
+import 'package:signalr_netcore/hub_connection.dart';
class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
///////////////////// Web RTC Video Calling //////////////////////
// Video Call
- late RTCPeerConnection _peerConnection;
- RTCVideoRenderer _localVideoRenderer = RTCVideoRenderer();
- final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
+ late RTCPeerConnection _pc;
+ late ChatProviderModel chatProvModel;
+ RTCVideoRenderer? localVideoRenderer;
+ RTCVideoRenderer? remoteRenderer;
+ final AudioPlayer player = AudioPlayer();
MediaStream? _localStream;
- MediaStream? _remoteStream;
+ late CallDataModel outGoingCallData;
+ bool isMicOff = false;
+ bool isLoudSpeaker = false;
+ bool isCamOff = false;
+ bool isCallEnded = false;
+ bool isVideoCall = false;
+ bool isAudioCall = false;
+ bool isCallStarted = false;
+ bool isFrontCamera = true;
+ late SingleUserChatModel incomingCallData;
+
+ /// WebRTC Connection Variables
+ bool isIncomingCallLoader = true;
+ bool isIncomingCall = false;
+ bool isOutGoingCall = false;
- void initCallListeners() {
+ late BuildContext providerContext;
+
+ void initCallListeners({required BuildContext context}) {
+ providerContext = context;
+ if (kDebugMode) {
+ print("=================== Call Listeners Registered =======================");
+ }
chatHubConnection.on("OnCallAcceptedAsync", onCallAcceptedAsync);
chatHubConnection.on("OnIceCandidateAsync", onIceCandidateAsync);
chatHubConnection.on("OnOfferAsync", onOfferAsync);
chatHubConnection.on("OnAnswerOffer", onAnswerOffer);
chatHubConnection.on("OnHangUpAsync", onHangUpAsync);
chatHubConnection.on("OnCallDeclinedAsync", onCallDeclinedAsync);
+ // chatHubConnection.on("OnIncomingCallAsync", OnIncomingCallAsync);
}
//Video Constraints
- var videoConstraints = {
+ Map videoConstraints = {
"video": {
"mandatory": {
- "width": {"min": 320},
- "height": {"min": 180}
+ "width": {"min": 1280},
+ "height": {"min": 720}
},
"optional": [
{
"width": {"max": 1280}
},
- {"frameRate": 25},
+ {"frameRate": 60},
{"facingMode": "user"}
]
},
- "frameRate": 25,
- "width": 420, //420,//640,//1280,
- "height": 240 //240//480//720
+ "frameRate": 60,
+ "width": 1280, //420,//640,//1280,
+ "height": 720, //240//480//720
+ "audio": true,
};
// Audio Constraints
- var audioConstraints = {
+ Map audioConstraints = {
"sampleRate": 8000,
"sampleSize": 16,
"channelCount": 2,
@@ -54,128 +89,292 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
"audio": true,
};
- Future _createPeerConnection() async {
- // {"url": "stun:stun.l.google.com:19302"},
- Map configuration = {
- "iceServers": [
- {"urls": 'stun:15.185.116.59:3478'},
- {"urls": "turn:15.185.116.59:3479", "username": "admin", "credential": "admin"}
- ]
- };
+ Future init() async {
+ _pc = await creatOfferWithCon();
+ Future.delayed(const Duration(seconds: 2), () {
+ connectIncomingCall();
+ });
+ }
- Map offerSdpConstraints = {
- "mandatory": {
- "OfferToReceiveAudio": true,
- "OfferToReceiveVideo": true,
- },
- "optional": [],
- };
+ ///////////////////////////////////////////////OutGoing Call////////////////////////////////////////////////////
- RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints);
- // if (pc != null) print(pc);
- //pc.addStream(widget.localStream);
-
- pc.onIceCandidate = (e) {
- if (e.candidate != null) {
- print(json.encode({
- 'candidate': e.candidate.toString(),
- 'sdpMid': e.sdpMid.toString(),
- 'sdpMlineIndex': e.sdpMLineIndex,
- }));
- }
- };
- pc.onIceConnectionState = (e) {
- print(e);
- };
- pc.onAddStream = (stream) {
- print('addStream: ' + stream.id);
- _remoteRenderer.srcObject = stream;
- };
- return pc;
+ Future initLocalCamera({required ChatProviderModel chatProvmodel, required callData, required BuildContext context, bool isIncomingCall = false}) async {
+ isCallEnded = false;
+ chatProvModel = chatProvmodel;
+ outGoingCallData = callData;
+ await initStreams();
+ await startCall(callType: isVideoCall ? "Video" : "Audio", context: context);
+ _pc = await creatOfferWithCon();
+ notifyListeners();
}
- void init() {
- initRenderers();
- _createPeerConnection().then((pc) {
- _peerConnection = pc;
- // _setRemoteDescription(widget.info);
- });
+ Future startCall({required String callType, required BuildContext context}) async {
+ chatProvModel.isTextMsg = true;
+ chatProvModel.isAttachmentMsg = false;
+ chatProvModel.isVoiceMsg = false;
+ chatProvModel.isReplyMsg = false;
+ chatProvModel.isCall = true;
+ chatProvModel.message.text = "Start $callType call ${outGoingCallData.receiverName.toString().replaceAll(".", " ")}";
+ chatProvModel.sendChatMessage(
+ context,
+ targetUserId: outGoingCallData.receiverId!,
+ userStatus: 1,
+ userEmail: outGoingCallData.receiverEmail!,
+ targetUserName: outGoingCallData.receiverName!,
+ );
+ await invoke(
+ invokeMethod: "CallUserAsync",
+ currentUserID: outGoingCallData.callerId!,
+ targetUserID: outGoingCallData.receiverId!,
+ );
+ await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 4);
}
- void initRenderers() {
- _localVideoRenderer.initialize();
- _remoteRenderer.initialize();
- initLocalCamera();
+ // OutGoing Listeners
+ void onCallAcceptedAsync(List