Webrtc Calling

merge-requests/188/head
Aamir Muhammad 3 years ago
parent 304f77b9bb
commit b21142e4af

@ -133,6 +133,5 @@ Future<dynamic> backgroundMessageHandler(RemoteMessage message) async {
await Firebase.initializeApp();
Utils.saveStringFromPrefs("isAppOpendByChat", "false");
Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString());
ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message);
}

@ -1,10 +1,14 @@
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/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' as ALM;
class ChatVoipCall {
static final ChatVoipCall _instance = ChatVoipCall._internal();
@ -14,51 +18,112 @@ class ChatVoipCall {
factory ChatVoipCall() => _instance;
Future<void> showCallkitIncoming({required String uuid, required RemoteMessage data}) async {
logger.d(data.data["user_chat_history_response"]);
await ChatVoipCall().listenerEvent();
SingleUserChatModel callerData = SingleUserChatModel.fromJson(jsonDecode(data.data["user_chat_history_response"]));
var params = CallKitParams(
id: uuid,
nameCaller: callerData.currentUserName,
appName: 'Mohemm',
avatar: 'https://i.pravatar.cc/100',
handle: '0123456789',
type: 0,
duration: 30000,
textAccept: 'Accept',
textDecline: 'Decline',
textMissedCall: 'Missed call',
textCallback: 'Call back',
extra: <String, dynamic>{'userId': '1a2b3c4d'},
headers: <String, dynamic>{'apiKey': 'Abc@123!', 'platform': 'flutter'},
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: 'CallKitLogo',
handleType: '',
supportsVideo: true,
maximumCallGroups: 2,
maximumCallsPerCallGroup: 1,
audioSessionMode: 'default',
audioSessionActive: true,
audioSessionPreferredSampleRate: 44100.0,
audioSessionPreferredIOBufferDuration: 0.005,
supportsDTMF: true,
supportsHolding: true,
supportsGrouping: false,
supportsUngrouping: false,
ringtonePath: 'system_ringtone_default',
),
);
logger.d(callerData.targetUserId);
await FlutterCallkitIncoming.showCallkitIncoming(params);
ALM.Response autoLoginData = ALM.Response.fromJson(jsonDecode(data.data["user_token_response"]));
var values = {
"loginDetails": autoLoginData.toJson(),
"callerDetails": callerData.toJson(),
};
logger.d(values);
CallKitParams params = CallKitParams(
id: uuid,
nameCaller: callerData.targetUserName,
appName: 'Mohemm',
handle: '',
type: 0,
duration: 25000,
textAccept: 'Accept',
textDecline: 'Decline',
textMissedCall: 'Missed call',
textCallback: 'Call back',
extra: values,
android: const AndroidParams(
isCustomNotification: true,
isShowLogo: true,
isShowCallback: false,
isShowMissedCallNotification: true,
ringtonePath: 'system_ringtone_default',
backgroundColor: '#0955fa',
backgroundUrl: 'assets/test.png',
actionColor: '#4CAF50',
),
ios: IOSParams(
iconName: 'Mohemm',
handleType: '',
supportsVideo: true,
maximumCallGroups: 2,
maximumCallsPerCallGroup: 1,
audioSessionMode: 'default',
audioSessionActive: true,
audioSessionPreferredSampleRate: 38000.0,
audioSessionPreferredIOBufferDuration: 0.005,
supportsDTMF: true,
supportsHolding: true,
supportsGrouping: false,
supportsUngrouping: false,
ringtonePath: 'system_ringtone_default',
),
);
if (callerData.chatEventId == 3) {
await Utils.saveStringFromPrefs("isIncomingCall", "true");
await Utils.saveStringFromPrefs("inComingCallData",jsonEncode(params.toJson()));
await FlutterCallkitIncoming.showCallkitIncoming(params);
}
}
//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:
break;
case Event.ACTION_CALL_DECLINE:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
break;
case Event.ACTION_CALL_ENDED:
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.saveStringFromPrefs("inComingCallData", "null");
// TODO: ended an incoming/outgoing call
break;
case Event.ACTION_CALL_TIMEOUT:
Utils.saveStringFromPrefs("isIncomingCall", "false");
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 {}
}
}

