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(); await Firebase.initializeApp();
Utils.saveStringFromPrefs("isAppOpendByChat", "false"); Utils.saveStringFromPrefs("isAppOpendByChat", "false");
Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString()); Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString());
ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message); ChatVoipCall().showCallkitIncoming(uuid: const Uuid().v4(), data: message);
} }

@ -1,10 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'package:firebase_messaging/firebase_messaging.dart'; 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/entities/entities.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.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/main.dart';
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.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 { class ChatVoipCall {
static final ChatVoipCall _instance = ChatVoipCall._internal(); static final ChatVoipCall _instance = ChatVoipCall._internal();
@ -14,51 +18,112 @@ class ChatVoipCall {
factory ChatVoipCall() => _instance; factory ChatVoipCall() => _instance;
Future<void> showCallkitIncoming({required String uuid, required RemoteMessage data}) async { 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"])); SingleUserChatModel callerData = SingleUserChatModel.fromJson(jsonDecode(data.data["user_chat_history_response"]));
var params = CallKitParams( ALM.Response autoLoginData = ALM.Response.fromJson(jsonDecode(data.data["user_token_response"]));
id: uuid, var values = {
nameCaller: callerData.currentUserName, "loginDetails": autoLoginData.toJson(),
appName: 'Mohemm', "callerDetails": callerData.toJson(),
avatar: 'https://i.pravatar.cc/100', };
handle: '0123456789', logger.d(values);
type: 0, CallKitParams params = CallKitParams(
duration: 30000, id: uuid,
textAccept: 'Accept', nameCaller: callerData.targetUserName,
textDecline: 'Decline', appName: 'Mohemm',
textMissedCall: 'Missed call', handle: '',
textCallback: 'Call back', type: 0,
extra: <String, dynamic>{'userId': '1a2b3c4d'}, duration: 25000,
headers: <String, dynamic>{'apiKey': 'Abc@123!', 'platform': 'flutter'}, textAccept: 'Accept',
android: const AndroidParams( textDecline: 'Decline',
isCustomNotification: true, textMissedCall: 'Missed call',
isShowLogo: false, textCallback: 'Call back',
isShowCallback: false, extra: values,
isShowMissedCallNotification: true, android: const AndroidParams(
ringtonePath: 'system_ringtone_default', isCustomNotification: true,
backgroundColor: '#0955fa', isShowLogo: true,
backgroundUrl: 'assets/test.png', isShowCallback: false,
actionColor: '#4CAF50', isShowMissedCallNotification: true,
), ringtonePath: 'system_ringtone_default',
ios: IOSParams( backgroundColor: '#0955fa',
iconName: 'CallKitLogo', backgroundUrl: 'assets/test.png',
handleType: '', actionColor: '#4CAF50',
supportsVideo: true, ),
maximumCallGroups: 2, ios: IOSParams(
maximumCallsPerCallGroup: 1, iconName: 'Mohemm',
audioSessionMode: 'default', handleType: '',
audioSessionActive: true, supportsVideo: true,
audioSessionPreferredSampleRate: 44100.0, maximumCallGroups: 2,
audioSessionPreferredIOBufferDuration: 0.005, maximumCallsPerCallGroup: 1,
supportsDTMF: true, audioSessionMode: 'default',
supportsHolding: true, audioSessionActive: true,
supportsGrouping: false, audioSessionPreferredSampleRate: 38000.0,
supportsUngrouping: false, audioSessionPreferredIOBufferDuration: 0.005,
ringtonePath: 'system_ringtone_default', supportsDTMF: true,
), supportsHolding: true,
); supportsGrouping: false,
logger.d(callerData.targetUserId); supportsUngrouping: false,
await FlutterCallkitIncoming.showCallkitIncoming(params); 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/monthly_attendance_screen.dart';
import 'package:mohem_flutter_app/ui/attendance/vacation_rule_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/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_detailed_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_home.dart'; import 'package:mohem_flutter_app/ui/chat/chat_home.dart';
import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.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 chat = "/chat";
static const String chatDetailed = "/chatDetailed"; static const String chatDetailed = "/chatDetailed";
static const String chatFavoriteUsers = "/chatFavoriteUsers"; static const String chatFavoriteUsers = "/chatFavoriteUsers";
static const String chatStartCall = "/chatStartCall";
//Marathon //Marathon
static const String marathonIntroScreen = "/marathonIntroScreen"; static const String marathonIntroScreen = "/marathonIntroScreen";
@ -294,6 +296,8 @@ class AppRoutes {
chat: (BuildContext context) => ChatHome(), chat: (BuildContext context) => ChatHome(),
chatDetailed: (BuildContext context) => ChatDetailScreen(), chatDetailed: (BuildContext context) => ChatDetailScreen(),
chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(), chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(),
chatStartCall: (BuildContext context) => StartCallPage(),
// Marathon // Marathon
marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(), 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/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/entities/android_params.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/call_kit_params.dart';
import 'package:flutter_callkit_incoming/entities/ios_params.dart'; import 'package:flutter_callkit_incoming/entities/ios_params.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import 'package:mohem_flutter_app/app_state/app_state.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/main.dart';
import 'package:mohem_flutter_app/models/chat/call.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/models/chat/webrtc_payloads.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart'; import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
@ -28,6 +31,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
final AudioPlayer player = AudioPlayer(); final AudioPlayer player = AudioPlayer();
late MediaStream localStream; late MediaStream localStream;
late CallDataModel outGoingCallData; late CallDataModel outGoingCallData;
late IncomingCallDataPayload incomingCallData;
bool isMicOff = false; bool isMicOff = false;
bool isLoudSpeaker = false; bool isLoudSpeaker = false;
bool isCamOff = false; bool isCamOff = false;
@ -40,6 +44,8 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
/// WebRTC Connection Variables /// WebRTC Connection Variables
bool _offer = false; bool _offer = false;
bool isIncomingCallLoader = true;
late BuildContext providerContext; late BuildContext providerContext;
void initCallListeners({required BuildContext context}) { 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 { Future<void> invoke({required String invokeMethod, required int currentUserID, required int targetUserID, var data, int userStatus = 1}) async {
List<Object> args = []; List<Object> args = [];
// logger.w(currentUserID.toString() + " -- " + targetUserID.toString() + " -- " + isVideoCall.toString()); Utils.showToast(currentUserID.toString() + " -- " + targetUserID.toString() + " -- " + isVideoCall.toString());
if (invokeMethod == "CallUserAsync") { if (invokeMethod == "CallUserAsync") {
args = [currentUserID, targetUserID, isVideoCall]; args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "answerCallAsync") { } else if (invokeMethod == "answerCallAsync") {
@ -425,57 +431,9 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners(); notifyListeners();
} }
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!);
}
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',
),
);
} }

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

@ -1,20 +1,25 @@
import 'dart:async';
import 'dart:core'; import 'dart:core';
import 'dart:ui'; import 'dart:ui';
import 'package:draggable_widget/draggable_widget.dart'; import 'package:draggable_widget/draggable_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_webrtc/flutter_webrtc.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/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/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_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'; import 'package:provider/provider.dart';
class StartCallPage extends StatefulWidget { class StartCallPage extends StatefulWidget {
RTCVideoRenderer localRenderer; RTCVideoRenderer? localRenderer;
RTCVideoRenderer remoteRenderer; RTCVideoRenderer? remoteRenderer;
StartCallPage({required this.localRenderer, required this.remoteRenderer}); StartCallPage({this.localRenderer, this.remoteRenderer});
@override @override
_StartCallPageState createState() => _StartCallPageState(); _StartCallPageState createState() => _StartCallPageState();
@ -22,245 +27,306 @@ class StartCallPage extends StatefulWidget {
class _StartCallPageState extends State<StartCallPage> { class _StartCallPageState extends State<StartCallPage> {
final dragController = DragController(); final dragController = DragController();
late ChatCallProvider callProvider; late ChatProviderModel cPro;
late ChatCallProvider callPro;
IncomingCallDataPayload incomingCallData = IncomingCallDataPayload();
bool isIncomingCall = false;
//userChatDetails
@override @override
void initState() { 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(); super.initState();
refresh();
} }
void refresh() { @override
Future.delayed(const Duration(seconds: 1), () { void dispose() {
callProvider.notifyListeners(); 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 @override
Widget build(BuildContext context) { 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( return SizedBox(
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: Stack( child: provider.isVideoCall
alignment: FractionalOffset.center, ? Stack(
children: [ alignment: FractionalOffset.center,
if (provider.isVideoCall) children: <Widget>[
Positioned.fill( if (provider.isVideoCall)
child: RTCVideoView( Positioned.fill(
widget.remoteRenderer, child: RTCVideoView(
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, widget.remoteRenderer!,
key: const Key('remote'), objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
), key: const Key('remote'),
), ),
if (provider.isVideoCall) ),
DraggableWidget( if (provider.isVideoCall)
bottomMargin: 20, DraggableWidget(
topMargin: 40, bottomMargin: 20,
intialVisibility: true, topMargin: 40,
horizontalSpace: 20, intialVisibility: true,
shadowBorderRadius: 50, horizontalSpace: 20,
initialPosition: AnchoringPosition.topLeft, shadowBorderRadius: 50,
dragController: dragController, initialPosition: AnchoringPosition.topLeft,
normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), dragController: dragController,
draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
child: SizedBox( draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
height: 200, child: SizedBox(
width: 140, height: 200,
child: RTCVideoView( width: 140,
widget.localRenderer, child: RTCVideoView(
mirror: true, widget.localRenderer!,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, 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: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, if (!provider.isVideoCall)
mainAxisSize: MainAxisSize.max, Positioned.fill(
children: <Widget>[ child: ClipRect(
40.height, child: BackdropFilter(
Row( filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
crossAxisAlignment: CrossAxisAlignment.center, child: Container(
mainAxisAlignment: MainAxisAlignment.center, decoration: BoxDecoration(
children: <Widget>[ color: MyColors.grey57Color.withOpacity(
Container( 0.3,
margin: const EdgeInsets.all(21.0), ),
child: Container( ),
margin: const EdgeInsets.only( child: Column(
left: 10.0, crossAxisAlignment: CrossAxisAlignment.start,
right: 10.0, mainAxisSize: MainAxisSize.max,
), children: <Widget>[
child: Column( 40.height,
crossAxisAlignment: CrossAxisAlignment.center, Row(
mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
SvgPicture.asset( Container(
"assets/images/user.svg", margin: const EdgeInsets.all(21.0),
height: 70, child: Container(
width: 70, margin: const EdgeInsets.only(
fit: BoxFit.cover, left: 10.0,
), right: 10.0,
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,
), ),
), child: Column(
const Text( crossAxisAlignment: CrossAxisAlignment.center,
"On Call", mainAxisSize: MainAxisSize.min,
style: TextStyle( mainAxisAlignment: MainAxisAlignment.spaceAround,
fontSize: 16, children: <Widget>[
decoration: TextDecoration.none, SvgPicture.asset(
fontWeight: FontWeight.w600, "assets/images/user.svg",
color: Color( height: 70,
0xffC6C6C6, width: 70,
), fit: BoxFit.cover,
letterSpacing: -0.48, ),
height: 23 / 24, 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( Align(
constraints: BoxConstraints(), alignment: Alignment.bottomCenter,
onPressed: () { child: Container(
provider.switchCamera(); padding: const EdgeInsets.only(
}, bottom: 20,
elevation: 2.0, left: 40,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor, right: 40,
padding: const EdgeInsets.all(
10.0,
), ),
shape: const CircleBorder(), child: Row(
child: Icon( mainAxisSize: MainAxisSize.max,
provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera, mainAxisAlignment: MainAxisAlignment.spaceBetween,
color: MyColors.white, children: <Widget>[
size: 30.0, // 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(); : provider.isIncomingCallLoader
}, ? SizedBox(
elevation: 2.0, width: double.infinity,
fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey, height: 500,
padding: const EdgeInsets.all( child: Center(
10.0, child: CircularProgressIndicator(),
),
shape: const CircleBorder(),
child: Icon(
provider.isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 30.0,
),
), ),
RawMaterialButton( )
constraints: BoxConstraints(), : Column(
onPressed: () { children: const [
provider.endCall().then((value) { Center(
if (value) { child: Text(
Navigator.of(context).pop(); "Testing",
} ),
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
10.0,
), ),
shape: const CircleBorder(), Center(
child: const Icon( child: Text(
Icons.call_end, "Data",
color: MyColors.white, ),
size: 30.0,
), ),
), ],
], ),
),
),
),
],
),
); );
}); });
} }

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