main_production_audio_video_call
Sikander Saleem 3 weeks ago
parent 5bba9a8974
commit e834bf322b

@ -6,6 +6,7 @@ import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/cx_module/chat/model/chat_participant_model.dart'; import 'package:test_sa/modules/cx_module/chat/model/chat_participant_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/single_btn_dialog.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart'; import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
import '../chat_provider.dart'; import '../chat_provider.dart';
@ -14,8 +15,9 @@ import 'call_provider.dart';
class AudioCallPage extends StatefulWidget { class AudioCallPage extends StatefulWidget {
Participants sender; Participants sender;
Participants recipient; Participants recipient;
bool isAccept;
AudioCallPage({Key? key, required this.sender, required this.recipient}) : super(key: key); AudioCallPage({Key? key, required this.sender, required this.recipient, this.isAccept = false}) : super(key: key);
@override @override
_AudioCallPageState createState() { _AudioCallPageState createState() {
@ -30,6 +32,7 @@ class _AudioCallPageState extends State<AudioCallPage> {
// Participants? participants; // Participants? participants;
String currentStatus = "Connecting..."; String currentStatus = "Connecting...";
bool isDialogOpen = false;
@override @override
void initState() { void initState() {
@ -50,7 +53,41 @@ class _AudioCallPageState extends State<AudioCallPage> {
return; return;
} }
callPro.initAudioCallListeners(); callPro.initAudioCallListeners();
callPro.startCall(widget.sender.employeeNumber ?? "", widget.recipient?.employeeNumber ?? "", false, null, null); if (widget.isAccept) {
callPro.acceptCall(widget.sender.employeeNumber ?? "", widget.recipient?.employeeNumber ?? "", false);
} else {
callPro.startCall(widget.sender.employeeNumber ?? "", widget.recipient?.employeeNumber ?? "", false, null, null, onSuccess: (String msg) {
callPro.dispose();
Navigator.pop(context);
}, onFailed: (String msg) {
if (isDialogOpen) {
return;
}
isDialogOpen = true;
showDialog(
context: context,
barrierDismissible: false,
useRootNavigator: false,
builder: (BuildContext cxt) => PopScope(
canPop: false,
onPopInvokedWithResult: (bool didPop, Object? result) {
if (didPop) return;
},
child: SingleBtnDialog(
title: "Failed",
message: msg,
okTitle: "Go Back",
onTap: () {
Navigator.pop(context);
callPro.dispose();
Navigator.pop(context);
}),
),
).then((val) {
isDialogOpen = false;
});
});
}
Map<String, dynamic> json = { Map<String, dynamic> json = {
// "callerID": AppState().chatDetails!.response!.id!.toString(), // "callerID": AppState().chatDetails!.response!.id!.toString(),
@ -148,7 +185,7 @@ class _AudioCallPageState extends State<AudioCallPage> {
callPro.toggleMute(); callPro.toggleMute();
}), }),
callButton("calling/end_call", () async { callButton("calling/end_call", () async {
callPro.endCall(); callPro.endCall(widget.sender.employeeNumber ?? "", widget.recipient.employeeNumber ?? "");
Navigator.pop(context); Navigator.pop(context);
}, isEndCall: true, size: 68), }, isEndCall: true, size: 68),
callButton("calling/speaker_enable", () {}), callButton("calling/speaker_enable", () {}),

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -61,26 +62,24 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
return hub; return hub;
} }
Future<bool> buildHubConnection(String employeeNumber) async { Future<bool> buildHubConnection(String employeeNumber, {HubConnection? hubC}) async {
if (hubC != null) {
callHubConnection = hubC;
await callHubConnection!.start();
return true;
}
try { try {
await _disposeConnection(); await _disposeConnection();
// isLoading = true;
// notifyListeners();
http.Response response = await ApiClient().postJsonForResponse(URLs.chatSdkToken, {"apiKey": URLs.chatApiKey, "employeeNumber": employeeNumber, "voIPToken": null}); http.Response response = await ApiClient().postJsonForResponse(URLs.chatSdkToken, {"apiKey": URLs.chatApiKey, "employeeNumber": employeeNumber, "voIPToken": null});
if (response.statusCode == 200) { if (response.statusCode == 200) {
ChatLoginResponse chatResponse = ChatLoginResponse.fromJson(jsonDecode(response.body)); ChatLoginResponse chatResponse = ChatLoginResponse.fromJson(jsonDecode(response.body));
print("chatResponse:${chatResponse.token}");
callHubConnection = await getHubConnection(chatResponse.token!); callHubConnection = await getHubConnection(chatResponse.token!);
await callHubConnection!.start(); await callHubConnection!.start();
// isLoading = false;
// notifyListeners();
return true; return true;
} }
// isLoading = false;
// notifyListeners();
return false; return false;
} catch (e) { } catch (e) {
print("buildHubConnectionE:$e");
await _disposeConnection(); await _disposeConnection();
return false; return false;
} }
@ -214,35 +213,40 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners(); notifyListeners();
} }
late String senderN; String? senderN;
late String recipientN; String? recipientN;
late bool isVideo; bool? isVideo;
int? moduleId; int? moduleId;
int? referenceID; int? referenceID;
void startCall(String senderN, String recipientN, bool isVideo, int? moduleId, int? referenceID) async { Function(String message)? onFailed;
Function(String message)? onSuccess;
void startCall(String? senderN, String? recipientN, bool? isVideo, int? moduleId, int? referenceID, {Function(String message)? onFailed, Function(String message)? onSuccess}) async {
this.senderN = senderN; this.senderN = senderN;
this.recipientN = recipientN; this.recipientN = recipientN;
this.isVideo = isVideo; this.isVideo = isVideo;
this.moduleId = moduleId; this.moduleId = moduleId;
this.referenceID = referenceID; this.referenceID = referenceID;
invoke(invokeMethod: "CallUserAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo); this.onFailed = onFailed;
this.onSuccess = onSuccess;
invoke(invokeMethod: "CallUserAsync", currentUserID: senderN!, targetUserID: recipientN!, isVideoCall: isVideo!);
} }
void acceptCall(String senderN, String recipientN, bool isVideo, int moduleId, int? referenceID) async { void acceptCall(String senderN, String recipientN, bool isVideo) async {
invoke(invokeMethod: "AnswerCallAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo); invoke(invokeMethod: "AnswerCallAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo);
} }
void declineCall(String senderN, String recipientN, bool isVideo, int moduleId, int? referenceID) async { Future declineCall(String senderN, String recipientN) async {
invoke(invokeMethod: "CallDeclinedAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo); await invoke(invokeMethod: "CallDeclinedAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: false);
} }
void endCall() { void endCall(String senderN, String recipientN) {
invoke(invokeMethod: "HangUpAsync", currentUserID: senderN, targetUserID: recipientN, data: moduleId); invoke(invokeMethod: "HangUpAsync", currentUserID: senderN, targetUserID: recipientN);
} }
void toggleMute() { void toggleMute() {
invoke(invokeMethod: "AudioToggle", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo); invoke(invokeMethod: "AudioToggle", currentUserID: senderN!, targetUserID: recipientN!, isVideoCall: isVideo!);
} }
void checkCall(Map<String, dynamic> message) { void checkCall(Map<String, dynamic> message) {
@ -280,6 +284,7 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
void onOfferAsync(List<Object?>? params) { void onOfferAsync(List<Object?>? params) {
print("onCallAcceptedAsync: $params"); print("onCallAcceptedAsync: $params");
print("onCallAcceptedAsync: $params");
} }
void onAnswerOffer(List<Object?>? params) { void onAnswerOffer(List<Object?>? params) {
@ -288,10 +293,32 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
void onHangUpAsync(List<Object?>? params) { void onHangUpAsync(List<Object?>? params) {
print("onHangUpAsync: $params"); print("onHangUpAsync: $params");
handleResponse(params);
} }
void onCallDeclinedAsync(List<Object?>? params) { void onCallDeclinedAsync(List<Object?>? params) {
print("onCallDeclinedAsync: $params"); print("onCallDeclinedAsync: $params");
handleResponse(params);
// try {
// if (params?.isNotEmpty ?? false) {
// print("onCallDeclinedAsync:${params?.length}:${params}");
// if (params!.length > 2) {
// print("runTimeData:${params[1].runtimeType}");
// var data = jsonDecode(jsonEncode(params[1].toString()));
// print("runTimeData:${data.runtimeType}");
// onFailed?.call("Call Declined, ${data["targetUserId"]} has declined your call.");
// return;
// } else { print("runTimeData:${params[0].runtimeType}");
// var data = jsonDecode(params[0].toString());
// print("runTimeData:${data.runtimeType}");
// onFailed?.call("Call Declined, ${data["targetUserId"]} has declined your call.");
// return;
// }
// }
// } catch (ex) {
// print("Ex:$ex");
// onFailed?.call("Call Declined, User has declined your call.");
// }
} }
void onAudioToggle(List<Object?>? params) { void onAudioToggle(List<Object?>? params) {
@ -308,12 +335,14 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
Future<void> invoke({required String invokeMethod, required String currentUserID, required String targetUserID, bool isVideoCall = false, var data}) async { Future<void> invoke({required String invokeMethod, required String currentUserID, required String targetUserID, bool isVideoCall = false, var data}) async {
List<Object> args = []; List<Object> args = [];
if (invokeMethod == "answerCallAsync") { if (invokeMethod == "AnswerCallAsync") {
args = [currentUserID, targetUserID];
} else if (invokeMethod == 'CallDeclinedAsync') {
args = [currentUserID, targetUserID]; args = [currentUserID, targetUserID];
} else if (invokeMethod == "CallUserAsync") { } else if (invokeMethod == "CallUserAsync") {
args = [currentUserID, targetUserID, isVideoCall]; args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "HangUpAsync ") { } else if (invokeMethod == "HangUpAsync") {
args = [currentUserID, targetUserID, data]; args = [currentUserID, targetUserID];
} else if (invokeMethod == "AudioToggle") { } else if (invokeMethod == "AudioToggle") {
args = [currentUserID, targetUserID, isVideoCall]; args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "IceCandidateAsync") { } else if (invokeMethod == "IceCandidateAsync") {
@ -323,6 +352,7 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
} else if (invokeMethod == "AnswerOfferAsync") { } else if (invokeMethod == "AnswerOfferAsync") {
args = [targetUserID, data]; args = [targetUserID, data];
} }
print("Invoking:$invokeMethod:${args}");
await callHubConnection?.invoke(invokeMethod, args: args); await callHubConnection?.invoke(invokeMethod, args: args);
} }
@ -335,4 +365,22 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
callHubConnection?.off('OnHangUpAsync'); callHubConnection?.off('OnHangUpAsync');
callHubConnection?.off('OnAudioToggle'); callHubConnection?.off('OnAudioToggle');
} }
handleResponse(List<Object?>? params) {
if (params?.isNotEmpty ?? false) {
if (params![0] == "NO_ANSWER") {
onFailed?.call("No Answer, User did not answer your call.");
return;
} else if (params[0] == "NO_ANSWER") {
onFailed?.call("Call Declined, User has declined your call.");
return;
} else if (params[0] == 'USER_BUSY') {
onFailed?.call("The user is currently busy on another call");
} else if (params[0] == 'USER_OFFLINE') {
onFailed?.call("The user is currently offline");
} else {
onSuccess?.call("");
}
}
}
} }

@ -18,6 +18,7 @@ import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/modules/cx_module/chat/calling/audio_call_page.dart'; import 'package:test_sa/modules/cx_module/chat/calling/audio_call_page.dart';
import 'package:test_sa/modules/cx_module/chat/calling/call_provider.dart';
import 'package:test_sa/modules/cx_module/chat/model/chat_participant_model.dart'; import 'package:test_sa/modules/cx_module/chat/model/chat_participant_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
@ -216,60 +217,76 @@ class _LandPageState extends State<LandPage> {
} }
callingEvents(BuildContext context) { callingEvents(BuildContext context) {
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async { FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
switch (event!.eventName) { if (event == null) return;
case CallEventConstants.actionCallIncoming:
// TODO: received an incoming call switch (event) {
break; case CallEventActionCallAccept(:final callKitParams):
case CallEventConstants.actionCallStart: _handleCallAccepted(callKitParams);
// TODO: started an outgoing call
// TODO: show screen calling in Flutter case CallEventActionCallDecline(:final callKitParams):
break; _handleCallDeclined(callKitParams);
case CallEventConstants.actionCallAccept:
var activeCalls = await FlutterCallkitIncoming.activeCalls(); case CallEventActionDidUpdateDevicePushTokenVoip():
if (activeCalls is List && activeCalls.isNotEmpty) { // TODO: Handle this case.
final lastCall = activeCalls.last; throw UnimplementedError();
await FlutterCallkitIncoming.endCall(lastCall.extra?["callId"]); case CallEventActionCallIncoming():
FlutterCallkitIncoming.endAllCalls(); // TODO: Handle this case.
Navigator.of(context).push(MaterialPageRoute( throw UnimplementedError();
builder: (_) => case CallEventActionCallStart():
AudioCallPage(sender: Participants(employeeNumber: lastCall.extra?["calleeEmployeeNumber"]), recipient: Participants(employeeNumber: lastCall.extra?["callerEmployeeNumber"])))); // TODO: Handle this case.
} throw UnimplementedError();
break; case CallEventActionCallEnded():
case CallEventConstants.actionCallDecline: // TODO: Handle this case.
// TODO: declined an incoming call throw UnimplementedError();
break; case CallEventActionCallTimeout():
case CallEventConstants.actionCallEnded: // TODO: Handle this case.
// TODO: ended an incoming/outgoing call throw UnimplementedError();
break; case CallEventActionCallConnected():
case CallEventConstants.actionCallTimeout: // TODO: Handle this case.
// TODO: missed an incoming call throw UnimplementedError();
break; case CallEventActionCallCallback():
case CallEventConstants.actionCallCallback: // TODO: Handle this case.
// TODO: click action `Call back` from missed call notification throw UnimplementedError();
break; case CallEventActionCallToggleHold():
case CallEventConstants.actionCallToggleHold: // TODO: Handle this case.
// TODO: only iOS throw UnimplementedError();
break; case CallEventActionCallToggleMute():
case CallEventConstants.actionCallToggleMute: // TODO: Handle this case.
// TODO: only iOS throw UnimplementedError();
break; case CallEventActionCallToggleDmtf():
case CallEventConstants.actionCallToggleDmtf: // TODO: Handle this case.
// TODO: only iOS throw UnimplementedError();
break; case CallEventActionCallToggleGroup():
case CallEventConstants.actionCallToggleGroup: // TODO: Handle this case.
// TODO: only iOS throw UnimplementedError();
break; case CallEventActionCallToggleAudioSession():
case CallEventConstants.actionCallToggleAudioSession: // TODO: Handle this case.
// TODO: only iOS throw UnimplementedError();
break; case CallEventActionCallCustom():
case CallEventConstants.actionDidUpdateDevicePushTokenVoip: // TODO: Handle this case.
// TODO: only iOS throw UnimplementedError();
break;
case CallEventConstants.actionCallCustom:
// TODO: for custom action
break;
} }
}); });
} }
Future<void> _handleCallDeclined(CallKitParams callKitParams) async {
FlutterCallkitIncoming.endCall(callKitParams.extra?["callId"]);
var calPro = Provider.of<CallProvider>(context, listen: false);
bool isConnected = await calPro.buildHubConnection(callKitParams.extra?["calleeEmployeeNumber"]);
if (isConnected) {
await calPro.declineCall(callKitParams.extra?["calleeEmployeeNumber"], callKitParams.extra?["callerEmployeeNumber"]);
calPro.dispose();
}
}
void _handleCallAccepted(CallKitParams callKitParams) {
FlutterCallkitIncoming.endCall(callKitParams.extra?["callId"]);
FlutterCallkitIncoming.endAllCalls();
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AudioCallPage(
sender: Participants(employeeNumber: callKitParams.extra?["calleeEmployeeNumber"]),
recipient: Participants(employeeNumber: callKitParams.extra?["callerEmployeeNumber"]),
isAccept: true)));
}
} }

Loading…
Cancel
Save