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/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/swipe_module/dialoge/single_btn_dialog.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
import '../chat_provider.dart';
@ -14,8 +15,9 @@ import 'call_provider.dart';
class AudioCallPage extends StatefulWidget {
Participants sender;
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
_AudioCallPageState createState() {
@ -30,6 +32,7 @@ class _AudioCallPageState extends State<AudioCallPage> {
// Participants? participants;
String currentStatus = "Connecting...";
bool isDialogOpen = false;
@override
void initState() {
@ -50,7 +53,41 @@ class _AudioCallPageState extends State<AudioCallPage> {
return;
}
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 = {
// "callerID": AppState().chatDetails!.response!.id!.toString(),
@ -148,7 +185,7 @@ class _AudioCallPageState extends State<AudioCallPage> {
callPro.toggleMute();
}),
callButton("calling/end_call", () async {
callPro.endCall();
callPro.endCall(widget.sender.employeeNumber ?? "", widget.recipient.employeeNumber ?? "");
Navigator.pop(context);
}, isEndCall: true, size: 68),
callButton("calling/speaker_enable", () {}),

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:math';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
@ -61,26 +62,24 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
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 {
await _disposeConnection();
// isLoading = true;
// notifyListeners();
http.Response response = await ApiClient().postJsonForResponse(URLs.chatSdkToken, {"apiKey": URLs.chatApiKey, "employeeNumber": employeeNumber, "voIPToken": null});
if (response.statusCode == 200) {
ChatLoginResponse chatResponse = ChatLoginResponse.fromJson(jsonDecode(response.body));
print("chatResponse:${chatResponse.token}");
callHubConnection = await getHubConnection(chatResponse.token!);
await callHubConnection!.start();
// isLoading = false;
// notifyListeners();
return true;
}
// isLoading = false;
// notifyListeners();
return false;
} catch (e) {
print("buildHubConnectionE:$e");
await _disposeConnection();
return false;
}
@ -214,35 +213,40 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
late String senderN;
late String recipientN;
late bool isVideo;
String? senderN;
String? recipientN;
bool? isVideo;
int? moduleId;
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.recipientN = recipientN;
this.isVideo = isVideo;
this.moduleId = moduleId;
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);
}
void declineCall(String senderN, String recipientN, bool isVideo, int moduleId, int? referenceID) async {
invoke(invokeMethod: "CallDeclinedAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo);
Future declineCall(String senderN, String recipientN) async {
await invoke(invokeMethod: "CallDeclinedAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: false);
}
void endCall() {
invoke(invokeMethod: "HangUpAsync", currentUserID: senderN, targetUserID: recipientN, data: moduleId);
void endCall(String senderN, String recipientN) {
invoke(invokeMethod: "HangUpAsync", currentUserID: senderN, targetUserID: recipientN);
}
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) {
@ -280,6 +284,7 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
void onOfferAsync(List<Object?>? params) {
print("onCallAcceptedAsync: $params");
print("onCallAcceptedAsync: $params");
}
void onAnswerOffer(List<Object?>? params) {
@ -288,10 +293,32 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
void onHangUpAsync(List<Object?>? params) {
print("onHangUpAsync: $params");
handleResponse(params);
}
void onCallDeclinedAsync(List<Object?>? 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) {
@ -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 {
List<Object> args = [];
if (invokeMethod == "answerCallAsync") {
if (invokeMethod == "AnswerCallAsync") {
args = [currentUserID, targetUserID];
} else if (invokeMethod == 'CallDeclinedAsync') {
args = [currentUserID, targetUserID];
} else if (invokeMethod == "CallUserAsync") {
args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "HangUpAsync ") {
args = [currentUserID, targetUserID, data];
} else if (invokeMethod == "HangUpAsync") {
args = [currentUserID, targetUserID];
} else if (invokeMethod == "AudioToggle") {
args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "IceCandidateAsync") {
@ -323,6 +352,7 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
} else if (invokeMethod == "AnswerOfferAsync") {
args = [targetUserID, data];
}
print("Invoking:$invokeMethod:${args}");
await callHubConnection?.invoke(invokeMethod, args: args);
}
@ -335,4 +365,22 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
callHubConnection?.off('OnHangUpAsync');
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/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/call_provider.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/common_widgets/app_filled_button.dart';
@ -216,60 +217,76 @@ class _LandPageState extends State<LandPage> {
}
callingEvents(BuildContext context) {
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async {
switch (event!.eventName) {
case CallEventConstants.actionCallIncoming:
// TODO: received an incoming call
break;
case CallEventConstants.actionCallStart:
// TODO: started an outgoing call
// TODO: show screen calling in Flutter
break;
case CallEventConstants.actionCallAccept:
var activeCalls = await FlutterCallkitIncoming.activeCalls();
if (activeCalls is List && activeCalls.isNotEmpty) {
final lastCall = activeCalls.last;
await FlutterCallkitIncoming.endCall(lastCall.extra?["callId"]);
FlutterCallkitIncoming.endAllCalls();
Navigator.of(context).push(MaterialPageRoute(
builder: (_) =>
AudioCallPage(sender: Participants(employeeNumber: lastCall.extra?["calleeEmployeeNumber"]), recipient: Participants(employeeNumber: lastCall.extra?["callerEmployeeNumber"]))));
}
break;
case CallEventConstants.actionCallDecline:
// TODO: declined an incoming call
break;
case CallEventConstants.actionCallEnded:
// TODO: ended an incoming/outgoing call
break;
case CallEventConstants.actionCallTimeout:
// TODO: missed an incoming call
break;
case CallEventConstants.actionCallCallback:
// TODO: click action `Call back` from missed call notification
break;
case CallEventConstants.actionCallToggleHold:
// TODO: only iOS
break;
case CallEventConstants.actionCallToggleMute:
// TODO: only iOS
break;
case CallEventConstants.actionCallToggleDmtf:
// TODO: only iOS
break;
case CallEventConstants.actionCallToggleGroup:
// TODO: only iOS
break;
case CallEventConstants.actionCallToggleAudioSession:
// TODO: only iOS
break;
case CallEventConstants.actionDidUpdateDevicePushTokenVoip:
// TODO: only iOS
break;
case CallEventConstants.actionCallCustom:
// TODO: for custom action
break;
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
if (event == null) return;
switch (event) {
case CallEventActionCallAccept(:final callKitParams):
_handleCallAccepted(callKitParams);
case CallEventActionCallDecline(:final callKitParams):
_handleCallDeclined(callKitParams);
case CallEventActionDidUpdateDevicePushTokenVoip():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallIncoming():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallStart():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallEnded():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallTimeout():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallConnected():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallCallback():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallToggleHold():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallToggleMute():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallToggleDmtf():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallToggleGroup():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallToggleAudioSession():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallCustom():
// TODO: Handle this case.
throw UnimplementedError();
}
});
}
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