diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f187ebd..5b3b2ba 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,7 +7,15 @@ - + + + + + + + + + main() async { ), ChangeNotifierProvider( create: (_) => MarathonProvider(), - ) + ), + // ChangeNotifierProvider( + // create: (_) => ChatCallProvider(), + // ), ], child: const MyApp(), ), @@ -261,4 +264,3 @@ class MyApp extends StatelessWidget { // }); // } // } - diff --git a/lib/models/chat/call.dart b/lib/models/chat/call.dart index 7f8f6eb..ce58ae3 100644 --- a/lib/models/chat/call.dart +++ b/lib/models/chat/call.dart @@ -7,127 +7,191 @@ import 'dart:convert'; class CallDataModel { CallDataModel({ this.callerId, - this.callReceiverID, - this.notificationForeground, - this.message, + this.callerDetails, + this.receiverId, + this.receiverDetails, this.title, - this.type, - this.identity, - this.name, - this.isCall, - this.isWebrtc, - this.contant, - this.contantNo, - this.chatEventId, - this.fileTypeId, - this.currentUserId, - this.chatSource, - this.userChatHistoryLineRequestList, - this.server, + this.calltype, }); String? callerId; - String? callReceiverID; - String? notificationForeground; - String? message; - String? title; - String? type; - String? identity; - String? name; - String? isCall; - String? isWebrtc; - String? contant; - String? contantNo; - String? chatEventId; - dynamic? fileTypeId; - String? currentUserId; - String? chatSource; - List? userChatHistoryLineRequestList; - String? server; + CallerDetails? callerDetails; + String? receiverId; + ReceiverDetails? receiverDetails; + dynamic title; + String? calltype; factory CallDataModel.fromRawJson(String str) => CallDataModel.fromJson(json.decode(str)); String toRawJson() => json.encode(toJson()); factory CallDataModel.fromJson(Map json) => CallDataModel( - callerId: json["callerID"] == null ? null : json["callerID"], - callReceiverID: json["callReceiverID"] == null ? null : json["callReceiverID"], - notificationForeground: json["notification_foreground"] == null ? null : json["notification_foreground"], - message: json["message"] == null ? null : json["message"], - title: json["title"] == null ? null : json["title"], - type: json["type"] == null ? null : json["type"], - identity: json["identity"] == null ? null : json["identity"], - name: json["name"] == null ? null : json["name"], - isCall: json["is_call"] == null ? null : json["is_call"], - isWebrtc: json["is_webrtc"] == null ? null : json["is_webrtc"], - contant: json["contant"] == null ? null : json["contant"], - contantNo: json["contantNo"] == null ? null : json["contantNo"], - chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], - fileTypeId: json["fileTypeId"], - currentUserId: json["currentUserId"] == null ? null : json["currentUserId"], - chatSource: json["chatSource"] == null ? null : json["chatSource"], - userChatHistoryLineRequestList: json["userChatHistoryLineRequestList"] == null - ? null - : List.from( - json["userChatHistoryLineRequestList"].map( - (x) => UserChatHistoryLineRequestList.fromJson(x), - ), - ), - server: json["server"] == null ? null : json["server"], - ); + callerId: json["callerID"], + callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]), + receiverId: json["receiverID"], + receiverDetails: json["receiverDetails"] == null ? null : ReceiverDetails.fromJson(json["receiverDetails"]), + title: json["title"], + calltype: json["calltype"], + ); + + Map toJson() => { + "callerID": callerId, + "callerDetails": callerDetails?.toJson(), + "receiverID": receiverId, + "receiverDetails": receiverDetails?.toJson(), + "title": title, + "calltype": calltype, + }; +} + +class CallerDetails { + CallerDetails({ + this.response, + this.errorResponses, + }); + + Response? response; + dynamic errorResponses; + + factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory CallerDetails.fromJson(Map json) => CallerDetails( + response: json["response"] == null ? null : Response.fromJson(json["response"]), + errorResponses: json["errorResponses"], + ); Map toJson() => { - "callerID": callerId == null ? null : callerId, - "callReceiverID": callReceiverID == null ? null : callReceiverID, - "notification_foreground": notificationForeground == null ? null : notificationForeground, - "message": message == null ? null : message, - "title": title == null ? null : title, - "type": type == null ? null : type, - "identity": identity == null ? null : identity, - "name": name == null ? null : name, - "is_call": isCall == null ? null : isCall, - "is_webrtc": isWebrtc == null ? null : isWebrtc, - "contant": contant == null ? null : contant, - "contantNo": contantNo == null ? null : contantNo, - "chatEventId": chatEventId == null ? null : chatEventId, - "fileTypeId": fileTypeId, - "currentUserId": currentUserId == null ? null : currentUserId, - "chatSource": chatSource == null ? null : chatSource, - "userChatHistoryLineRequestList": userChatHistoryLineRequestList == null - ? null - : List.from( - userChatHistoryLineRequestList!.map( - (x) => x.toJson(), - ), - ), - "server": server == null ? null : server, - }; + "response": response?.toJson(), + "errorResponses": errorResponses, + }; } -class UserChatHistoryLineRequestList { - UserChatHistoryLineRequestList({ - this.isSeen, - this.isDelivered, - this.targetUserId, - this.targetUserStatus, +class Response { + Response({ + this.id, + this.userName, + this.email, + this.phone, + this.title, + this.token, + this.isDomainUser, + this.isActiveCode, + this.encryptedUserId, + this.encryptedUserName, }); - bool? isSeen; - bool? isDelivered; - int? targetUserId; - int? targetUserStatus; + int? id; + String? userName; + String? email; + dynamic phone; + String? title; + String? token; + bool? isDomainUser; + bool? isActiveCode; + String? encryptedUserId; + String? encryptedUserName; + + factory Response.fromRawJson(String str) => Response.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory Response.fromJson(Map json) => Response( + id: json["id"], + userName: json["userName"], + email: json["email"], + phone: json["phone"], + title: json["title"], + token: json["token"], + isDomainUser: json["isDomainUser"], + isActiveCode: json["isActiveCode"], + encryptedUserId: json["encryptedUserId"], + encryptedUserName: json["encryptedUserName"], + ); + + Map toJson() => { + "id": id, + "userName": userName, + "email": email, + "phone": phone, + "title": title, + "token": token, + "isDomainUser": isDomainUser, + "isActiveCode": isActiveCode, + "encryptedUserId": encryptedUserId, + "encryptedUserName": encryptedUserName, + }; +} + +class ReceiverDetails { + ReceiverDetails({ + this.id, + this.userName, + this.email, + this.phone, + this.title, + this.userStatus, + this.image, + this.unreadMessageCount, + this.userAction, + this.isPin, + this.isFav, + this.isAdmin, + this.rKey, + this.totalCount, + }); + + int? id; + String? userName; + String? email; + dynamic phone; + dynamic title; + int? userStatus; + String? image; + int? unreadMessageCount; + dynamic userAction; + bool? isPin; + bool? isFav; + bool? isAdmin; + String? rKey; + int? totalCount; + + factory ReceiverDetails.fromRawJson(String str) => ReceiverDetails.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); - factory UserChatHistoryLineRequestList.fromJson(Map json) => UserChatHistoryLineRequestList( - isSeen: json["isSeen"] == null ? null : json["isSeen"], - isDelivered: json["isDelivered"] == null ? null : json["isDelivered"], - targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], - targetUserStatus: json["targetUserStatus"] == null ? null : json["targetUserStatus"], - ); + factory ReceiverDetails.fromJson(Map json) => ReceiverDetails( + id: json["id"], + userName: json["userName"], + email: json["email"], + phone: json["phone"], + title: json["title"], + userStatus: json["userStatus"], + image: json["image"], + unreadMessageCount: json["unreadMessageCount"], + userAction: json["userAction"], + isPin: json["isPin"], + isFav: json["isFav"], + isAdmin: json["isAdmin"], + rKey: json["rKey"], + totalCount: json["totalCount"], + ); Map toJson() => { - "isSeen": isSeen == null ? null : isSeen, - "isDelivered": isDelivered == null ? null : isDelivered, - "targetUserId": targetUserId == null ? null : targetUserId, - "targetUserStatus": targetUserStatus == null ? null : targetUserStatus, - }; + "id": id, + "userName": userName, + "email": email, + "phone": phone, + "title": title, + "userStatus": userStatus, + "image": image, + "unreadMessageCount": unreadMessageCount, + "userAction": userAction, + "isPin": isPin, + "isFav": isFav, + "isAdmin": isAdmin, + "rKey": rKey, + "totalCount": totalCount, + }; } diff --git a/lib/provider/chat_call_provider.dart b/lib/provider/chat_call_provider.dart new file mode 100644 index 0000000..45205df --- /dev/null +++ b/lib/provider/chat_call_provider.dart @@ -0,0 +1,187 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; + +class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { + ///////////////////// Web RTC Video Calling ////////////////////// + // Video Call + late RTCPeerConnection _peerConnection; + RTCVideoRenderer _localVideoRenderer = RTCVideoRenderer(); + final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer(); + + MediaStream? _localStream; + MediaStream? _remoteStream; + + void initCallListeners() { + chatHubConnection.on("OnCallAcceptedAsync", onCallAcceptedAsync); + chatHubConnection.on("OnIceCandidateAsync", onIceCandidateAsync); + chatHubConnection.on("OnOfferAsync", onOfferAsync); + chatHubConnection.on("OnAnswerOffer", onAnswerOffer); + chatHubConnection.on("OnHangUpAsync", onHangUpAsync); + chatHubConnection.on("OnCallDeclinedAsync", onCallDeclinedAsync); + } + + //Video Constraints + var videoConstraints = { + "video": { + "mandatory": { + "width": {"min": 320}, + "height": {"min": 180} + }, + "optional": [ + { + "width": {"max": 1280} + }, + {"frameRate": 25}, + {"facingMode": "user"} + ] + }, + "frameRate": 25, + "width": 420, //420,//640,//1280, + "height": 240 //240//480//720 + }; + + // Audio Constraints + var audioConstraints = { + "sampleRate": 8000, + "sampleSize": 16, + "channelCount": 2, + "echoCancellation": true, + "audio": true, + }; + + Future _createPeerConnection() async { + // {"url": "stun:stun.l.google.com:19302"}, + Map configuration = { + "iceServers": [ + {"urls": 'stun:15.185.116.59:3478'}, + {"urls": "turn:15.185.116.59:3479", "username": "admin", "credential": "admin"} + ] + }; + + Map offerSdpConstraints = { + "mandatory": { + "OfferToReceiveAudio": true, + "OfferToReceiveVideo": true, + }, + "optional": [], + }; + + RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints); + // if (pc != null) print(pc); + //pc.addStream(widget.localStream); + + pc.onIceCandidate = (e) { + if (e.candidate != null) { + print(json.encode({ + 'candidate': e.candidate.toString(), + 'sdpMid': e.sdpMid.toString(), + 'sdpMlineIndex': e.sdpMLineIndex, + })); + } + }; + pc.onIceConnectionState = (e) { + print(e); + }; + pc.onAddStream = (stream) { + print('addStream: ' + stream.id); + _remoteRenderer.srcObject = stream; + }; + return pc; + } + + void init() { + initRenderers(); + _createPeerConnection().then((pc) { + _peerConnection = pc; + // _setRemoteDescription(widget.info); + }); + } + + void initRenderers() { + _localVideoRenderer.initialize(); + _remoteRenderer.initialize(); + initLocalCamera(); + } + + void initLocalCamera() async { + _localStream = await navigator.mediaDevices.getUserMedia({'video': true, 'audio': true}); + _localVideoRenderer.srcObject = _localStream; + // _localVideoRenderer.srcObject = await navigator.mediaDevices + // .getUserMedia({'video': true, 'audio': true}); + print('this source Object'); + print('this suarce ${_localVideoRenderer.srcObject != null}'); + notifyListeners(); + } + + void startCall({required String callType}) {} + + void endCall() {} + + void checkCall(Map message) { + switch (message["callStatus"]) { + case 'connected': + {} + break; + case 'offer': + {} + break; + case 'accept': + {} + break; + case 'candidate': + {} + break; + case 'bye': + {} + break; + case 'leave': + {} + break; + } + } + + //// Listeners Methods //// + + void onCallAcceptedAsync(List? params) {} + + void onIceCandidateAsync(List? params) {} + + void onOfferAsync(List? params) {} + + void onAnswerOffer(List? params) {} + + void onHangUpAsync(List? params) {} + + void onCallDeclinedAsync(List? params) {} + + //// Invoke Methods + + Future invoke({required String invokeMethod, required String currentUserID, required String targetUserID, bool isVideoCall = false, var data}) async { + List args = []; + if (invokeMethod == "answerCallAsync") { + args = [currentUserID, targetUserID]; + } else if (invokeMethod == "CallUserAsync") { + args = [currentUserID, targetUserID, isVideoCall]; + } else if (invokeMethod == "IceCandidateAsync") { + args = [targetUserID, data]; + } else if (invokeMethod == "OfferAsync") { + args = [targetUserID, data]; + } else if (invokeMethod == "AnswerOfferAsync") { + args = [targetUserID, data]; + //json In Data + } + await chatHubConnection.invoke(invokeMethod, args: args); + } + + void stopListeners() async { + chatHubConnection.off('OnCallDeclinedAsync'); + chatHubConnection.off('OnCallAcceptedAsync'); + chatHubConnection.off('OnIceCandidateAsync'); + chatHubConnection.off('OnAnswerOffer'); + } +} diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index 3e7bc4b..d374114 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -69,6 +69,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List teamMembersList = []; Material.TextDirection textDirection = Material.TextDirection.ltr; + bool isRTL = false; + String msgText = ""; //Chat Home Page Counter int chatUConvCounter = 0; @@ -77,6 +79,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List? chatUsersList = []; int pageNo = 1; + bool disbaleChatForThisUser = false; + Future getUserAutoLoginToken() async { userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); if (userLoginResponse.response != null) { @@ -86,6 +90,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Utils.showToast( userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr", ); + disbaleChatForThisUser = true; + notifyListeners(); } } @@ -117,6 +123,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // chatHubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); + if (kDebugMode) { logger.i("All listeners registered"); } @@ -1007,23 +1014,25 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } void disposeData() { - search.clear(); - isChatScreenActive = false; - receiverID = 0; - paginationVal = 0; - message.text = ''; - isTextMsg = false; - isAttachmentMsg = false; - isVoiceMsg = false; - isReplyMsg = false; - repliedMsg = []; - sFileType = ""; - deleteData(); - favUsersList.clear(); - searchedChats?.clear(); - pChatHistory?.clear(); - chatHubConnection.stop(); - AppState().chatDetails = null; + if (!disbaleChatForThisUser) { + search.clear(); + isChatScreenActive = false; + receiverID = 0; + paginationVal = 0; + message.text = ''; + isTextMsg = false; + isAttachmentMsg = false; + isVoiceMsg = false; + isReplyMsg = false; + repliedMsg = []; + sFileType = ""; + deleteData(); + favUsersList.clear(); + searchedChats?.clear(); + pChatHistory?.clear(); + chatHubConnection.stop(); + AppState().chatDetails = null; + } } void deleteData() { @@ -1407,17 +1416,16 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { void inputBoxDirection(String val) { if (val.isNotEmpty) { isTextMsg = true; - RegExp exp = RegExp("[a-zA-Z]"); - if (exp.hasMatch(val.substring(val.length - 1)) && val.substring(val.length - 1) != " ") { - textDirection = Material.TextDirection.ltr; - notifyListeners(); - } else if (val.substring(val.length - 1) != " " && !exp.hasMatch(val.substring(val.length - 1))) { - textDirection = Material.TextDirection.rtl; - notifyListeners(); - } } else { isTextMsg = false; } + msgText = val; + notifyListeners(); + } + + void onDirectionChange(bool val) { + isRTL = val; + notifyListeners(); } Material.TextDirection getTextDirection(String v) { @@ -1451,18 +1459,19 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } void openChatByNoti(BuildContext context) async { + SingleUserChatModel nUser = SingleUserChatModel(); Utils.saveStringFromPrefs("isAppOpendByChat", "false"); - SingleUserChatModel nUser = SingleUserChatModel.fromJson(jsonDecode(await Utils.getStringFromPrefs("notificationData"))); - Utils.saveStringFromPrefs("notificationData", "null"); - logger.w(jsonEncode(nUser)); - Future.delayed(const Duration(seconds: 2)); - for (ChatUser user in searchedChats!) { - if (user.id == nUser.targetUserId) { - Navigator.pushNamed(context, AppRoutes.chatDetailed, arguments: ChatDetailedScreenParams(user, false)); - return; - } else { - openChatByNoti(context); + if (await Utils.getStringFromPrefs("notificationData") != "null") { + nUser = SingleUserChatModel.fromJson(jsonDecode(await Utils.getStringFromPrefs("notificationData"))); + Utils.saveStringFromPrefs("notificationData", "null"); + Future.delayed(const Duration(seconds: 2)); + for (ChatUser user in searchedChats!) { + if (user.id == nUser.targetUserId) { + Navigator.pushNamed(context, AppRoutes.chatDetailed, arguments: ChatDetailedScreenParams(user, false)); + return; + } } } + Utils.saveStringFromPrefs("notificationData", "null"); } } diff --git a/lib/ui/chat/call/chat_outgoing_call_screen.dart b/lib/ui/chat/call/chat_outgoing_call_screen.dart index 627c24a..2356e10 100644 --- a/lib/ui/chat/call/chat_outgoing_call_screen.dart +++ b/lib/ui/chat/call/chat_outgoing_call_screen.dart @@ -10,12 +10,14 @@ 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/provider/chat_call_provider.dart'; +import 'package:provider/provider.dart'; class OutGoingCall extends StatefulWidget { - CallDataModel OutGoingCallData; - bool? isVideoCall; + CallDataModel outGoingCallData; + bool isVideoCall; - OutGoingCall({Key? key, required this.OutGoingCallData, this.isVideoCall}) : super(key: key); + OutGoingCall({Key? key, required this.outGoingCallData, required this.isVideoCall}) : super(key: key); @override _OutGoingCallState createState() => _OutGoingCallState(); @@ -23,23 +25,25 @@ class OutGoingCall extends StatefulWidget { class _OutGoingCallState extends State with SingleTickerProviderStateMixin { AnimationController? _animationController; - CameraController? _controller; + late CameraController controller; + late List _cameras; Future? _initializeControllerFuture; bool isCameraReady = false; bool isMicOff = false; bool isLoudSpeaker = false; bool isCamOff = false; + late ChatCallProvider callProviderd; @override void initState() { + callProviderd = Provider.of(context, listen: false); _animationController = AnimationController( vsync: this, duration: const Duration( milliseconds: 500, ), ); - logger.d(jsonEncode(widget.OutGoingCallData)); - //_runAnimation(); + // _runAnimation(); // connectSignaling(); WidgetsBinding.instance.addPostFrameCallback( (_) => _runAnimation(), @@ -58,13 +62,10 @@ class _OutGoingCallState extends State with SingleTickerProviderSt return Stack( alignment: FractionalOffset.center, children: [ - if (widget.isVideoCall!) + if (widget.isVideoCall) Positioned.fill( - child: AspectRatio( - aspectRatio: _controller!.value.aspectRatio, - child: CameraPreview( - _controller!, - ), + child: CameraPreview( + controller, ), ), Positioned.fill( @@ -74,7 +75,7 @@ class _OutGoingCallState extends State with SingleTickerProviderSt child: Container( decoration: BoxDecoration( color: MyColors.grey57Color.withOpacity( - 0.7, + 0.3, ), ), child: Column( @@ -105,9 +106,9 @@ class _OutGoingCallState extends State with SingleTickerProviderSt fit: BoxFit.cover, ), 10.height, - const Text( - "Aamir Saleem Ahmad", - style: TextStyle( + Text( + widget.outGoingCallData.title, + style: const TextStyle( fontSize: 21, fontWeight: FontWeight.bold, color: MyColors.white, @@ -179,7 +180,7 @@ class _OutGoingCallState extends State with SingleTickerProviderSt mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (widget.isVideoCall!) + if (widget.isVideoCall) RawMaterialButton( onPressed: () { _camOff(); @@ -267,13 +268,10 @@ class _OutGoingCallState extends State with SingleTickerProviderSt } void _runAnimation() async { - List cameras = await availableCameras(); - CameraDescription firstCamera = cameras[1]; - _controller = CameraController( - firstCamera, - ResolutionPreset.medium, - ); - _initializeControllerFuture = _controller!.initialize(); + _cameras = await availableCameras(); + CameraDescription firstCamera = _cameras[1]; + controller = CameraController(firstCamera, ResolutionPreset.medium); + _initializeControllerFuture = controller.initialize(); setState(() {}); // setAudioFile(); for (int i = 0; i < 100; i++) { @@ -304,7 +302,7 @@ class _OutGoingCallState extends State with SingleTickerProviderSt try { // backToHome(); // final roomModel = RoomModel(name: widget.OutGoingCallData.name, token: widget.OutGoingCallData.sessionId, identity: widget.OutGoingCallData.identity); - await _controller?.dispose(); + await controller?.dispose(); // changeCallStatusAPI(4); diff --git a/lib/ui/chat/chat_detailed_screen.dart b/lib/ui/chat/chat_detailed_screen.dart index a72e12b..421eb91 100644 --- a/lib/ui/chat/chat_detailed_screen.dart +++ b/lib/ui/chat/chat_detailed_screen.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -13,7 +14,10 @@ import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/call.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.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/provider/chat_call_provider.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; +import 'package:mohem_flutter_app/ui/chat/custom_auto_direction.dart'; import 'package:mohem_flutter_app/ui/chat/call/chat_outgoing_call_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart'; import 'package:mohem_flutter_app/ui/chat/common.dart'; @@ -41,8 +45,10 @@ class ChatDetailScreen extends StatefulWidget { class _ChatDetailScreenState extends State { final RefreshController _rc = RefreshController(initialRefresh: false); late ChatProviderModel data; + late ChatCallProvider callPro; ChatDetailedScreenParams? params; - var textDirection = TextDirection.RTL; + + // var textDirection = TextDirection.RTL; void getMoreChat() async { if (params != null) { @@ -72,6 +78,7 @@ class _ChatDetailScreenState extends State { Widget build(BuildContext context) { params = ModalRoute.of(context)!.settings.arguments as ChatDetailedScreenParams; data = Provider.of(context, listen: false); + // callPro = Provider.of(context, listen: false); if (params != null) { data.getSingleUserChatHistory( senderUID: AppState().chatDetails!.response!.id!.toInt(), @@ -92,11 +99,11 @@ class _ChatDetailScreenState extends State { chatUser: params!.chatUser, actions: [ // SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() { - // // makeCall(callType: "AUDIO", con: hubConnection); + // makeCall(callType: "AUDIO"); // }), // 24.width, // SvgPicture.asset("assets/icons/chat/video_call.svg", width: 21, height: 18).onPress(() { - // // makeCall(callType: "VIDEO", con: hubConnection); + // makeCall(callType: "VIDEO"); // }), // 21.width, ], @@ -252,37 +259,39 @@ class _ChatDetailScreenState extends State { if (!m.isRecoding) Row( children: [ - TextField( - textDirection: m.textDirection, - controller: m.message, - decoration: InputDecoration( - hintTextDirection: m.textDirection, - hintText: m.isAttachmentMsg - ? m.selectedFile.path.split("/").last - : m.textDirection.name == "rtl" ? "اكتب هنا للرد" :LocaleKeys.typeheretoreply.tr(), - hintStyle: TextStyle(color: m.isAttachmentMsg ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14), - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - filled: true, - fillColor: MyColors.white, - contentPadding: const EdgeInsets.only( - left: 21, - top: 20, - bottom: 20, + CustomAutoDirection( + onDirectionChange: (bool isRTL) => m.onDirectionChange(isRTL), + text: m.msgText, + child: TextField( + // textDirection: m.textDirection, + controller: m.message, + decoration: InputDecoration( + hintTextDirection: m.textDirection, + hintText: m.isAttachmentMsg ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(), + hintStyle: TextStyle(color: m.isAttachmentMsg ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14), + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + filled: true, + fillColor: MyColors.white, + contentPadding: const EdgeInsets.only( + left: 21, + top: 20, + bottom: 20, + ), + prefixIconConstraints: const BoxConstraints(), + prefixIcon: m.sFileType.isNotEmpty + ? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15) + : null, ), - prefixIconConstraints: const BoxConstraints(), - prefixIcon: m.sFileType.isNotEmpty - ? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15) - : null, - ), - onChanged: (String val) { - m.inputBoxDirection(val); - m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!); - }, - ).expanded, + onChanged: (String val) { + m.inputBoxDirection(val); + m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!); + }, + ).expanded, + ), if (m.sFileType.isNotEmpty) Row( children: [ @@ -342,45 +351,30 @@ class _ChatDetailScreenState extends State { } } - void makeCall({required String callType, required HubConnection con}) async { + void makeCall({required String callType}) async { + callPro.initCallListeners(); print("================== Make call Triggered ============================"); Map json = { "callerID": AppState().chatDetails!.response!.id!.toString(), - "callReceiverID": params!.chatUser!.id.toString(), - "notification_foreground": "true", - "message": "Aamir is calling", - "title": "Video Call", - "type": callType == "VIDEO" ? "Video" : "Audio", - "identity": AppState().chatDetails!.response!.userName, - "name": AppState().chatDetails!.response!.title, - "is_call": "true", - "is_webrtc": "true", - "contant": "Start video Call ${AppState().chatDetails!.response!.userName}", - "contantNo": "775d1f11-62d9-6fcc-91f6-21f8c14559fb", - "chatEventId": "3", - "fileTypeId": null, - "currentUserId": AppState().chatDetails!.response!.id!.toString(), - "chatSource": "1", - "userChatHistoryLineRequestList": [ - { - "isSeen": false, - "isDelivered": false, - "targetUserId": params!.chatUser!.id!, - "targetUserStatus": 4, - } - ], - // "server": "https://192.168.8.163:8086", - "server": "https://livecareturn.hmg.com:8086", + "callerDetails": AppState().chatDetails!.toJson(), + "receiverID": params!.chatUser!.id.toString(), + "receiverDetails": params!.chatUser!.toJson(), + "title": params!.chatUser!.userName!.replaceAll(".", " "), + "calltype": callType == "VIDEO" ? "Video" : "Audio", }; + logger.w(json); CallDataModel callData = CallDataModel.fromJson(json); await Navigator.push( context, MaterialPageRoute( builder: (BuildContext context) => OutGoingCall( isVideoCall: callType == "VIDEO" ? true : false, - OutGoingCallData: callData, + outGoingCallData: callData, ), ), - ); + ).then((value) { + print("then"); + callPro.stopListeners(); + }); } } diff --git a/lib/ui/chat/chat_home.dart b/lib/ui/chat/chat_home.dart index 2e23254..9c5f216 100644 --- a/lib/ui/chat/chat_home.dart +++ b/lib/ui/chat/chat_home.dart @@ -52,11 +52,11 @@ class _ChatHomeState extends State { if (data.searchedChats == null || data.searchedChats!.isEmpty) { data.isLoading = true; data.getUserRecentChats().whenComplete(() async { - String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); - String notificationData = await Utils.getStringFromPrefs("notificationData"); - if (isAppOpendByChat != "null" || isAppOpendByChat == "true" && notificationData != "null") { - data.openChatByNoti(context); - } + // String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); + // String notificationData = await Utils.getStringFromPrefs("notificationData"); + // if (isAppOpendByChat != "null" || isAppOpendByChat == "true" && notificationData != "null") { + // data.openChatByNoti(context); + // } }); } } diff --git a/lib/ui/chat/custom_auto_direction.dart b/lib/ui/chat/custom_auto_direction.dart new file mode 100644 index 0000000..7e40644 --- /dev/null +++ b/lib/ui/chat/custom_auto_direction.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart' as intl; + +class CustomAutoDirection extends StatefulWidget { + final String text; + final Widget child; + final void Function(bool isRTL)? onDirectionChange; + + const CustomAutoDirection({Key? key, required this.text, required this.child, this.onDirectionChange}) : super(key: key); + + @override + _CustomAutoDirectionState createState() => _CustomAutoDirectionState(); +} + +class _CustomAutoDirectionState extends State { + late String text; + late Widget childWidget; + + @override + Widget build(BuildContext context) { + text = widget.text; + childWidget = widget.child; + return Directionality(textDirection: isRTL(text) ? TextDirection.rtl : TextDirection.ltr, child: childWidget); + } + + @override + void didUpdateWidget(CustomAutoDirection oldWidget) { + if (isRTL(oldWidget.text) != isRTL(widget.text)) { + WidgetsBinding.instance.addPostFrameCallback((_) => widget.onDirectionChange?.call(isRTL(widget.text))); + } + super.didUpdateWidget(oldWidget); + } + + bool isRTL(String text) { + if (text.isEmpty) return Directionality.of(context) == TextDirection.rtl; + return intl.Bidi.detectRtlDirectionality(text); + } +} diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index bb361af..7a8f857 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -86,24 +86,28 @@ class _DashboardScreenState extends State with WidgetsBindingOb void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); - chatHubConnection.stop(); + if (!cProvider.disbaleChatForThisUser) { + chatHubConnection.stop(); + } } void _bHubCon() { cProvider.getUserAutoLoginToken().whenComplete(() async { - String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); - if (isAppOpendByChat != null && isAppOpendByChat == "true") { - Utils.showLoading(context); - cProvider.buildHubConnection(); - Future.delayed(const Duration(seconds: 2), () async { - cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); - gotoChat(context); - }); - } else { - cProvider.buildHubConnection(); - Future.delayed(const Duration(seconds: 2), () { - cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); - }); + if (!cProvider.disbaleChatForThisUser) { + String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); + if (isAppOpendByChat != null && isAppOpendByChat == "true") { + Utils.showLoading(context); + cProvider.buildHubConnection(); + Future.delayed(const Duration(seconds: 2), () async { + cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); + gotoChat(context); + }); + } else { + cProvider.buildHubConnection(); + Future.delayed(const Duration(seconds: 2), () { + cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); + }); + } } }); } @@ -139,7 +143,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb data.fetchMenuEntries(); data.getCategoryOffersListAPI(context); marathonProvider.getMarathonDetailsFromApi(); - if (!isFromInit) checkHubCon(); + if (!cProvider.disbaleChatForThisUser && !isFromInit) checkHubCon(); _refreshController.refreshCompleted(); } @@ -555,7 +559,11 @@ class _DashboardScreenState extends State with WidgetsBindingOb children: [ SvgPicture.asset( "assets/icons/chat/chat.svg", - color: currentIndex == 4 ? MyColors.grey3AColor : MyColors.grey98Color, + color: currentIndex == 4 + ? MyColors.grey3AColor + : cProvider.disbaleChatForThisUser + ? MyColors.lightGreyE3Color + : MyColors.grey98Color, ).paddingAll(4), Consumer( builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { @@ -565,7 +573,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb child: Container( padding: const EdgeInsets.only(left: 4, right: 4), alignment: Alignment.center, - decoration: BoxDecoration(color: MyColors.redColor, borderRadius: BorderRadius.circular(17)), + decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)), child: data.chatUConvCounter.toString().toText10(color: Colors.white), ), ); @@ -592,7 +600,9 @@ class _DashboardScreenState extends State with WidgetsBindingOb } else if (index == 3) { Navigator.pushNamed(context, AppRoutes.itemsForSale); } else if (index == 4) { - Navigator.pushNamed(context, AppRoutes.chat); + if (!cProvider.disbaleChatForThisUser) { + Navigator.pushNamed(context, AppRoutes.chat); + } } }, ), diff --git a/pubspec.yaml b/pubspec.yaml index 13ca645..20328fe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -91,7 +91,7 @@ dependencies: logging: ^1.0.1 swipe_to: ^1.0.2 flutter_webrtc: ^0.9.16 - camera: ^0.10.0+4 + camera: ^0.10.3 flutter_local_notifications: any #firebase_analytics: any