Chat Fix & Calling

merge-requests/188/head
Aamir Muhammad 3 years ago
parent 11227b00b4
commit c4c7a91d7b

@ -0,0 +1,196 @@
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/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;
Future<void> showCallkitIncoming({required String uuid, RemoteMessage? data, CallDataModel? incomingCallData, bool background = false}) async {
await ChatVoipCall().listenerEvent();
ALM.Response autoLoginData;
SingleUserChatModel callerData;
// if(!background){}
await initProviders();
if (data!.data["user_token_response"] == null || data.data["user_token_response"].isEmpty) {
autoLoginData = ALM.Response.fromJson(AppState().getchatUserDetails!.response!.toJson());
dynamic items = jsonDecode(data!.data["user_chat_history_response"]);
callerData = SingleUserChatModel(
targetUserId: items["CurrentUserId"],
targetUserEmail: items["CurrentUserName"],
targetUserName: items["CurrentUserEmail"],
currentUserId: autoLoginData.id,
currentUserEmail: autoLoginData.email,
currentUserName: autoLoginData.userName!.split("@").first,
chatEventId: 3);
isUserOnline = true;
} else {
autoLoginData = ALM.Response.fromJson(jsonDecode(data.data["user_token_response"]));
callerData = SingleUserChatModel.fromJson(json.decode(data!.data["user_chat_history_response"]));
}
if (data!.data["callType"] == "video") {
cProv.isVideoCall = true;
} else {
cProv.isAudioCall = true;
cProv.isVideoCall = false;
}
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");
connection(
data: jsonEncode([
{"loginDetails": autoLoginData.toJson(), "callerDetails": callerData.toJson()}
]),
isUserOnline: isUserOnline);
await FlutterCallkitIncoming.showCallkitIncoming(params);
}
}
Future<void> initProviders() async {
cProv = Provider.of<ChatCallProvider>(AppRoutes.navigatorKey.currentContext!, listen: false);
prov = Provider.of<ChatProviderModel>(AppRoutes.navigatorKey.currentContext!, listen: false);
}
void connection({required data, required bool isUserOnline}) async {
cProv.isUserOnline = isUserOnline;
if (isUserOnline) {
cProv.startIncomingCallViaKit(inCallData: data);
} else {
dynamic callData = await jsonDecode(data);
AppState().setchatUserDetails = UserAutoLoginModel(
response: Response.fromJson(callData[0]["loginDetails"]),
errorResponses: null,
);
cProv.startIncomingCallViaKit(inCallData: data);
try {
await prov.buildHubConnection(context: AppRoutes.navigatorKey.currentContext!, ccProvider: cProv).whenComplete(() {
cProv.init();
});
} catch (e) {
logger.w(e);
}
}
}
//Function(CallEvent) callback
Future<void> listenerEvent() async {
try {
FlutterCallkitIncoming.onEvent.listen((event) async {
switch (event!.event) {
case Event.ACTION_CALL_INCOMING:
// TODO: received an incoming call
break;
case Event.ACTION_CALL_START:
// TODO: started an outgoing call
// TODO: show screen calling in Flutter
break;
case Event.ACTION_CALL_ACCEPT:
if (isUserOnline) {
cProv.init();
}
Navigator.pushNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall);
// Navigator.pushNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.chatStartCall);
break;
case Event.ACTION_CALL_DECLINE:
cProv.isIncomingCall = true;
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
cProv.endCall();
break;
case Event.ACTION_CALL_ENDED:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
break;
case Event.ACTION_CALL_TIMEOUT:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
break;
case Event.ACTION_CALL_CALLBACK:
// TODO: only Android - click action `Call back` from missed call notification
break;
case Event.ACTION_CALL_TOGGLE_HOLD:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_MUTE:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_DMTF:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_GROUP:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_AUDIO_SESSION:
// TODO: only iOS
break;
case Event.ACTION_DID_UPDATE_DEVICE_PUSH_TOKEN_VOIP:
// TODO: only iOS
break;
}
});
} on Exception {}
}
}