@ -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/start_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';
@ -182,6 +183,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";
@ -294,6 +296,8 @@ class AppRoutes {
chat: (BuildContext context) => ChatHome(),
chatDetailed: (BuildContext context) => ChatDetailScreen(),
chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(),
chatStartCall: (BuildContext context) => StartCallPage(),
// Marathon
marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(),

@ -0,0 +1,425 @@
// To parse this JSON data, do
//
// final incomingCallDataPayload = incomingCallDataPayloadFromJson(jsonString);
import 'dart:convert';
class IncomingCallDataPayload {
IncomingCallDataPayload({
this.id,
this.nameCaller,
this.appName,
this.avatar,
this.handle,
this.type,
this.duration,
this.textAccept,
this.textDecline,
this.textMissedCall,
this.textCallback,
this.extra,
this.headers,
this.android,
this.ios,
});
String? id;
String? nameCaller;
String? appName;
dynamic avatar;
String? handle;
dynamic? type;
dynamic? duration;
String? textAccept;
String? textDecline;
String? textMissedCall;
String? textCallback;
Extra? extra;
dynamic headers;
Android? android;
Ios? ios;
factory IncomingCallDataPayload.fromRawJson(String str) => IncomingCallDataPayload.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory IncomingCallDataPayload.fromJson(Map<String, dynamic> json) => IncomingCallDataPayload(
id: json["id"],
nameCaller: json["nameCaller"],
appName: json["appName"],
avatar: json["avatar"],
handle: json["handle"],
type: json["type"],
duration: json["duration"],
textAccept: json["textAccept"],
textDecline: json["textDecline"],
textMissedCall: json["textMissedCall"],
textCallback: json["textCallback"],
extra: json["extra"] == null ? null : Extra.fromJson(json["extra"]),
headers: json["headers"],
android: json["android"] == null ? null : Android.fromJson(json["android"]),
ios: json["ios"] == null ? null : Ios.fromJson(json["ios"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"nameCaller": nameCaller,
"appName": appName,
"avatar": avatar,
"handle": handle,
"type": type,
"duration": duration,
"textAccept": textAccept,
"textDecline": textDecline,
"textMissedCall": textMissedCall,
"textCallback": textCallback,
"extra": extra?.toJson(),
"headers": headers,
"android": android?.toJson(),
"ios": ios?.toJson(),
};
}
class Android {
Android({
this.isCustomNotification,
this.isShowLogo,
this.isShowCallback,
this.isShowMissedCallNotification,
this.ringtonePath,
this.backgroundColor,
this.backgroundUrl,
this.actionColor,
this.incomingCallNotificationChannelName,
this.missedCallNotificationChannelName,
});
bool? isCustomNotification;
bool? isShowLogo;
bool? isShowCallback;
bool? isShowMissedCallNotification;
String? ringtonePath;
String? backgroundColor;
String? backgroundUrl;
String? actionColor;
dynamic incomingCallNotificationChannelName;
dynamic missedCallNotificationChannelName;
factory Android.fromRawJson(String str) => Android.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Android.fromJson(Map<String, dynamic> json) => Android(
isCustomNotification: json["isCustomNotification"],
isShowLogo: json["isShowLogo"],
isShowCallback: json["isShowCallback"],
isShowMissedCallNotification: json["isShowMissedCallNotification"],
ringtonePath: json["ringtonePath"],
backgroundColor: json["backgroundColor"],
backgroundUrl: json["backgroundUrl"],
actionColor: json["actionColor"],
incomingCallNotificationChannelName: json["incomingCallNotificationChannelName"],
missedCallNotificationChannelName: json["missedCallNotificationChannelName"],
);
Map<String, dynamic> toJson() => {
"isCustomNotification": isCustomNotification,
"isShowLogo": isShowLogo,
"isShowCallback": isShowCallback,
"isShowMissedCallNotification": isShowMissedCallNotification,
"ringtonePath": ringtonePath,
"backgroundColor": backgroundColor,
"backgroundUrl": backgroundUrl,
"actionColor": actionColor,
"incomingCallNotificationChannelName": incomingCallNotificationChannelName,
"missedCallNotificationChannelName": missedCallNotificationChannelName,
};
}
class Extra {
Extra({
this.loginDetails,
this.callerDetails,
});
LoginDetails? loginDetails;
CallerDetails? callerDetails;
factory Extra.fromRawJson(String str) => Extra.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Extra.fromJson(Map<String, dynamic> json) => Extra(
loginDetails: json["loginDetails"] == null ? null : LoginDetails.fromJson(json["loginDetails"]),
callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]),
);
Map<String, dynamic> toJson() => {
"loginDetails": loginDetails?.toJson(),
"callerDetails": callerDetails?.toJson(),
};
}
class CallerDetails {
CallerDetails({
this.userChatHistoryId,
this.userChatHistoryLineId,
this.contant,
this.contantNo,
this.currentUserId,
this.currentUserName,
this.targetUserId,
this.targetUserName,
this.encryptedTargetUserId,
this.encryptedTargetUserName,
this.currentUserEmail,
this.targetUserEmail,
this.chatEventId,
this.fileTypeId,
this.isSeen,
this.isDelivered,
this.createdDate,
this.chatSource,
this.conversationId,
this.fileTypeResponse,
this.userChatReplyResponse,
});
int? userChatHistoryId;
int? userChatHistoryLineId;
String? contant;
String? contantNo;
int? currentUserId;
String? currentUserName;
int? targetUserId;
String? targetUserName;
String? encryptedTargetUserId;
String? encryptedTargetUserName;
String? currentUserEmail;
String? targetUserEmail;
int? chatEventId;
dynamic fileTypeId;
bool? isSeen;
bool? isDelivered;
DateTime? createdDate;
int? chatSource;
String? conversationId;
FileTypeResponse? fileTypeResponse;
dynamic userChatReplyResponse;
factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory CallerDetails.fromJson(Map<String, dynamic> json) => CallerDetails(
userChatHistoryId: json["userChatHistoryId"],
userChatHistoryLineId: json["userChatHistoryLineId"],
contant: json["contant"],
contantNo: json["contantNo"],
currentUserId: json["currentUserId"],
currentUserName: json["currentUserName"],
targetUserId: json["targetUserId"],
targetUserName: json["targetUserName"],
encryptedTargetUserId: json["encryptedTargetUserId"],
encryptedTargetUserName: json["encryptedTargetUserName"],
currentUserEmail: json["currentUserEmail"],
targetUserEmail: json["targetUserEmail"],
chatEventId: json["chatEventId"],
fileTypeId: json["fileTypeId"],
isSeen: json["isSeen"],
isDelivered: json["isDelivered"],
createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]),
chatSource: json["chatSource"],
conversationId: json["conversationId"],
fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]),
userChatReplyResponse: json["userChatReplyResponse"],
);
Map<String, dynamic> toJson() => {
"userChatHistoryId": userChatHistoryId,
"userChatHistoryLineId": userChatHistoryLineId,
"contant": contant,
"contantNo": contantNo,
"currentUserId": currentUserId,
"currentUserName": currentUserName,
"targetUserId": targetUserId,
"targetUserName": targetUserName,
"encryptedTargetUserId": encryptedTargetUserId,
"encryptedTargetUserName": encryptedTargetUserName,
"currentUserEmail": currentUserEmail,
"targetUserEmail": targetUserEmail,
"chatEventId": chatEventId,
"fileTypeId": fileTypeId,
"isSeen": isSeen,
"isDelivered": isDelivered,
"createdDate": createdDate?.toIso8601String(),
"chatSource": chatSource,
"conversationId": conversationId,
"fileTypeResponse": fileTypeResponse?.toJson(),
"userChatReplyResponse": userChatReplyResponse,
};
}
class FileTypeResponse {
FileTypeResponse({
this.fileTypeId,
this.fileTypeName,
this.fileTypeDescription,
this.fileKind,
this.fileName,
});
int? fileTypeId;
dynamic fileTypeName;
dynamic fileTypeDescription;
dynamic fileKind;
dynamic fileName;
factory FileTypeResponse.fromRawJson(String str) => FileTypeResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory FileTypeResponse.fromJson(Map<String, dynamic> json) => FileTypeResponse(
fileTypeId: json["fileTypeId"],
fileTypeName: json["fileTypeName"],
fileTypeDescription: json["fileTypeDescription"],
fileKind: json["fileKind"],
fileName: json["fileName"],
);
Map<String, dynamic> toJson() => {
"fileTypeId": fileTypeId,
"fileTypeName": fileTypeName,
"fileTypeDescription": fileTypeDescription,
"fileKind": fileKind,
"fileName": fileName,
};
}
class LoginDetails {
LoginDetails({
this.id,
this.userName,
this.email,
this.phone,
this.title,
this.token,
this.isDomainUser,
this.isActiveCode,
this.encryptedUserId,
this.encryptedUserName,
});
int? id;
String? userName;
String? email;
dynamic phone;
String? title;
String? token;
bool? isDomainUser;
bool? isActiveCode;
String? encryptedUserId;
String? encryptedUserName;
factory LoginDetails.fromRawJson(String str) => LoginDetails.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory LoginDetails.fromJson(Map<String, dynamic> json) => LoginDetails(
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"],
);
Map<String, dynamic> toJson() => {
"id": id,
"userName": userName,
"email": email,
"phone": phone,
"title": title,
"token": token,
"isDomainUser": isDomainUser,
"isActiveCode": isActiveCode,
"encryptedUserId": encryptedUserId,
"encryptedUserName": encryptedUserName,
};
}
class Ios {
Ios({
this.iconName,
this.handleType,
this.supportsVideo,
this.maximumCallGroups,
this.maximumCallsPerCallGroup,
this.audioSessionMode,
this.audioSessionActive,
this.audioSessionPreferredSampleRate,
this.audioSessionPreferredIoBufferDuration,
this.supportsDtmf,
this.supportsHolding,
this.supportsGrouping,
this.supportsUngrouping,
this.ringtonePath,
});
String? iconName;
String? handleType;
bool? supportsVideo;
int? maximumCallGroups;
int? maximumCallsPerCallGroup;
String? audioSessionMode;
bool? audioSessionActive;
double? audioSessionPreferredSampleRate;
double? audioSessionPreferredIoBufferDuration;
bool? supportsDtmf;
bool? supportsHolding;
bool? supportsGrouping;
bool? supportsUngrouping;
String? ringtonePath;
factory Ios.fromRawJson(String str) => Ios.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Ios.fromJson(Map<String, dynamic> json) => Ios(
iconName: json["iconName"],
handleType: json["handleType"],
supportsVideo: json["supportsVideo"],
maximumCallGroups: json["maximumCallGroups"],
maximumCallsPerCallGroup: json["maximumCallsPerCallGroup"],
audioSessionMode: json["audioSessionMode"],
audioSessionActive: json["audioSessionActive"],
audioSessionPreferredSampleRate: json["audioSessionPreferredSampleRate"],
audioSessionPreferredIoBufferDuration: json["audioSessionPreferredIOBufferDuration"]?.toDouble(),
supportsDtmf: json["supportsDTMF"],
supportsHolding: json["supportsHolding"],
supportsGrouping: json["supportsGrouping"],
supportsUngrouping: json["supportsUngrouping"],
ringtonePath: json["ringtonePath"],
);
Map<String, dynamic> toJson() => {
"iconName": iconName,
"handleType": handleType,
"supportsVideo": supportsVideo,
"maximumCallGroups": maximumCallGroups,
"maximumCallsPerCallGroup": maximumCallsPerCallGroup,
"audioSessionMode": audioSessionMode,
"audioSessionActive": audioSessionActive,
"audioSessionPreferredSampleRate": audioSessionPreferredSampleRate,
"audioSessionPreferredIOBufferDuration": audioSessionPreferredIoBufferDuration,
"supportsDTMF": supportsDtmf,
"supportsHolding": supportsHolding,
"supportsGrouping": supportsGrouping,
"supportsUngrouping": supportsUngrouping,
"ringtonePath": ringtonePath,
};
}

