diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index 29619ea..b7dfa34 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -202,6 +202,12 @@ class AppState { set setiosVoipPlayerID(String value) { _iosVoipPlayerID = value; } + + bool _isUserOnline = false; + bool get getisUserOnline => _isUserOnline; + set setisUserOnline(bool value) { + _isUserOnline = value; + } } diff --git a/lib/classes/chat_call_kit.dart b/lib/classes/chat_call_kit.dart index 3582859..644c552 100644 --- a/lib/classes/chat_call_kit.dart +++ b/lib/classes/chat_call_kit.dart @@ -156,8 +156,8 @@ class ChatVoipCall { 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!); + ALM.UserAutoLoginModel model = await ChatApiClient().getUserCallToken(userid: _iosCallPayload!.incomingCallReciverId.toString()); + dynamic Res = await ChatApiClient().callDecline(cUserID: int.parse(_iosCallPayload!.incomingCallerId!), tUserID: int.parse(_iosCallPayload!.incomingCallReciverId.toString()), targetUsertoken: model.response!.token!); } catch (err) { print(err); } diff --git a/lib/provider/chat_call_provider.dart b/lib/provider/chat_call_provider.dart index 6af99b6..0bfa728 100644 --- a/lib/provider/chat_call_provider.dart +++ b/lib/provider/chat_call_provider.dart @@ -48,7 +48,7 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { late BuildContext providerContext; List devices = []; - var _videoDeviceId ; + var _videoDeviceId; void initCallListeners({required BuildContext context}) { providerContext = context; @@ -64,29 +64,6 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { // chatHubConnection.on("OnIncomingCallAsync", OnIncomingCallAsync); } - //Video Constraints - - Map videoConstraints = { - "video": { - "mandatory": { - "width": {"min": 1280}, - "height": {"min": 720} - }, - "optional": [ - // {'sourceId': _videoDeviceId}, - { - "width": {"max": 1280} - }, - {"frameRate": 60}, - {"facingMode": "user"} - ] - }, - "frameRate": 60, - "width": 1280, //420,//640,//1280, - "height": 720, //240//480//720 - "audio": true, -}; - // Audio Constraints Map audioConstraints = { "sampleRate": 8000, @@ -112,6 +89,12 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { await initStreams(); await startCall(callType: isVideoCall ? "Video" : "Audio", context: context); _pc = await creatOfferWithCon(); + connectOutgoing(); + notifyListeners(); + } + + void connectOutgoing() { + isOutGoingCall = true; notifyListeners(); } @@ -589,13 +572,68 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { ///////////////// Incoming Call /////////////////////////////// Future initStreams() async { - devices = await navigator.mediaDevices.enumerateDevices(); - + List devices = await navigator.mediaDevices.enumerateDevices(); localVideoRenderer = RTCVideoRenderer(); remoteRenderer = RTCVideoRenderer(); await localVideoRenderer!.initialize(); - _localStream ??= await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints); + _localStream ??= await navigator.mediaDevices.getUserMedia(isVideoCall + // ? Platform.isIOS + // ? // iOS media constraints for maximum quality camera + // { + // 'audio': true, + // 'video': { + // 'facingMode': 'user', // Use 'user' for front camera, 'environment' for back camera + // 'width': { + // 'ideal': 1080, // Set the ideal width (maximum quality) + // }, + // 'height': { + // 'ideal': 1920, // Set the ideal height (maximum quality) + // }, + // 'frameRate': { + // 'ideal': 30, // Set the ideal frame rate (adjust as needed) + // }, + // }, + // } + // : // Android media constraints for maximum quality camera + // { + // 'audio': true, + // 'video': { + // 'facingMode': 'user', // Use 'user' for front camera, 'environment' for back camera + // 'width': { + // 'ideal': 1920, // Set the ideal width (maximum quality) + // }, + // 'height': { + // 'ideal': 1080, // Set the ideal height (maximum quality) + // }, + // 'frameRate': { + // 'ideal': 30, // Set the ideal frame rate (adjust as needed) + // }, + // }, + // } + + ? { + "video": { + "mandatory": { + "width": {"min": 1080}, + "height": {"min": 1920} + }, + "optional": [ + {'sourceId': devices[1].deviceId}, + { + "width": {"max": 1080} + }, + {"frameRate": 30}, + {"facingMode": "user"} + ] + }, + "frameRate": 30, + "width": 1080, //420,//640,//1280, + "height": 1920, //240//480//720 + "audio": true, + } + + : audioConstraints); localVideoRenderer!.srcObject = _localStream; await remoteRenderer!.initialize(); notifyListeners(); diff --git a/lib/ui/chat/call/chat_incoming_call_screen.dart b/lib/ui/chat/call/chat_incoming_call_screen.dart index 528e08e..0c0e1f2 100644 --- a/lib/ui/chat/call/chat_incoming_call_screen.dart +++ b/lib/ui/chat/call/chat_incoming_call_screen.dart @@ -81,14 +81,15 @@ class _StartCallPageState extends State { } void startIosCall() async { + print(await Utils.getStringFromPrefs("iosCallPayload")); IosCallPayload _iosCallPayload = IosCallPayload.fromRawJson(await Utils.getStringFromPrefs("iosCallPayload")); - var userID = _iosCallPayload!.callData!.split("-").first; - var callType = _iosCallPayload!.callData!.split("-").last; + var userID = _iosCallPayload!.incomingCallReciverId; + var callType = _iosCallPayload!.incomingCallType; SingleUserChatModel inCallData = SingleUserChatModel( targetUserName: _iosCallPayload.incomingCallerName, chatEventId: 3, targetUserId: int.parse(_iosCallPayload.incomingCallerId!), - currentUserId: int.parse(userID), + currentUserId: int.parse(userID.toString()), ); if (provider.isUserOnline) { cProv.isUserOnline = provider.isUserOnline; @@ -103,7 +104,7 @@ class _StartCallPageState extends State { print("====== Processing Incoming Call ========="); } cProv.isUserOnline = provider.isUserOnline; - UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserCallToken(userid: userID); + UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserCallToken(userid: userID.toString()); if (userLoginResponse.response != null) { AppState().setchatUserDetails = userLoginResponse; Utils.saveStringFromPrefs("userLoginChatDetails", jsonEncode(userLoginResponse.response)); @@ -142,19 +143,20 @@ class _StartCallPageState extends State { child: Center(child: CircularProgressIndicator()), ) : provider.isIncomingCall - ? SizedBox( + ? Container( width: double.infinity, height: double.infinity, + color: Colors.black, child: Stack( alignment: FractionalOffset.center, children: [ if (!provider.isAudioCall && provider.isVideoCall) - Positioned.fill( - child: RTCVideoView( - provider.remoteRenderer!, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, - key: const Key('remote'), - ), + RTCVideoView( + provider.remoteRenderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, + filterQuality: FilterQuality.high + , + key: const Key('remote'), ), if (provider.isVideoCall) DraggableWidget( @@ -173,6 +175,7 @@ class _StartCallPageState extends State { child: RTCVideoView( provider.localVideoRenderer!, mirror: true, + filterQuality: FilterQuality.high, objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, ), ), @@ -374,12 +377,10 @@ class _StartCallPageState extends State { alignment: FractionalOffset.center, children: [ if (!provider.isAudioCall && provider.isVideoCall) - Positioned.fill( - child: RTCVideoView( - provider.remoteRenderer!, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, - key: const Key('remote'), - ), + RTCVideoView( + provider.remoteRenderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, + key: const Key('remote'), ), if (provider.isVideoCall) DraggableWidget( diff --git a/lib/ui/chat/call/chat_outgoing_call_screen.dart b/lib/ui/chat/call/chat_outgoing_call_screen.dart index 1db6ecc..872c98e 100644 --- a/lib/ui/chat/call/chat_outgoing_call_screen.dart +++ b/lib/ui/chat/call/chat_outgoing_call_screen.dart @@ -25,16 +25,18 @@ class OutGoingCall extends StatefulWidget { class _OutGoingCallState extends State { late ChatCallProvider callProvider; late ChatProviderModel chatProvider; + bool loader = true; @override void initState() { super.initState(); } - void init() { + Future init() async { widget.isVideoCall ? callProvider.isVideoCall = true : callProvider.isVideoCall = false; callProvider.isOutGoingCall = true; - callProvider.initLocalCamera(chatProvmodel: chatProvider, callData: widget.outGoingCallData, context: context); + await callProvider.initLocalCamera(chatProvmodel: chatProvider, callData: widget.outGoingCallData, context: context); + loader = false; } @override @@ -52,129 +54,133 @@ class _OutGoingCallState extends State { if (chatcp.isCallEnded) { Navigator.pop(context); } - return Stack( - alignment: FractionalOffset.center, - children: [ - if (chatcp.isVideoCall) - Positioned.fill( - child: RTCVideoView( - chatcp.localVideoRenderer!, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, - ), - ), - 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, + return loader + ? const Center( + child: CircularProgressIndicator(), + ) + : Stack( + alignment: FractionalOffset.center, + children: [ + if (chatcp.isVideoCall) + Positioned.fill( + child: RTCVideoView( + chatcp.localVideoRenderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, ), ), - 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( + 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, + 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( + widget.outGoingCallData.receiverName.toString().replaceAll(".", " "), + style: const TextStyle( + fontSize: 21, + fontWeight: FontWeight.bold, + color: MyColors.white, + letterSpacing: -1.26, + height: 23 / 12, + ), + ), + const Text( + "Ringing...", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color( + 0xffC6C6C6, + ), + letterSpacing: -0.48, + height: 23 / 24, + ), + ), + const SizedBox( + height: 2, + ), + ], + ), + ), + ), + ], + ), + const Spacer(), + Container( margin: const EdgeInsets.only( - left: 10.0, - right: 10.0, + bottom: 70.0, + left: 49, + right: 49, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 70, - width: 70, - fit: BoxFit.cover, - ), - 10.height, - Text( - widget.outGoingCallData.receiverName.toString().replaceAll(".", " "), - style: const TextStyle( - fontSize: 21, - fontWeight: FontWeight.bold, - color: MyColors.white, - letterSpacing: -1.26, - height: 23 / 12, + RawMaterialButton( + onPressed: () { + chatcp.endCall(isUserOnline: chatProvider.isUserOnline).then((bool value) { + if (value) { + Navigator.of(context).pop(); + // print("Reintiiiiiiitttzzzz"); + // chatcp.initStreams(); + } + }); + }, + elevation: 2.0, + fillColor: MyColors.redA3Color, + padding: const EdgeInsets.all( + 15.0, ), - ), - const Text( - "Ringing...", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Color( - 0xffC6C6C6, - ), - letterSpacing: -0.48, - height: 23 / 24, + shape: const CircleBorder(), + child: const Icon( + Icons.call_end, + color: MyColors.white, + size: 35.0, ), ), - const SizedBox( - height: 2, - ), ], ), ), - ), - ], - ), - const Spacer(), - Container( - margin: const EdgeInsets.only( - bottom: 70.0, - left: 49, - right: 49, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - RawMaterialButton( - onPressed: () { - chatcp.endCall(isUserOnline: chatProvider.isUserOnline).then((bool value) { - if (value) { - Navigator.of(context).pop(); - // print("Reintiiiiiiitttzzzz"); - // chatcp.initStreams(); - } - }); - }, - elevation: 2.0, - fillColor: MyColors.redA3Color, - padding: const EdgeInsets.all( - 15.0, - ), - shape: const CircleBorder(), - child: const Icon( - Icons.call_end, - color: MyColors.white, - size: 35.0, - ), - ), ], ), ), - ], + ), ), ), - ), - ), - ), - ], - ); + ], + ); }), ); } diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 600ebc3..39878f0 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -32,7 +32,6 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.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_user_login_token_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/member_information_list_model.dart'; @@ -40,10 +39,7 @@ import 'package:mohem_flutter_app/models/member_login_list_model.dart'; import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; -import 'package:mohem_flutter_app/widgets/button/hmg_connectivity_button.dart'; import 'package:mohem_flutter_app/widgets/input_widget.dart'; - -// import 'package:safe_device/safe_device.dart'; import 'package:wifi_iot/wifi_iot.dart'; class LoginScreen extends StatefulWidget { @@ -77,7 +73,7 @@ class _LoginScreenState extends State with WidgetsBindingObserver { // late HmsApiAvailability hmsApiAvailability; - final voIPKit = FlutterIOSVoIPKit.instance; + final FlutterIOSVoIPKit voIPKit = FlutterIOSVoIPKit.instance; late Timer timeOutTimer; @override @@ -93,13 +89,15 @@ class _LoginScreenState extends State with WidgetsBindingObserver { callListeners(); checkAndNavigationCallingPage(); } - setupVoIPCallBacks(); + if (Platform.isIOS) { + setupVoIPCallBacks(); + } } // IOS Voip Call void setupVoIPCallBacks() { if (Platform.isIOS) { - voIPKit.getVoIPToken().then((value) async { + voIPKit.getVoIPToken().then((String? value) async { if (value != null) { AppState().setiosVoipPlayerID = await ChatApiClient().oneSignalVoip(value!); } @@ -114,8 +112,7 @@ class _LoginScreenState extends State with WidgetsBindingObserver { Map payload, ) async { _iosCallPayload = IosCallPayload.fromJson(payload); - isIncomingCall = true; - _timeOut(); + // _timeOut(); }; voIPKit.onDidRejectIncomingCall = ( @@ -130,17 +127,15 @@ class _LoginScreenState extends State with WidgetsBindingObserver { String uuid, String callerId, ) async { - var callerID = "did accept call $callerId"; - timeOutTimer.cancel(); - await connectCall(); - await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); - await voIPKit.callConnected(); + await connectCall(uuid: uuid, callDetails: callerId); + voIPKit.acceptIncomingCall(callerState: CallStateType.calling); + voIPKit.callConnected(); }; } void _timeOut() async { timeOutTimer = Timer(const Duration(seconds: 25), () async { - var incomingCallerName = await voIPKit.getIncomingCallerName(); + String? incomingCallerName = await voIPKit.getIncomingCallerName(); voIPKit.unansweredIncomingCall( skipLocalNotification: false, missedCallTitle: '📞 Missed call', @@ -151,17 +146,31 @@ class _LoginScreenState extends State with WidgetsBindingObserver { }); } - Future connectCall() async { - try { + Future connectCall({required String uuid, required String callDetails}) async { + isIncomingCall = true; + if (AppState().getisUserOnline) { + _iosCallPayload = IosCallPayload( + uuid: uuid, incomingCallerId: callDetails.split("-")[0], incomingCallReciverId: callDetails.split("-")[1], incomingCallerName: _iosCallPayload!.incomingCallerName, incomingCallType: callDetails.split("-").last); + + } else { + _iosCallPayload = IosCallPayload( + uuid: uuid, incomingCallerId: callDetails.split("-")[0], incomingCallReciverId: callDetails.split("-")[1], incomingCallerName: null, incomingCallType: callDetails.split("-").last); + } + if (_iosCallPayload!.incomingCallerName == null) { + if (Platform.isIOS) { + Utils.hideLoading(context); + } + await Utils.saveStringFromPrefs("iosCallPayload", jsonEncode(_iosCallPayload)); + MaterialPageRoute pageRoute = await MaterialPageRoute(builder: (BuildContext context) => StartCallPage()); + Navigator.push(context, pageRoute); + } else if (AppState().getisUserOnline) { + await Utils.saveStringFromPrefs("iosCallPayload", jsonEncode(_iosCallPayload)); 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); + MaterialPageRoute pageRoute = await MaterialPageRoute(builder: (BuildContext context) => StartCallPage()); + Navigator.push(context, pageRoute); + } else { + FlutterCallkitIncoming.endAllCalls(); + Utils.showToast("Something wen't wrong"); } } @@ -180,21 +189,9 @@ class _LoginScreenState extends State with WidgetsBindingObserver { // print(error); // } // } - // 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)); - // } - // } catch (e) { - // logger.d(e); - // } - // } Future callListeners() async { try { - print("Call Listeners Init"); FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async { switch (event!.event) { case Event.actionCallIncoming: @@ -238,7 +235,7 @@ class _LoginScreenState extends State with WidgetsBindingObserver { if (Platform.isAndroid) { Utils.hideLoading(context); } - var pageRoute = MaterialPageRoute(builder: (context) => StartCallPage()); + MaterialPageRoute pageRoute = MaterialPageRoute(builder: (BuildContext context) => StartCallPage()); Navigator.push(context, pageRoute).whenComplete(() { checkFirebaseToken(); }); @@ -254,7 +251,6 @@ class _LoginScreenState extends State with WidgetsBindingObserver { var calls = await FlutterCallkitIncoming.activeCalls(); if (calls is List) { if (calls.isNotEmpty) { - print('DATA: $calls'); return calls[0]; } else { return null; @@ -267,7 +263,7 @@ class _LoginScreenState extends State with WidgetsBindingObserver { if (currentCall != null && Platform.isAndroid) { isIncomingCall = true; Utils.hideLoading(context); - Navigator.push(context, MaterialPageRoute(builder: (context) => StartCallPage())); + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => StartCallPage())); } } @@ -390,7 +386,7 @@ class _LoginScreenState extends State with WidgetsBindingObserver { // if (isAppOpenBySystem!) checkFirebaseToken(); Utils.showLoading(context); - Future.delayed(const Duration(seconds: 2)).whenComplete(() { + Future.delayed(Duration(seconds: Platform.isIOS ? 3 : 2)).whenComplete(() { if (!isIncomingCall) { if (isAppOpenBySystem!) checkFirebaseToken(); }