@ -8,7 +8,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.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';
import 'package:mohem_flutter_app/classes/voip_chat_call.dart';
import 'package:mohem_flutter_app/classes/chat_call_kit.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';
@ -60,7 +60,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);
@ -116,25 +116,28 @@ 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') {
if( Platform.isAndroid){
ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message);
}
}
}
void _handleOpenApp(RemoteMessage message) {
logger.i("Open App Message:" + message.toMap().toString());
if (message.data.isNotEmpty && message.data["messageType"] == 'chat') {
Utils.saveStringFromPrefs("isAppOpendByChat", "true");
Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString());
}
}
}
AndroidNotificationChannel channel = const AndroidNotificationChannel(
AndroidNotificationChannel channel = AndroidNotificationChannel(
"high_importance_channel",
"High Importance Notifications",
importance: Importance.high,
@ -146,6 +149,8 @@ Future<dynamic> backgroundMessageHandler(RemoteMessage message) async {
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);
if( Platform.isAndroid) {
ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message, background: true);
}
}
}

@ -1,188 +0,0 @@
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/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;
Future<void> showCallkitIncoming({required String uuid, RemoteMessage? data, bool isOnline = false, CallDataModel? incomingCallData}) async {
await ChatVoipCall().listenerEvent();
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"]));
data!.data["callType"] == "video" ? cProv.isVideoCall = true : cProv.isVideoCall = false;
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<void> initProviders() async {
cProv = Provider.of<ChatCallProvider>(AppRoutes.navigatorKey.currentContext!, listen: false);
prov = Provider.of<ChatProviderModel>(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,
);
cProv.startIncomingCallViaKit(inCallData: data);
try {
await prov.buildHubConnection(context: AppRoutes.navigatorKey.currentContext!, ccProvider: cProv).whenComplete(() {
cProv.init();
});
} catch (e) {
logger.w(e);
}
}
//Function(CallEvent) callback
Future<void> listenerEvent() async {
try {
FlutterCallkitIncoming.onEvent.listen((event) async {
switch (event!.event) {
case Event.ACTION_CALL_INCOMING:
// TODO: received an incoming call
break;
case Event.ACTION_CALL_START:
// TODO: started an outgoing call
// 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:
cProv.isIncomingCall = true;
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
cProv.endCall();
break;
case Event.ACTION_CALL_ENDED:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
break;
case Event.ACTION_CALL_TIMEOUT:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
break;
case Event.ACTION_CALL_CALLBACK:
// TODO: only Android - click action `Call back` from missed call notification
break;
case Event.ACTION_CALL_TOGGLE_HOLD:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_MUTE:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_DMTF:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_GROUP:
// TODO: only iOS
break;
case Event.ACTION_CALL_TOGGLE_AUDIO_SESSION:
// TODO: only iOS
break;
case Event.ACTION_DID_UPDATE_DEVICE_PUSH_TOKEN_VOIP:
// TODO: only iOS
break;
}
});
} on Exception {}
}
}