@ -4,13 +4,16 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/entities/android_params.dart';
import 'package:flutter_callkit_incoming/entities/call_event.dart';
import 'package:flutter_callkit_incoming/entities/call_kit_params.dart';
import 'package:flutter_callkit_incoming/entities/ios_params.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/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/models/chat/incomingCall.dart';
import 'package:mohem_flutter_app/models/chat/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';
@ -28,6 +31,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
final AudioPlayer player = AudioPlayer();
late MediaStream localStream;
late CallDataModel outGoingCallData;
late IncomingCallDataPayload incomingCallData;
bool isMicOff = false;
bool isLoudSpeaker = false;
bool isCamOff = false;
@ -40,6 +44,8 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
/// WebRTC Connection Variables
bool _offer = false;
bool isIncomingCallLoader = true;
late BuildContext providerContext;
void initCallListeners({required BuildContext context}) {
@ -258,7 +264,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
Future<void> invoke({required String invokeMethod, required int currentUserID, required int targetUserID, var data, int userStatus = 1}) async {
List<Object> args = [];
// logger.w(currentUserID.toString() + " -- " + targetUserID.toString() + " -- " + isVideoCall.toString());
Utils.showToast(currentUserID.toString() + " -- " + targetUserID.toString() + " -- " + isVideoCall.toString());
if (invokeMethod == "CallUserAsync") {
args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "answerCallAsync") {
@ -425,57 +431,9 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
CallKitParams callKitParams = CallKitParams(
id: "_currentUuid",
nameCaller: 'Hien Nguyen',
appName: 'Callkit',
avatar: 'https://i.pravatar.cc/100',
handle: '0123456789',
type: 0,
textAccept: 'Accept',
textDecline: 'Decline',
textMissedCall: 'Missed call',
textCallback: 'Call back',
duration: 30000,
extra: <String, dynamic>{'userId': '1a2b3c4d'},
headers: <String, dynamic>{'apiKey': 'Abc@123!', 'platform': 'flutter'},
android: const AndroidParams(
isCustomNotification: true,
isShowLogo: false,
isShowCallback: false,
isShowMissedCallNotification: true,
ringtonePath: 'system_ringtone_default',
backgroundColor: '#0955fa',
backgroundUrl: 'https://i.pravatar.cc/500',
actionColor: '#4CAF50',
incomingCallNotificationChannelName: "Incoming Call",
missedCallNotificationChannelName: "Missed Call"),
ios: IOSParams(
iconName: 'CallKitLogo',
handleType: 'generic',
supportsVideo: true,
maximumCallGroups: 2,
maximumCallsPerCallGroup: 1,
audioSessionMode: 'default',
audioSessionActive: true,
audioSessionPreferredSampleRate: 44100.0,
audioSessionPreferredIOBufferDuration: 0.005,
supportsDTMF: true,
supportsHolding: true,
supportsGrouping: false,
supportsUngrouping: false,
ringtonePath: 'system_ringtone_default',
),
);
Future<void> startIncomingCallViaKit() async {
await startIncomingCall();
Utils.showToast("Inside Incoming Call");
await invoke(invokeMethod: "answerCallAsync", currentUserID: incomingCallData.extra!.loginDetails!.id!, targetUserID: incomingCallData.extra!.callerDetails!.targetUserId!);
}
}

@ -84,6 +84,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
bool disbaleChatForThisUser = false;
bool isCall = false;
userLoginToken.UserAutoLoginModel userLoginData = userLoginToken.UserAutoLoginModel();
Future<void> getUserAutoLoginToken() async {
userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
if (userLoginResponse.response != null) {
@ -99,7 +103,11 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
Future<void> buildHubConnection({required BuildContext context, required ChatCallProvider ccProvider}) async {
chatHubConnection = await getHubConnection();
try {
chatHubConnection = await getHubConnection();
}catch(e){
Utils.showToast(e.toString());
}
await chatHubConnection.start();
if (kDebugMode) {
logger.i("Hub Conn: Startedddddddd");

@ -1,20 +1,25 @@
import 'dart:async';
import 'dart:core';
import 'dart:ui';
import 'package:draggable_widget/draggable_widget.dart';
import 'package:flutter/material.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/models/chat/get_user_login_token_model.dart';
import 'package:mohem_flutter_app/models/chat/incomingCall.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:provider/provider.dart';
class StartCallPage extends StatefulWidget {
RTCVideoRenderer localRenderer;
RTCVideoRenderer remoteRenderer;
RTCVideoRenderer? localRenderer;
RTCVideoRenderer? remoteRenderer;
StartCallPage({required this.localRenderer, required this.remoteRenderer});
StartCallPage({this.localRenderer, this.remoteRenderer});
@override
_StartCallPageState createState() => _StartCallPageState();
@ -22,245 +27,306 @@ class StartCallPage extends StatefulWidget {
class _StartCallPageState extends State<StartCallPage> {
final dragController = DragController();
late ChatCallProvider callProvider;
late ChatProviderModel cPro;
late ChatCallProvider callPro;
IncomingCallDataPayload incomingCallData = IncomingCallDataPayload();
bool isIncomingCall = false;
//userChatDetails
@override
void initState() {
callProvider = Provider.of<ChatCallProvider>(context, listen: false);
callPro = Provider.of<ChatCallProvider>(context, listen: false);
cPro = Provider.of<ChatProviderModel>(context, listen: false);
super.initState();
refresh();
}
void refresh() {
Future.delayed(const Duration(seconds: 1), () {
callProvider.notifyListeners();
@override
void dispose() {
if (chatHubConnection != null) {
chatHubConnection.stop();
}
super.dispose();
}
void connection() async {
isIncomingCall = (Utils.getStringFromPrefs("isIncomingCall") == "true" ? true : false);
Utils.saveStringFromPrefs("isIncomingCall", "false");
Utils.showToast(incomingCallData.extra!.loginDetails!.toRawJson(), longDuration: false);
cPro.userLoginData = UserAutoLoginModel(
response: Response(
id: incomingCallData.extra!.loginDetails!.id,
userName: incomingCallData.extra!.loginDetails!.userName,
email: incomingCallData.extra!.loginDetails!.email,
phone: incomingCallData.extra!.loginDetails!.phone,
title: incomingCallData.extra!.loginDetails!.title,
token: incomingCallData.extra!.loginDetails!.token,
isActiveCode: incomingCallData.extra!.loginDetails!.isActiveCode,
isDomainUser: incomingCallData.extra!.loginDetails!.isDomainUser,
encryptedUserId: incomingCallData.extra!.loginDetails!.encryptedUserName,
encryptedUserName: incomingCallData.extra!.loginDetails!.encryptedUserId),
errorResponses: null,
);
AppState().setchatUserDetails = cPro.userLoginData;
await cPro.buildHubConnection(context: context, ccProvider: callPro);
callPro.incomingCallData = incomingCallData;
callPro.isIncomingCallLoader = false;
Future.delayed(const Duration(seconds: 2)).then((value) {
callPro.startIncomingCallViaKit();
callPro.notifyListeners();
});
}
@override
Widget build(BuildContext context) {
return Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider provider, Widget? child) {
incomingCallData = ModalRoute.of(context)!.settings.arguments as IncomingCallDataPayload;
if (incomingCallData != null) {
connection();
}
return Consumer2<ChatCallProvider, ChatProviderModel>(builder: (BuildContext context, ChatCallProvider provider, ChatProviderModel cpm, Widget? child) {
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
alignment: FractionalOffset.center,
children: [
if (provider.isVideoCall)
Positioned.fill(
child: RTCVideoView(
widget.remoteRenderer,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
key: const Key('remote'),
),
),
if (provider.isVideoCall)
DraggableWidget(
bottomMargin: 20,
topMargin: 40,
intialVisibility: true,
horizontalSpace: 20,
shadowBorderRadius: 50,
initialPosition: AnchoringPosition.topLeft,
dragController: dragController,
normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
child: SizedBox(
height: 200,
width: 140,
child: RTCVideoView(
widget.localRenderer,
mirror: true,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
),
),
),
if (!provider.isVideoCall)
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
child: provider.isVideoCall
? Stack(
alignment: FractionalOffset.center,
children: <Widget>[
if (provider.isVideoCall)
Positioned.fill(
child: RTCVideoView(
widget.remoteRenderer!,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
key: const Key('remote'),
),
),
if (provider.isVideoCall)
DraggableWidget(
bottomMargin: 20,
topMargin: 40,
intialVisibility: true,
horizontalSpace: 20,
shadowBorderRadius: 50,
initialPosition: AnchoringPosition.topLeft,
dragController: dragController,
normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
child: SizedBox(
height: 200,
width: 140,
child: RTCVideoView(
widget.localRenderer!,
mirror: true,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
callProvider.outGoingCallData.receiverName!,
style: const TextStyle(
fontSize: 21,
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
if (!provider.isVideoCall)
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
),
const Text(
"On Call",
style: TextStyle(
fontSize: 16,
decoration: TextDecoration.none,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
callPro.outGoingCallData.receiverName!,
style: const TextStyle(
fontSize: 21,
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
),
const Text(
"On Call",
style: TextStyle(
fontSize: 16,
decoration: TextDecoration.none,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
const SizedBox(
height: 2,
),
],
),
),
const SizedBox(
height: 2,
),
],
),
),
],
),
),
],
],
),
),
],
),
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.only(
bottom: 20,
left: 40,
right: 40,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// if (provider.isVideoCall)
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
callProvider.loudOn();
},
elevation: 2.0,
fillColor: callProvider.isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.camOff();
},
elevation: 2.0,
fillColor: provider.isCamOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isCamOff ? Icons.videocam_off : Icons.videocam,
color: MyColors.white,
size: 30.0,
),
),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.switchCamera();
},
elevation: 2.0,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor,
padding: const EdgeInsets.all(
10.0,
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.only(
bottom: 20,
left: 40,
right: 40,
),
shape: const CircleBorder(),
child: Icon(
provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera,
color: MyColors.white,
size: 30.0,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// if (provider.isVideoCall)
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
callPro.loudOn();
},
elevation: 2.0,
fillColor: callPro.isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.camOff();
},
elevation: 2.0,
fillColor: provider.isCamOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isCamOff ? Icons.videocam_off : Icons.videocam,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.switchCamera();
},
elevation: 2.0,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.micOff();
},
elevation: 2.0,
fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: const BoxConstraints(),
onPressed: () {
provider.endCall().then((value) {
if (value) {
Navigator.of(context).pop();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 30.0,
),
),
],
),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.micOff();
},
elevation: 2.0,
fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 30.0,
),
),
],
)
: provider.isIncomingCallLoader
? SizedBox(
width: double.infinity,
height: 500,
child: Center(
child: CircularProgressIndicator(),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.endCall().then((value) {
if (value) {
Navigator.of(context).pop();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
10.0,
)
: Column(
children: const [
Center(
child: Text(
"Testing",
),
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 30.0,
Center(
child: Text(
"Data",
),
),
),
],
),
),
),
],
),
],
),
);
});
}

