main_production_audio_video_call
Sikander Saleem 2 weeks ago
parent e834bf322b
commit 957b6eddea

@ -324,10 +324,18 @@ Future<void> handleDataMessage(Map<String, dynamic> messageData, bool isFromBack
return false;
}();
final moduleId = messageData['moduleId'] as String? ?? messageData['applicationId']?.toString();
String? moduleId = messageData['moduleId'] as String? ?? messageData['applicationId']?.toString();
if (messageData['moduleId'] != null && messageData['moduleId'].toString().isNotEmpty) {
moduleId = messageData['moduleId']?.toString();
} else if (messageData['applicationId'] != null && messageData['applicationId'].toString().isNotEmpty) {
moduleId = messageData['applicationId']?.toString();
}
final referenceId = messageData['referenceId'] as String?;
final conversationId = messageData['conversationId'] as String?;
print("handleDataMessage:moduleId:$moduleId");
WidgetsFlutterBinding.ensureInitialized();
Timer? _callTimeoutTimer;
try {

@ -158,7 +158,9 @@ import 'modules/cleaner_module/provider/cleaner_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
FlutterCallkitIncoming.onBackgroundMessage(onBackgroundMessage);
if (Platform.isAndroid) {
FlutterCallkitIncoming.onBackgroundMessage(onBackgroundMessage);
}
FlutterCallkitIncoming.endAllCalls();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
// HttpOverrides.global = MyHttpOverrides(); // for later use.

@ -15,9 +15,11 @@ import 'call_provider.dart';
class AudioCallPage extends StatefulWidget {
Participants sender;
Participants recipient;
int moduleId;
int? referenceID;
bool isAccept;
AudioCallPage({Key? key, required this.sender, required this.recipient, this.isAccept = false}) : super(key: key);
AudioCallPage({Key? key, required this.sender, required this.recipient, this.isAccept = false, required this.moduleId, this.referenceID}) : super(key: key);
@override
_AudioCallPageState createState() {
@ -31,7 +33,6 @@ class _AudioCallPageState extends State<AudioCallPage> {
// Participants? participants;
String currentStatus = "Connecting...";
bool isDialogOpen = false;
@override
@ -54,12 +55,14 @@ class _AudioCallPageState extends State<AudioCallPage> {
}
callPro.initAudioCallListeners();
if (widget.isAccept) {
callPro.acceptCall(widget.sender.employeeNumber ?? "", widget.recipient?.employeeNumber ?? "", false);
callPro.acceptCall(widget.sender.employeeNumber ?? "", widget.recipient?.employeeNumber ?? "", false, moduleId: widget.moduleId);
} else {
callPro.startCall(widget.sender.employeeNumber ?? "", widget.recipient?.employeeNumber ?? "", false, null, null, onSuccess: (String msg) {
callPro.startCall(widget.sender.employeeNumber ?? "", widget.recipient?.employeeNumber ?? "", false, widget.moduleId, widget.referenceID, onSuccess: (String msg) {
callPro.dispose();
Navigator.pop(context);
}, onFailed: (String msg) {
callPro.dispose();
print("isDialogOpen:$isDialogOpen");
if (isDialogOpen) {
return;
}
@ -79,7 +82,6 @@ class _AudioCallPageState extends State<AudioCallPage> {
okTitle: "Go Back",
onTap: () {
Navigator.pop(context);
callPro.dispose();
Navigator.pop(context);
}),
),
@ -174,18 +176,29 @@ class _AudioCallPageState extends State<AudioCallPage> {
24.height,
InfoTextLabelWidget(label: chatProvider.recipient?.userName ?? "", textStyle: AppTextStyles.heading4.copyWith(fontWeight: FontWeight.w600), color: Colors.white),
4.height,
InfoTextLabelWidget(label: currentStatus, textStyle: AppTextStyles.heading6, color: Colors.white)
Selector<CallProvider, String>(
selector: (_, myModel) => myModel.currentStatus, // Selects only the userName
builder: (_, currentStatus, __) => InfoTextLabelWidget(label: currentStatus, textStyle: AppTextStyles.heading6, color: Colors.white),
),
4.height,
Selector<CallProvider, bool>(
selector: (_, myModel) => myModel.isPeerMuted,
builder: (_, isPeerMuted, __) => isPeerMuted ? InfoTextLabelWidget(label: "Mic Mute", textStyle: AppTextStyles.tinyFont, color: AppColor.red30) : const SizedBox(),
),
],
),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
callButton("calling/mic_enable", () {
callPro.toggleMute();
}),
Selector<CallProvider, bool>(
selector: (_, myModel) => myModel.isMuted, // Selects only the userName
builder: (_, isMuted, __) => callButton(isMuted ? "calling/mic_disable" : "calling/mic_enable", () {
callPro.toggleMute();
}),
),
callButton("calling/end_call", () async {
callPro.endCall(widget.sender.employeeNumber ?? "", widget.recipient.employeeNumber ?? "");
callPro.endCall(widget.sender.employeeNumber ?? "", widget.recipient.employeeNumber ?? "", moduleId: widget.moduleId);
Navigator.pop(context);
}, isEndCall: true, size: 68),
callButton("calling/speaker_enable", () {}),

@ -35,6 +35,8 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
bool isLoading = false;
String currentStatus = "Connecting...";
Future<void> _disposeConnection() async {
try {
if (callHubConnection != null) {
@ -216,7 +218,7 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
String? senderN;
String? recipientN;
bool? isVideo;
int? moduleId;
int moduleId = 1004;
int? referenceID;
Function(String message)? onFailed;
@ -226,27 +228,29 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
this.senderN = senderN;
this.recipientN = recipientN;
this.isVideo = isVideo;
this.moduleId = moduleId;
// this.moduleId = moduleId;
this.referenceID = referenceID;
this.onFailed = onFailed;
this.onSuccess = onSuccess;
invoke(invokeMethod: "CallUserAsync", currentUserID: senderN!, targetUserID: recipientN!, isVideoCall: isVideo!);
invoke(invokeMethod: "CallUserAsync", currentUserID: senderN!, targetUserID: recipientN!, isVideoCall: isVideo!, data: [this.moduleId, referenceID]);
}
void acceptCall(String senderN, String recipientN, bool isVideo) async {
invoke(invokeMethod: "AnswerCallAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo);
void acceptCall(String senderN, String recipientN, bool isVideo, {required int moduleId}) async {
invoke(invokeMethod: "AnswerCallAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: isVideo, data: [this.moduleId]);
}
Future declineCall(String senderN, String recipientN) async {
await invoke(invokeMethod: "CallDeclinedAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: false);
Future declineCall(String senderN, String recipientN, {required int moduleId}) async {
await invoke(invokeMethod: "CallDeclinedAsync", currentUserID: senderN, targetUserID: recipientN, isVideoCall: false, data: [this.moduleId]);
}
void endCall(String senderN, String recipientN) {
invoke(invokeMethod: "HangUpAsync", currentUserID: senderN, targetUserID: recipientN);
void endCall(String senderN, String recipientN, {required int moduleId}) {
invoke(invokeMethod: "HangUpAsync", currentUserID: senderN, targetUserID: recipientN, data: [moduleId]);
}
void toggleMute() {
invoke(invokeMethod: "AudioToggle", currentUserID: senderN!, targetUserID: recipientN!, isVideoCall: isVideo!);
void toggleMute() async {
await invoke(invokeMethod: "AudioToggle", currentUserID: senderN!, targetUserID: recipientN!);
isMuted = !isMuted;
notifyListeners();
}
void checkCall(Map<String, dynamic> message) {
@ -323,7 +327,7 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
void onAudioToggle(List<Object?>? params) {
print("onAudioToggle: $params");
isMuted = !isMuted;
isPeerMuted = !isPeerMuted;
notifyListeners();
}
@ -335,16 +339,19 @@ 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 (data != null) {
data = data.toString().replaceFirst("[", "").replaceFirst("]", "");
}
if (invokeMethod == "AnswerCallAsync") {
args = [currentUserID, targetUserID];
args = [currentUserID, targetUserID, data];
} else if (invokeMethod == 'CallDeclinedAsync') {
args = [currentUserID, targetUserID];
args = [currentUserID, targetUserID, data];
} else if (invokeMethod == "CallUserAsync") {
args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "HangUpAsync") {
args = [currentUserID, targetUserID];
args = [currentUserID, targetUserID, data];
} else if (invokeMethod == "AudioToggle") {
args = [currentUserID, targetUserID, isVideoCall];
args = [currentUserID, targetUserID];
} else if (invokeMethod == "IceCandidateAsync") {
args = [targetUserID, data];
} else if (invokeMethod == "OfferAsync") {
@ -376,6 +383,8 @@ class CallProvider with ChangeNotifier, DiagnosticableTreeMixin {
return;
} else if (params[0] == 'USER_BUSY') {
onFailed?.call("The user is currently busy on another call");
} else if (params[0] == 'USER_DECLINE') {
onFailed?.call("Call Declined, User has declined your call.");
} else if (params[0] == 'USER_OFFLINE') {
onFailed?.call("The user is currently offline");
} else {

@ -202,75 +202,95 @@ class _ChatPageState extends State<ChatPage> {
appBar: DefaultAppBar(
title: widget.title,
actions: [
IconButton(
icon: "calling/audio_call_icon".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Audio",
onPressed: () async {
bool audioPermission = await Provider.of<ChatProvider>(context, listen: false).checkAudioPermissions();
if (!audioPermission) {
context.showConfirmDialog("To perform audio call, we need permission to access microphone.", title: "Permission Required", onTap: () async {
Navigator.pop(context);
audioPermission = await Provider.of<ChatProvider>(context, listen: false).requestAudioPermissions();
if (!audioPermission) {
bool openSetting = await showDialog(
context: context,
builder: (_) => const AAlertDialog(
title: "Permission denied",
content: "Permission is denied. Please go to application Setting to enable it.",
button1Text: "Cancel",
button2Text: "Open Setting",
Selector<ChatProvider, _ChatConnectionState>(
selector: (_, provider) => _ChatConnectionState(
isLoading: provider.chatLoginTokenLoading,
loginResponse: provider.chatLoginResponse,
),
builder: (context, connectionState, child) {
if (!connectionState.isLoading && connectionState.loginResponse == null) {
return SizedBox();
} else {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: "calling/audio_call_icon".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Audio",
onPressed: () async {
bool audioPermission = await Provider.of<ChatProvider>(context, listen: false).checkAudioPermissions();
if (!audioPermission) {
context.showConfirmDialog("To perform audio call, we need permission to access microphone.", title: "Permission Required", onTap: () async {
Navigator.pop(context);
audioPermission = await Provider.of<ChatProvider>(context, listen: false).requestAudioPermissions();
if (!audioPermission) {
bool openSetting = await showDialog(
context: context,
builder: (_) => const AAlertDialog(
title: "Permission denied",
content: "Permission is denied. Please go to application Setting to enable it.",
button1Text: "Cancel",
button2Text: "Open Setting",
),
);
if (!openSetting) PermissionHandler.openAppSettings();
return;
} else {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AudioCallPage(
sender: Provider.of<ChatProvider>(context, listen: false).sender!,
recipient: Provider.of<ChatProvider>(context, listen: false).recipient!,
moduleId: widget.moduleId,
referenceID: widget.requestId,
)));
}
});
} else {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AudioCallPage(
sender: Provider.of<ChatProvider>(context, listen: false).sender!,
recipient: Provider.of<ChatProvider>(context, listen: false).recipient!,
moduleId: widget.moduleId,
referenceID: widget.requestId,
)));
}
},
),
);
if (!openSetting) PermissionHandler.openAppSettings();
return;
} else {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AudioCallPage(
sender: Provider.of<ChatProvider>(context, listen: false).sender!,
recipient: Provider.of<ChatProvider>(context, listen: false).recipient!,
)));
}
});
} else {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AudioCallPage(
sender: Provider.of<ChatProvider>(context, listen: false).sender!,
recipient: Provider.of<ChatProvider>(context, listen: false).recipient!,
)));
}
},
),
IconButton(
icon: "calling/video_call_icon".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Video",
onPressed: () async {
bool videoPermission = await Provider.of<ChatProvider>(context, listen: false).checkVideoPermissions();
if (!videoPermission) {
context.showConfirmDialog("To perform Video call, we need permission to access microphone & Camera.", title: "Permission Required", onTap: () async {
Navigator.pop(context);
videoPermission = await Provider.of<ChatProvider>(context, listen: false).requestVideoPermissions();
if (videoPermission) {
bool openSetting = await showDialog(
context: context,
builder: (_) => const AAlertDialog(
title: "Permission denied",
content: "Permission is denied. Please go to application Setting to enable it.",
button1Text: "Cancel",
button2Text: "Open Setting",
IconButton(
icon: "calling/video_call_icon".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Video",
onPressed: () async {
bool videoPermission = await Provider.of<ChatProvider>(context, listen: false).checkVideoPermissions();
if (!videoPermission) {
context.showConfirmDialog("To perform Video call, we need permission to access microphone & Camera.", title: "Permission Required", onTap: () async {
Navigator.pop(context);
videoPermission = await Provider.of<ChatProvider>(context, listen: false).requestVideoPermissions();
if (videoPermission) {
bool openSetting = await showDialog(
context: context,
builder: (_) => const AAlertDialog(
title: "Permission denied",
content: "Permission is denied. Please go to application Setting to enable it.",
button1Text: "Cancel",
button2Text: "Open Setting",
),
);
if (!openSetting) PermissionHandler.openAppSettings();
return;
}
});
}
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CallPage(isAudio: false)));
},
),
);
if (!openSetting) PermissionHandler.openAppSettings();
return;
}
});
}
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CallPage(isAudio: false)));
},
),
],
);
}
}),
],
),
body: Selector<ChatProvider, _ChatConnectionState>(

@ -219,7 +219,7 @@ class _LandPageState extends State<LandPage> {
callingEvents(BuildContext context) {
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
if (event == null) return;
print("callingEvents:$event");
switch (event) {
case CallEventActionCallAccept(:final callKitParams):
_handleCallAccepted(callKitParams);
@ -236,9 +236,8 @@ class _LandPageState extends State<LandPage> {
case CallEventActionCallStart():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallEnded():
// TODO: Handle this case.
throw UnimplementedError();
case CallEventActionCallEnded(:final callKitParams):
_handleCallEnded(callKitParams);
case CallEventActionCallTimeout():
// TODO: Handle this case.
throw UnimplementedError();
@ -275,11 +274,16 @@ class _LandPageState extends State<LandPage> {
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"]);
await calPro.declineCall(callKitParams.extra?["calleeEmployeeNumber"], callKitParams.extra?["callerEmployeeNumber"], moduleId: int.parse(callKitParams.extra!["moduleId"]));
calPro.dispose();
}
}
Future<void> _handleCallEnded(CallKitParams callKitParams) async {
FlutterCallkitIncoming.endCall(callKitParams.extra?["callId"]);
FlutterCallkitIncoming.endAllCalls();
}
void _handleCallAccepted(CallKitParams callKitParams) {
FlutterCallkitIncoming.endCall(callKitParams.extra?["callId"]);
FlutterCallkitIncoming.endAllCalls();
@ -287,6 +291,7 @@ class _LandPageState extends State<LandPage> {
builder: (_) => AudioCallPage(
sender: Participants(employeeNumber: callKitParams.extra?["calleeEmployeeNumber"]),
recipient: Participants(employeeNumber: callKitParams.extra?["callerEmployeeNumber"]),
moduleId: int.parse(callKitParams.extra!["moduleId"]),
isAccept: true)));
}
}

Loading…
Cancel
Save