@ -1,13 +1,12 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.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/classes/voip_chat_call.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';
@ -16,7 +15,6 @@ 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 {
///////////////////// Web RTC Video Calling //////////////////////
@ -33,19 +31,18 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
bool isCamOff = false;
bool isCallEnded = false;
bool isVideoCall = false;
bool isAudioCall = false;
bool isCallStarted = false;
bool isFrontCamera = true;
bool isOnIncomingCallPage = false;
late SingleUserChatModel incomingCallData;
/// WebRTC Connection Variables
//bool _offer = false;
bool isIncomingCallLoader = true;
bool isIncomingCall = false;
late BuildContext providerContext;
bool isOutGoingCall = false;
bool isUserOnline = false;
/// Temp Variable Remove After Development
var tempPayLoad;
late BuildContext providerContext;
void initCallListeners({required BuildContext context}) {
providerContext = context;
@ -72,11 +69,11 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
{
"width": {"max": 1280}
},
{"frameRate": 25},
{"frameRate": 60},
{"facingMode": "user"}
]
},
"frameRate": 25,
"frameRate": 60,
"width": 1280, //420,//640,//1280,
"height": 720, //240//480//720
"audio": true,
@ -98,16 +95,16 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
});
}
///////////////////////////////////////////////OutGoing Call////////////////////////////////////////////////////
Future<void> initLocalCamera({required ChatProviderModel chatProvmodel, required callData, required BuildContext context, bool isIncomingCall = false}) async {
isCallEnded = false;
chatProvModel = chatProvmodel;
outGoingCallData = callData;
await localVideoRenderer.initialize();
localStream = await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints);
localVideoRenderer.srcObject = localStream;
await remoteRenderer.initialize();
// playRingtone();
await startCall(callType: isVideoCall ? "Video" : "Audio", context: context);
_pc = await creatOfferWithCon();
notifyListeners();
@ -135,57 +132,17 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 4);
}
Future<bool> endCall() async {
if (isIncomingCall) {
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;
isMicOff = false;
isLoudSpeaker = false;
localVideoRenderer.srcObject = null;
remoteRenderer.srcObject = null;
isIncomingCall = false;
return true;
} else {
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;
isMicOff = false;
isLoudSpeaker = false;
localVideoRenderer.srcObject = null;
remoteRenderer.srcObject = null;
//player.stop();
// _offer = false;
return true;
}
}
// OutGoing Listeners
void onCallAcceptedAsync(List<Object?>? params) async {
print("--------------------- On Call Accept ---------------------------------------");
dynamic items = params!.toList();
RTCSessionDescription description = await _createOffer();
await _pc.setLocalDescription(description);
var payload = {"target": items[0]["id"], "caller": outGoingCallData.callerId, "sdp": description.toMap()};
dynamic payload = {"target": items[0]["id"], "caller": outGoingCallData.callerId, "sdp": description.toMap()};
invoke(invokeMethod: "OfferAsync", currentUserID: outGoingCallData.callerId!, targetUserID: items[0]["id"], data: jsonEncode(payload));
}
Future<void> onIceCandidateAsync(List<Object?>? params) async {
var items = params!.toList();
dynamic items = params!.toList();
if (isIncomingCall) {
RemoteIceCandidatePayLoad data = RemoteIceCandidatePayLoad.fromJson(jsonDecode(items.first.toString()));
if (_pc != null) {
@ -223,7 +180,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
_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()};
dynamic 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 {
@ -235,6 +192,59 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
//////////////////////////// OutGoing Call End ///////////////////////////////////////
Future<bool> endCall() async {
if (isIncomingCall) {
logger.i("-----------------------Endeddddd By Me---------------------------");
if (chatHubConnection.state == HubConnectionState.Connected) {
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 0);
await invoke(invokeMethod: "HangUpAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 0);
}
if (_pc.connectionState == RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
_pc.dispose();
}
isCallStarted = false;
isVideoCall = false;
isCamOff = false;
isMicOff = false;
isLoudSpeaker = false;
localVideoRenderer.srcObject = null;
remoteRenderer.srcObject = null;
isIncomingCall = false;
if (chatHubConnection != null) {
chatHubConnection.stop();
}
return true;
} else {
if (isOutGoingCall) {
await invoke(invokeMethod: "HangUpAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1);
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 0);
} else {
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: AppState().chatDetails!.response!.id!, targetUserID: incomingCallData.targetUserId!, userStatus: 1);
}
if (_pc.connectionState == RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
_pc.dispose();
}
isCallStarted = false;
isVideoCall = false;
isCamOff = false;
isMicOff = false;
isLoudSpeaker = false;
localVideoRenderer.srcObject = null;
remoteRenderer.srcObject = null;
isOutGoingCall = false;
return true;
}
}
Future<void> reInit() async {
await localVideoRenderer.initialize();
localStream = await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints);
// localVideoRenderer.srcObject = localStream;
await remoteRenderer.initialize();
}
// Incoming Listeners
void onAnswerOffer(List<Object?>? payload) async {
@ -254,69 +264,73 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
void onHangUpAsync(List<Object?>? params) {
print("--------------------- onHangUp ---------------------------------------");
isOutGoingCall = false;
// isEndedByCaller = true;
endCall().then((bool value) {
isCallEnded = true;
Navigator.of(AppRoutes.navigatorKey.currentContext!).pop();
notifyListeners();
isCallEnded = true;
});
}
Future<void> OnIncomingCallAsync(List<Object?>? params) async {
print("--------------------- On Incoming Call ---------------------------------------");
dynamic items = params!.toList();
logger.d(items);
// Map<String, dynamic> 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<String, dynamic> 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;
// Future<void> OnIncomingCallAsync(List<Object?>? params) async {
// print("--------------------- On Incoming Call ---------------------------------------");
// dynamic items = params!.toList();
// logger.d(items);
// // Map<String, dynamic> 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<String, dynamic> 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<Object?>? params) {
endCall().then((bool value) {
if (value) {
isCallEnded = true;
notifyListeners();
}
});
print("================= On Declained ========================");
// endCall().then((bool value) {
// if (value) {
// isCallEnded = true;
// notifyListeners();
// }
// });
}
//// Invoke Methods
@ -356,12 +370,26 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
chatHubConnection.off('OnAnswerOffer');
}
void disposeRenders() async {
await localVideoRenderer.dispose();
localStream.dispose();
notifyListeners();
void playRingtone() async {
player.stop();
await player.setVolume(1.0);
String audioAsset = "";
if (Platform.isAndroid) {
audioAsset = "assets/audio/ring_60Sec.mp3";
} else {
audioAsset = "assets/audio/ring_30Sec.caf";
}
try {
await player.setAsset(audioAsset);
await player.load();
player.play();
} catch (e) {
print("Error: $e");
}
}
//////////////////// Web RTC Offers & Connections ////////////////////////
Future<RTCPeerConnection> creatOfferWithCon() async {
Map<String, dynamic> configuration = {
"sdpSemantics": "plan-b",
@ -385,7 +413,6 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
};
RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints);
logger.w(pc.connectionState);
await pc!.addStream(localStream!);
pc?.onConnectionState = (RTCPeerConnectionState state) {};
pc?.onAddStream = (MediaStream stream) {
@ -420,11 +447,11 @@ 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});
// 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);
@ -434,10 +461,12 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
RTCIceConnectionState.RTCIceConnectionStateDisconnected == state ||
RTCIceConnectionState.RTCIceConnectionStateClosed == state) {
logger.i("Ice Connection State:" + state.name);
endCall();
// endCall().then((value) {
// notifyListeners();
// });
}
};
pc!.onRenegotiationNeeded = _onRenegotiate;
// pc!.onRenegotiationNeeded = _onRenegotiate;
return pc;
}
@ -459,24 +488,6 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
}
}
void playRingtone() async {
player.stop();
await player.setVolume(1.0);
String audioAsset = "";
if (Platform.isAndroid) {
audioAsset = "assets/audio/ring_60Sec.mp3";
} else {
audioAsset = "assets/audio/ring_30Sec.caf";
}
try {
await player.setAsset(audioAsset);
await player.load();
player.play();
} catch (e) {
print("Error: $e");
}
}
Future<RTCSessionDescription> _createOffer() async {
RTCSessionDescription description = await _pc!.createOffer();
// _offer = true;
@ -489,6 +500,10 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
return description;
}
//////////////////// Web RTC End Offers ////////////////////
//////////////////// CallPage Buttons //////////////////////
void micOff() {
isMicOff = !isMicOff;
localStream.getAudioTracks().forEach((track) {
@ -502,11 +517,11 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
localStream.getVideoTracks().forEach((track) {
track.enabled = !track.enabled;
});
if (isCamOff) {
isVideoCall = false;
} else {
isVideoCall = true;
}
// if (isCamOff) {
// isVideoCall = false;
// } else {
// isVideoCall = true;
// }
notifyListeners();
}
@ -528,7 +543,11 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
Future<void> startIncomingCall() async {
/////////////////// End Call Page Buttons /////////////////////
///////////////// Incoming Call ///////////////////////////////
Future<void> initStreams() async {
await localVideoRenderer.initialize();
localStream = await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints);
localVideoRenderer.srcObject = localStream;
@ -538,10 +557,11 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
Future<void> startIncomingCallViaKit({bool isVCall = true, required var inCallData}) async {
Utils.saveStringFromPrefs("isIncomingCall", "false");
isVideoCall = isVCall;
await startIncomingCall();
await initStreams();
isIncomingCall = true;
dynamic callData = await jsonDecode(inCallData);
incomingCallData = SingleUserChatModel.fromJson(callData[0]["callerDetails"]);
loudOn();
notifyListeners();
}
@ -551,4 +571,13 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
isVideoCall = true;
notifyListeners();
}
////////////////// End Incoming Call ////////////////////////
//Extra
// void disposeRenders() async {
// await localVideoRenderer.dispose();
// localStream.dispose();
// notifyListeners();
// }
}

@ -580,7 +580,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
msg = voiceFile!.path.split("/").last;
} else {
msg = message.text;
logger.w(msg);
}
SingleUserChatModel data = SingleUserChatModel(
userChatHistoryId: 0,

@ -25,7 +25,7 @@ class OutGoingCall extends StatefulWidget {
_OutGoingCallState createState() => _OutGoingCallState();
}
class _OutGoingCallState extends State<OutGoingCall>{
class _OutGoingCallState extends State<OutGoingCall> {
late ChatCallProvider callProvider;
late ChatProviderModel chatProvider;
@ -39,19 +39,19 @@ class _OutGoingCallState extends State<OutGoingCall>{
void init() {
widget.isVideoCall ? callProvider.isVideoCall = true : callProvider.isVideoCall = false;
callProvider.isOutGoingCall = true;
callProvider.initLocalCamera(chatProvmodel: chatProvider, callData: widget.outGoingCallData, context: context);
//callProvider.init();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider chatcp, Widget? child) {
body:Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider chatcp, Widget? child) {
if (chatcp.isCallEnded) Navigator.pop(context);
return Stack(
alignment: FractionalOffset.center,
children: <Widget>[
if (widget.isVideoCall)
if (chatcp.isVideoCall)
Positioned.fill(
child: RTCVideoView(
callProvider.localVideoRenderer,

@ -1,14 +1,17 @@
import 'dart:convert';
import 'dart:core';
import 'dart:io';
import 'dart:ui';
import 'package:draggable_widget/draggable_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
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/extensions/string_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';
@ -25,24 +28,14 @@ class StartCallPage extends StatefulWidget {
class _StartCallPageState extends State<StartCallPage> {
final dragController = DragController();
late ChatProviderModel chatProvider;
late ChatCallProvider callProvider;
var inCallData;
//userChatDetails
@override
void initState() {
// callProvider = Provider.of<ChatCallProvider>(context, listen: false);
// chatProvider = Provider.of<ChatProviderModel>(context, listen: false);
// connection();
super.initState();
}
@override
void dispose() {
if (chatHubConnection != null) {
chatHubConnection.stop();
}
super.dispose();
}
@ -70,14 +63,14 @@ class _StartCallPageState extends State<StartCallPage> {
return Scaffold(
extendBody: true,
body: Consumer2<ChatCallProvider, ChatProviderModel>(builder: (BuildContext context, ChatCallProvider provider, ChatProviderModel cpm, Widget? child) {
// if (provider.isEndedByCaller && Platform.isAndroid) SystemNavigator.pop();
return SizedBox(
width: double.infinity,
height: double.infinity,
child: provider.isVideoCall
? Stack(
child:Stack(
alignment: FractionalOffset.center,
children: <Widget>[
if (provider.isVideoCall)
if(!provider.isAudioCall && provider.isVideoCall)
Positioned.fill(
child: RTCVideoView(
provider.remoteRenderer!,
@ -203,7 +196,7 @@ class _StartCallPageState extends State<StartCallPage> {
provider.loudOn();
},
elevation: 2.0,
fillColor: provider.isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
fillColor: provider.isLoudSpeaker ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
@ -220,7 +213,7 @@ class _StartCallPageState extends State<StartCallPage> {
provider.camOff();
},
elevation: 2.0,
fillColor: provider.isCamOff ? MyColors.green2DColor : Colors.grey,
fillColor: provider.isCamOff ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
@ -237,7 +230,7 @@ class _StartCallPageState extends State<StartCallPage> {
provider.switchCamera();
},
elevation: 2.0,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.textMixColor,
padding: const EdgeInsets.all(
10.0,
),
@ -254,7 +247,7 @@ class _StartCallPageState extends State<StartCallPage> {
provider.micOff();
},
elevation: 2.0,
fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey,
fillColor: provider.isMicOff ? MyColors.textMixColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
@ -292,14 +285,14 @@ class _StartCallPageState extends State<StartCallPage> {
),
],
)
: const SizedBox(
width: double.infinity,
height: 500,
child: Center(
child: CircularProgressIndicator(),
),
));
);
}),
);
}
}
/// if (Platform.isAndroid) {
// SystemNavigator.pop();
// } else if (Platform.isIOS) {
// exit(0);
// }

@ -1,5 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -96,6 +96,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
showTyping: true,
chatUser: params!.chatUser,
actions: [
if (Platform.isAndroid)
SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() async {
Future<PermissionStatus> micPer = Permission.microphone.request();
if (await micPer.isGranted) {
@ -106,7 +107,8 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
});
}
}),
24.width,
if (Platform.isAndroid) 24.width,
if (Platform.isAndroid)
SvgPicture.asset("assets/icons/chat/video_call.svg", width: 21, height: 18).onPress(() async {
Future<PermissionStatus> camPer = Permission.camera.request();
if (await camPer.isGranted) {
@ -117,7 +119,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
});
}
}),
21.width,
if (Platform.isAndroid) 21.width,
],
),
body: SafeArea(

Loading…
Cancel
Save