@ -21,6 +21,7 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/chat/incomingCall.dart';
import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart';
import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart';
import 'package:mohem_flutter_app/models/member_information_list_model.dart';
@ -32,7 +33,7 @@ import 'package:mohem_flutter_app/widgets/input_widget.dart';
// import 'package:safe_device/safe_device.dart';
import 'package:wifi_iot/wifi_iot.dart';
class LoginScreen extends StatefulWidget {
class LoginScreen extends StatefulWidget with WidgetsBindingObserver {
LoginScreen({Key? key}) : super(key: key);
@override
@ -60,7 +61,6 @@ class _LoginScreenState extends State<LoginScreen> {
bool isDevelopmentModeEnable = false;
late HmsApiAvailability hmsApiAvailability;
@override
void initState() {
super.initState();
@ -96,39 +96,44 @@ class _LoginScreenState extends State<LoginScreen> {
GetMobileLoginInfoListModel? loginInfo;
Future<void> checkFirebaseToken() async {
try {
Utils.showLoading(context);
if (Platform.isAndroid) {
try {
await hmsApiAvailability.isHMSAvailable().then((value) async {
if (value == 0) {
AppState().setIsHuawei = true;
AppNotifications().initHuaweiPush(checkLoginInfo);
} else {
await Firebase.initializeApp();
_firebaseMessaging = FirebaseMessaging.instance;
firebaseToken = await _firebaseMessaging.getToken();
AppNotifications().init(firebaseToken);
checkLoginInfo();
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}
});
} catch (ex) {}
} else {
await Firebase.initializeApp();
_firebaseMessaging = FirebaseMessaging.instance;
firebaseToken = await _firebaseMessaging.getToken();
AppNotifications().init(firebaseToken);
checkLoginInfo();
if (await Utils.getStringFromPrefs("isIncomingCall") == "true") {
Utils.hideLoading(context);
Navigator.pushNamed(context, AppRoutes.chatStartCall, arguments: IncomingCallDataPayload.fromRawJson(await Utils.getStringFromPrefs("inComingCallData")));
} else {
try {
Utils.showLoading(context);
if (Platform.isAndroid) {
try {
await hmsApiAvailability.isHMSAvailable().then((value) async {
if (value == 0) {
AppState().setIsHuawei = true;
AppNotifications().initHuaweiPush(checkLoginInfo);
} else {
await Firebase.initializeApp();
_firebaseMessaging = FirebaseMessaging.instance;
firebaseToken = await _firebaseMessaging.getToken();
AppNotifications().init(firebaseToken);
checkLoginInfo();
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}
});
} catch (ex) {}
} else {
await Firebase.initializeApp();
_firebaseMessaging = FirebaseMessaging.instance;
firebaseToken = await _firebaseMessaging.getToken();
AppNotifications().init(firebaseToken);
checkLoginInfo();
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}
}

Loading…
Cancel
Save