From 23820e7e5e4cb09f66e6a52a14352c0c8d991010 Mon Sep 17 00:00:00 2001 From: Aamir Imac Date: Tue, 25 Jul 2023 11:59:17 +0300 Subject: [PATCH] Ios Voip Kit --- lib/api/chat/chat_api_client.dart | 18 +- lib/app_state/app_state.dart | 11 + lib/classes/chat_call_kit.dart | 10 + lib/models/chat/call.dart | 4 + lib/provider/chat_call_provider.dart | 10 +- lib/provider/chat_provider_model.dart | 1 - .../chat/call/chat_incoming_call_screen.dart | 915 +++++++++--------- lib/ui/login/login_screen.dart | 109 +-- .../offers_and_discounts_details.dart | 2 +- pubspec.yaml | 3 +- 10 files changed, 570 insertions(+), 513 deletions(-) diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart index 5de715d..d127a3a 100644 --- a/lib/api/chat/chat_api_client.dart +++ b/lib/api/chat/chat_api_client.dart @@ -33,8 +33,8 @@ class ChatApiClient { "isMobile": true, "deviceToken": AppState().getIsHuawei ? AppState().getHuaweiPushToken : AppState().getDeviceToken, "isHuaweiDevice": AppState().getIsHuawei, - //"platform" : "", // ios, android - //"voipToken": "" + "platform": Platform.isIOS ? "ios" : "android", // ios, android + "voipToken": Platform.isIOS ? AppState().iosVoipPlayerID : "" }, ); @@ -188,6 +188,7 @@ class ChatApiClient { } return imagesData; } + // CallUser Login Token Future getUserCallToken({required String userid}) async { @@ -216,9 +217,6 @@ class ChatApiClient { // Call Decline On App Terminated State Future callDecline({required int cUserID, required int tUserID, required String targetUsertoken}) async { - // var headers = {'Content-Type': 'application/json'}; - // var request = http.Request('POST', Uri.parse('https://apiderichat.hmg.com/api/user/calldecline')); - Response response = await ApiClient().postJsonForResponse( "${ApiConsts.chatLoginTokenUrl}calldecline", {"currentUserId": cUserID, "targetUserId": tUserID, "secretKey": "derichatmobileuser", "targetUserToken": targetUsertoken}, @@ -231,14 +229,18 @@ class ChatApiClient { return response; } - Future OneSignalVoip({required String deviceID}) async { + Future oneSignalVoip(String value) async { + String id = ""; Response response = await ApiClient().postJsonForResponse( "${ApiConsts.oneSignalCall}players", - {"app_id": ApiConsts.oneSignalAppID, "identifier": deviceID, "device_type": 0, "test_type": !kReleaseMode ? 0 : 1}, + {"app_id": ApiConsts.oneSignalAppID, "identifier": value, "device_type": 0, "test_type": !kReleaseMode ? 1 : 0}, ); + + Map values = jsonDecode(response.body) as Map; + id = values["id"]; if (!kReleaseMode) { print("res: " + response.body); } - return response; + return id; } } diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index 7ccdd4f..29619ea 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -193,4 +193,15 @@ class AppState { } bool cancelRequestTrancsection = true; + + + String _iosVoipPlayerID = ""; + + String get iosVoipPlayerID => _iosVoipPlayerID; + + set setiosVoipPlayerID(String value) { + _iosVoipPlayerID = value; + } + + } diff --git a/lib/classes/chat_call_kit.dart b/lib/classes/chat_call_kit.dart index 616904c..3582859 100644 --- a/lib/classes/chat_call_kit.dart +++ b/lib/classes/chat_call_kit.dart @@ -153,6 +153,15 @@ class ChatVoipCall { //await ChatApiClient().callDecline(cUserID: data.extra!.callerDetails!.targetUserId!, tUserID: data.extra!.callerDetails!.currentUserId!, targetUsertoken: data.extra!.loginDetails!.token!); // logger.log(Level.error, "API-EVENT-END"); } + + Future voipDeclineCall(IosCallPayload? _iosCallPayload ) async { + try { + ALM.UserAutoLoginModel model = await ChatApiClient().getUserCallToken(userid: _iosCallPayload!.callData!.split("-").first); + dynamic Res = await ChatApiClient().callDecline(cUserID: int.parse(_iosCallPayload!.incomingCallerId!), tUserID: int.parse(_iosCallPayload!.callData!.split("-").first), targetUsertoken: model.response!.token!); + } catch (err) { + print(err); + } + } Future makeHub({required IncomingCallModel sessionData}) async { late HubConnection hc; @@ -168,3 +177,4 @@ class ChatVoipCall { } } } + diff --git a/lib/models/chat/call.dart b/lib/models/chat/call.dart index 9b6c36d..f433d3d 100644 --- a/lib/models/chat/call.dart +++ b/lib/models/chat/call.dart @@ -139,12 +139,14 @@ class IosCallPayload { String? incomingCallType; String? incomingCallerId; String? incomingCallerName; + String? callData; String? uuid; IosCallPayload({ this.incomingCallType, this.incomingCallerId, this.incomingCallerName, + this.callData, this.uuid, }); @@ -156,6 +158,7 @@ class IosCallPayload { incomingCallType: json["incoming_call_type"], incomingCallerId: json["incoming_caller_id"], incomingCallerName: json["incoming_caller_name"], + callData: json["call_data"], uuid: json["uuid"], ); @@ -163,6 +166,7 @@ class IosCallPayload { "incoming_call_type": incomingCallType, "incoming_caller_id": incomingCallerId, "incoming_caller_name": incomingCallerName, + "call_data": callData, "uuid": uuid, }; } diff --git a/lib/provider/chat_call_provider.dart b/lib/provider/chat_call_provider.dart index 8c31359..6af99b6 100644 --- a/lib/provider/chat_call_provider.dart +++ b/lib/provider/chat_call_provider.dart @@ -47,6 +47,9 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { late BuildContext providerContext; + List devices = []; + var _videoDeviceId ; + void initCallListeners({required BuildContext context}) { providerContext = context; if (kDebugMode) { @@ -62,6 +65,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { } //Video Constraints + Map videoConstraints = { "video": { "mandatory": { @@ -69,6 +73,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { "height": {"min": 720} }, "optional": [ + // {'sourceId': _videoDeviceId}, { "width": {"max": 1280} }, @@ -80,7 +85,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { "width": 1280, //420,//640,//1280, "height": 720, //240//480//720 "audio": true, - }; +}; // Audio Constraints Map audioConstraints = { @@ -584,9 +589,12 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { ///////////////// Incoming Call /////////////////////////////// Future initStreams() async { + devices = await navigator.mediaDevices.enumerateDevices(); + localVideoRenderer = RTCVideoRenderer(); remoteRenderer = RTCVideoRenderer(); await localVideoRenderer!.initialize(); + _localStream ??= await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints); localVideoRenderer!.srcObject = _localStream; await remoteRenderer!.initialize(); diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index bf4bec2..606f066 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -107,7 +107,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } catch (e) { disableChatForThisUser = true; isUserOnline = false; - notifyListeners(); } } diff --git a/lib/ui/chat/call/chat_incoming_call_screen.dart b/lib/ui/chat/call/chat_incoming_call_screen.dart index c3c0725..528e08e 100644 --- a/lib/ui/chat/call/chat_incoming_call_screen.dart +++ b/lib/ui/chat/call/chat_incoming_call_screen.dart @@ -8,10 +8,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:mohem_flutter_app/api/chat/chat_api_client.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/consts.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/call.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'; import 'package:mohem_flutter_app/models/chat/incoming_call_model.dart'; import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; @@ -46,7 +51,7 @@ class _StartCallPageState extends State { IncomingCallModel? sessionData; dynamic calls = await FlutterCallkitIncoming.activeCalls(); if (calls.isNotEmpty) { - sessionData = IncomingCallModel.fromRawJson(jsonEncode(calls[0])); + sessionData = IncomingCallModel.fromRawJson(jsonEncode(calls[0])); print(sessionData.toRawJson()); if (provider.isUserOnline) { cProv.isUserOnline = provider.isUserOnline; @@ -75,7 +80,47 @@ class _StartCallPageState extends State { } } - void startIosCall() {} + void startIosCall() async { + IosCallPayload _iosCallPayload = IosCallPayload.fromRawJson(await Utils.getStringFromPrefs("iosCallPayload")); + var userID = _iosCallPayload!.callData!.split("-").first; + var callType = _iosCallPayload!.callData!.split("-").last; + SingleUserChatModel inCallData = SingleUserChatModel( + targetUserName: _iosCallPayload.incomingCallerName, + chatEventId: 3, + targetUserId: int.parse(_iosCallPayload.incomingCallerId!), + currentUserId: int.parse(userID), + ); + if (provider.isUserOnline) { + cProv.isUserOnline = provider.isUserOnline; + if (kDebugMode) { + print("====== Processing Incoming Call in Online State ========="); + } + await cProv.startIncomingCallViaKit(inCallData: inCallData.toJson(), isVCall: callType == "video" ? true : false); + cProv.init(); + isCallConnected = true; + } else { + if (kDebugMode) { + print("====== Processing Incoming Call ========="); + } + cProv.isUserOnline = provider.isUserOnline; + UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserCallToken(userid: userID); + if (userLoginResponse.response != null) { + AppState().setchatUserDetails = userLoginResponse; + Utils.saveStringFromPrefs("userLoginChatDetails", jsonEncode(userLoginResponse.response)); + + await cProv.startIncomingCallViaKit(inCallData: inCallData.toJson(), isVCall: callType == "video" ? true : false); + try { + AppState().setchatUserDetails = UserAutoLoginModel(response: userLoginResponse.response, errorResponses: null); + await provider.buildHubConnection(context: context, ccProvider: cProv).whenComplete(() { + cProv.init(); + isCallConnected = true; + }); + } catch (e) { + logger.w(e); + } + } + } + } @override Widget build(BuildContext context) { @@ -92,459 +137,459 @@ class _StartCallPageState extends State { builder: (BuildContext context, ChatCallProvider provider, ChatProviderModel cpm, Widget? child) { return provider.isIncomingCallLoader ? const SizedBox( - width: double.infinity, - height: double.infinity, - child: Center(child: CircularProgressIndicator()), - ) + width: double.infinity, + height: double.infinity, + child: Center(child: CircularProgressIndicator()), + ) : provider.isIncomingCall - ? SizedBox( - width: double.infinity, - height: double.infinity, - child: Stack( - alignment: FractionalOffset.center, - children: [ - if (!provider.isAudioCall && provider.isVideoCall) - Positioned.fill( - child: RTCVideoView( - provider.remoteRenderer!, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, - key: const Key('remote'), - ), - ), - if (provider.isVideoCall) - DraggableWidget( - bottomMargin: 20, - topMargin: 40, - intialVisibility: true, - horizontalSpace: 20, - shadowBorderRadius: 50, - initialPosition: AnchoringPosition.topLeft, - dragController: dragController, - normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), - draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), - child: SizedBox( - height: 200, - width: 140, - child: RTCVideoView( - provider.localVideoRenderer!, - 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, + ? SizedBox( + width: double.infinity, + height: double.infinity, + child: Stack( + alignment: FractionalOffset.center, + children: [ + if (!provider.isAudioCall && provider.isVideoCall) + Positioned.fill( + child: RTCVideoView( + provider.remoteRenderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, + key: const Key('remote'), + ), ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - 40.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - margin: const EdgeInsets.all(21.0), - child: Container( - margin: const EdgeInsets.only( - left: 10.0, - right: 10.0, + if (provider.isVideoCall) + DraggableWidget( + bottomMargin: 20, + topMargin: 40, + intialVisibility: true, + horizontalSpace: 20, + shadowBorderRadius: 50, + initialPosition: AnchoringPosition.topLeft, + dragController: dragController, + normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), + draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), + child: SizedBox( + height: 200, + width: 140, + child: RTCVideoView( + provider.localVideoRenderer!, + 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.center, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 70, - width: 70, - fit: BoxFit.cover, - ), - 10.height, - Text( - provider.incomingCallData.targetUserName!, - 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, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + 40.height, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: const EdgeInsets.all(21.0), + child: Container( + margin: const EdgeInsets.only( + left: 10.0, + right: 10.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + SvgPicture.asset( + "assets/images/user.svg", + height: 70, + width: 70, + fit: BoxFit.cover, + ), + 10.height, + Text( + provider.incomingCallData.targetUserName!, + 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, + ), + ], + ), ), - letterSpacing: -0.48, - height: 23 / 24, ), - ), - 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: [ + // if (provider.isVideoCall) + RawMaterialButton( + constraints: const BoxConstraints(), + onPressed: () { + provider.loudOn(); + }, + elevation: 2.0, + fillColor: provider.isLoudSpeaker ? MyColors.textMixColor : 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.textMixColor : 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.textMixColor, + 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.textMixColor : 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(isUserOnline: cpm.isUserOnline).then((bool value) { + if (value) { + Navigator.of(context).pop(); + // print("Reintiiiiiiitttiiiiiiii"); + // provider.initStreams(); + } + }); + }, + 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, ), ), ], ), - ], - ), - ), - ), - ), - ), - 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: [ - // if (provider.isVideoCall) - RawMaterialButton( - constraints: const BoxConstraints(), - onPressed: () { - provider.loudOn(); - }, - elevation: 2.0, - fillColor: provider.isLoudSpeaker ? MyColors.textMixColor : 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.textMixColor : 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.textMixColor, - 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.textMixColor : 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(isUserOnline: cpm.isUserOnline).then((bool value) { - if (value) { - Navigator.of(context).pop(); - // print("Reintiiiiiiitttiiiiiiii"); - // provider.initStreams(); - } - }); - }, - 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, - ), - ), - ], - ), - ), - ), - ], - ), - ) - : provider.isOutGoingCall - ? SizedBox( - width: double.infinity, - height: double.infinity, - child: Stack( - alignment: FractionalOffset.center, - children: [ - if (!provider.isAudioCall && provider.isVideoCall) - Positioned.fill( - child: RTCVideoView( - provider.remoteRenderer!, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, - key: const Key('remote'), - ), - ), - if (provider.isVideoCall) - DraggableWidget( - bottomMargin: 20, - topMargin: 40, - intialVisibility: true, - horizontalSpace: 20, - shadowBorderRadius: 50, - initialPosition: AnchoringPosition.topLeft, - dragController: dragController, - normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), - draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), - child: SizedBox( - height: 200, - width: 140, - child: RTCVideoView( - provider.localVideoRenderer!, - 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, - mainAxisSize: MainAxisSize.max, + ], + ), + ) + : provider.isOutGoingCall + ? SizedBox( + width: double.infinity, + height: double.infinity, + child: Stack( + alignment: FractionalOffset.center, children: [ - 40.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - margin: const EdgeInsets.all(21.0), - child: Container( - margin: const EdgeInsets.only( - left: 10.0, - right: 10.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 70, - width: 70, - fit: BoxFit.cover, + if (!provider.isAudioCall && provider.isVideoCall) + Positioned.fill( + child: RTCVideoView( + provider.remoteRenderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, + key: const Key('remote'), + ), + ), + if (provider.isVideoCall) + DraggableWidget( + bottomMargin: 20, + topMargin: 40, + intialVisibility: true, + horizontalSpace: 20, + shadowBorderRadius: 50, + initialPosition: AnchoringPosition.topLeft, + dragController: dragController, + normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), + draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0), + child: SizedBox( + height: 200, + width: 140, + child: RTCVideoView( + provider.localVideoRenderer!, + 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, ), - 10.height, - Text( - provider.outGoingCallData.receiverName!, - style: const TextStyle( - fontSize: 21, - decoration: TextDecoration.none, - fontWeight: FontWeight.bold, - color: MyColors.white, - letterSpacing: -1.26, - height: 23 / 12, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + 40.height, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: const EdgeInsets.all(21.0), + child: Container( + margin: const EdgeInsets.only( + left: 10.0, + right: 10.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + SvgPicture.asset( + "assets/images/user.svg", + height: 70, + width: 70, + fit: BoxFit.cover, + ), + 10.height, + Text( + provider.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 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, - ), - ], + ], + ), ), ), ), - ], + ), + 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: [ + // if (provider.isVideoCall) + RawMaterialButton( + constraints: const BoxConstraints(), + onPressed: () { + provider.loudOn(); + }, + elevation: 2.0, + fillColor: provider.isLoudSpeaker ? MyColors.textMixColor : 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.textMixColor : 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.textMixColor, + 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.textMixColor : 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(isUserOnline: cpm.isUserOnline).then((bool 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, + ), + ), + ], + ), + ), ), ], ), - ), - ), - ), - ), - 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: [ - // if (provider.isVideoCall) - RawMaterialButton( - constraints: const BoxConstraints(), - onPressed: () { - provider.loudOn(); - }, - elevation: 2.0, - fillColor: provider.isLoudSpeaker ? MyColors.textMixColor : 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.textMixColor : 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.textMixColor, - 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.textMixColor : 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(isUserOnline: cpm.isUserOnline).then((bool 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, - ), - ), - ], - ), - ), - ), - ], - ), - ) - : const SizedBox(); + ) + : const SizedBox(); }, ), ); diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 149c4d2..600ebc3 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -55,7 +55,7 @@ class LoginScreen extends StatefulWidget { } } -class _LoginScreenState extends State with WidgetsBindingObserver{ +class _LoginScreenState extends State with WidgetsBindingObserver { TextEditingController username = TextEditingController(); TextEditingController password = TextEditingController(); @@ -65,7 +65,6 @@ class _LoginScreenState extends State with WidgetsBindingObserver{ late final FirebaseMessaging _firebaseMessaging; IosCallPayload? _iosCallPayload; - bool _autoLogin = false; bool? isAppOpenBySystem; @@ -80,8 +79,7 @@ class _LoginScreenState extends State with WidgetsBindingObserver{ final voIPKit = FlutterIOSVoIPKit.instance; late Timer timeOutTimer; - - + @override void initState() { super.initState(); @@ -98,82 +96,70 @@ class _LoginScreenState extends State with WidgetsBindingObserver{ setupVoIPCallBacks(); } - void _timeOut({ - int seconds = 15, - }) async { - timeOutTimer = Timer(Duration(seconds: seconds), () async { - print('🎈 example: timeOut'); - var incomingCallerName = await voIPKit.getIncomingCallerName(); - voIPKit.unansweredIncomingCall( - skipLocalNotification: false, - missedCallTitle: '📞 Missed call', - missedCallBody: 'There was a call from $incomingCallerName', - ); - }); - } - + // IOS Voip Call void setupVoIPCallBacks() { if (Platform.isIOS) { - voIPKit.getVoIPToken().then((value) { - print('🎈 example: getVoIPToken: $value'); - ChatApiClient().OneSignalVoip(deviceID: value!); + voIPKit.getVoIPToken().then((value) async { + if (value != null) { + AppState().setiosVoipPlayerID = await ChatApiClient().oneSignalVoip(value!); + } }); - - voIPKit.onDidUpdatePushToken = (String token) { - print('🎈 example: onDidUpdatePushToken: $token'); - }; } + voIPKit.onDidUpdatePushToken = (String token) { + print('🎈 example: onDidUpdatePushToken: $token'); + }; + voIPKit.onDidReceiveIncomingPush = ( - Map payload, - ) async { + Map payload, + ) async { _iosCallPayload = IosCallPayload.fromJson(payload); - logger.d(_iosCallPayload!.incomingCallerId!.split("-").last); - print('🎈 example: onDidReceiveIncomingPush $payload'); + isIncomingCall = true; _timeOut(); }; voIPKit.onDidRejectIncomingCall = ( - String uuid, - String callerId, - ) async { - try { - var logText = "did reject call $callerId"; - } catch (err) {} + String uuid, + String callerId, + ) async { + await ChatVoipCall().voipDeclineCall(_iosCallPayload); + await voIPKit.endCall(); }; voIPKit.onDidAcceptIncomingCall = ( - String uuid, - String callerId, - ) async { + String uuid, + String callerId, + ) async { var callerID = "did accept call $callerId"; - - debugPrint(callerID); - logger.d(_iosCallPayload!.incomingCallerId!.split("-").last); - debugPrint(_iosCallPayload!.incomingCallerId); - debugPrint(_iosCallPayload!.incomingCallerName); - debugPrint(_iosCallPayload!.incomingCallType); + timeOutTimer.cancel(); await connectCall(); await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); await voIPKit.callConnected(); - - Future.delayed(const Duration(milliseconds: 2500), () { - voIPKit.endCall().then((value) async {}); - }); - - timeOutTimer.cancel(); }; } + void _timeOut() async { + timeOutTimer = Timer(const Duration(seconds: 25), () async { + var incomingCallerName = await voIPKit.getIncomingCallerName(); + voIPKit.unansweredIncomingCall( + skipLocalNotification: false, + missedCallTitle: '📞 Missed call', + missedCallBody: 'There was a call from $incomingCallerName', + ); + await ChatVoipCall().voipDeclineCall(_iosCallPayload); + await voIPKit.endCall(); + }); + } Future connectCall() async { - try { - UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserCallToken(userid: _iosCallPayload!.incomingCallerId!.split("-").last); - if (userLoginResponse.response != null) { - AppState().setchatUserDetails = userLoginResponse; - Utils.saveStringFromPrefs("userLoginChatDetails", jsonEncode(userLoginResponse.response)); - } + BuildContext context = AppRoutes.navigatorKey.currentContext!; + Utils.hideLoading(context); + Utils.saveStringFromPrefs("iosCallPayload", jsonEncode(_iosCallPayload)); + var pageRoute = MaterialPageRoute(builder: (context) => StartCallPage()); + Navigator.push(context, pageRoute).whenComplete(() { + checkFirebaseToken(); + }); } catch (e) { logger.d(e); } @@ -206,13 +192,11 @@ class _LoginScreenState extends State with WidgetsBindingObserver{ // } // } - Future callListeners() async { try { print("Call Listeners Init"); FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async { switch (event!.event) { - case Event.actionCallIncoming: break; case Event.actionCallStart: @@ -239,10 +223,8 @@ class _LoginScreenState extends State with WidgetsBindingObserver{ Utils.saveStringFromPrefs("inComingCallData", "null"); FlutterCallkitIncoming.endAllCalls(); break; - } - print( '${event.toString()}\n'); - + print('${event.toString()}\n'); }); } on Exception { logger.log(Level.info, "EXCEPTION-ON-EVENTS"); @@ -289,9 +271,6 @@ class _LoginScreenState extends State with WidgetsBindingObserver{ } } - - - @override void dispose() { super.dispose(); @@ -408,7 +387,7 @@ class _LoginScreenState extends State with WidgetsBindingObserver{ // 13777 // Ab12345cd } - // if (isAppOpenBySystem!) checkFirebaseToken(); + // if (isAppOpenBySystem!) checkFirebaseToken(); Utils.showLoading(context); Future.delayed(const Duration(seconds: 2)).whenComplete(() { diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart index b4cb242..30a9fa2 100644 --- a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart @@ -76,7 +76,7 @@ class _OffersAndDiscountsDetailsState extends State { : getOffersList[0].titleEn!.toText22(isBold: true, color: const Color(0xff2B353E)).center, Html( data: AppState().isArabic(context) ? getOffersList[0].descriptionAr! : getOffersList[0].descriptionEn ?? "", - onLinkTap: (String? url, RenderContext context, Map attributes, _) { + onLinkTap: (String? url, Map attributes, _) { launchUrl(Uri.parse(url!)); }, ), diff --git a/pubspec.yaml b/pubspec.yaml index a75d032..442f752 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -87,7 +87,7 @@ dependencies: signalr_netcore: ^1.3.3 logging: ^1.0.1 swipe_to: ^1.0.2 - flutter_webrtc: ^0.9.20 + flutter_webrtc: ^0.9.34 draggable_widget: ^2.0.0 flutter_local_notifications: any flutter_callkit_incoming: ^2.0.0+1 @@ -119,7 +119,6 @@ dependencies: google_api_availability: ^3.0.1 flutter_ios_voip_kit: ^0.1.0 - file: ^6.1.4 dependency_overrides: