import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'package:connectivity/connectivity.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_tts/flutter_tts.dart'; import 'package:intl/intl.dart'; import 'package:just_audio/just_audio.dart'; import 'package:queuing_system/core/api.dart'; import 'package:queuing_system/core/response_models/call_config_model.dart'; import 'package:queuing_system/core/response_models/patient_ticket_model.dart'; import 'package:queuing_system/core/response_models/prayers_widget_model.dart'; import 'package:queuing_system/core/response_models/rss_feed_model.dart'; import 'package:queuing_system/core/response_models/weathers_widget_model.dart'; import 'package:queuing_system/core/response_models/widgets_config_model.dart'; import 'package:queuing_system/utils/call_by_voice.dart'; import 'package:queuing_system/utils/call_type.dart'; import 'package:queuing_system/utils/signalR_utils.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:signalr_core/signalr_core.dart'; bool isVoiceActualCompletedGlobally = false; class AppProvider extends ChangeNotifier { AppProvider() { callInitializations(); } Future callInitializations() async { await startSignalHubConnection(); await getInfoWidgetsDetailsFromServer(); await getLastTimeUpdatedFromCache(); listenNetworkConnectivity(); listenAudioPlayerEvents(); getTheWidgetsConfigurationsEveryMidnight(); // await callPatientsAPI(); } SignalRHelper signalRHelper = SignalRHelper(); final AudioPlayer audioPlayer = AudioPlayer(); FlutterTts flutterTts = FlutterTts(); CallConfig patientCallConfigurations = CallConfig(); List patientTickets = []; List isQueuePatients = []; String currentDeviceIp = ""; bool isCallingInProgress = false; bool isInternetConnectionAvailable = true; bool isApiCallNeeded = false; updateInternetConnection(bool value) { isInternetConnectionAvailable = value; notifyListeners(); } ScreenOrientationEnum currentScreenRotation = ScreenOrientationEnum.portraitUp; updateCurrentScreenRotation(ScreenOrientationEnum value) { currentScreenRotation = value; notifyListeners(); } Future getCurrentIP() async { final ips = await NetworkInterface.list(type: InternetAddressType.IPv4); for (var interface in ips) { if (interface.name == "eth0") { for (var address in interface.addresses) { currentDeviceIp = address.address; notifyListeners(); } } if (interface.name == "wlan0") { for (var address in interface.addresses) { currentDeviceIp = address.address; notifyListeners(); } } } } WidgetsConfigModel? currentWidgetsConfigModel; Future getInfoWidgetsConfigurationsFromServer() async { WidgetsConfigModel? widgetsConfigModel = await API.getWidgetConfigsFromServer(currentDeviceIp, onFailure: (error) { log("Api call failed with this error: ${error.toString()}"); }); if (widgetsConfigModel != null) currentWidgetsConfigModel = widgetsConfigModel; notifyListeners(); } WeathersWidgetModel currentWeathersWidgetModel = WeathersWidgetModel(); Future getWeatherDetailsFromServer() async { WeathersWidgetModel? weathersWidgetModel = await API.getWeatherDetailsFromServer( (currentWidgetsConfigModel!.cityKey ?? "").toString(), onFailure: (error) => log("Api call failed with this error: ${error.toString()}"), ); if (weathersWidgetModel != null) { currentWeathersWidgetModel = weathersWidgetModel; notifyListeners(); } } String nextPrayerToShowWithTime = ''; void getNextPrayerToShow() { final current = DateTime.now(); log("Checking Namaz time Locally at ${current.toString()} and ${current.timeZoneName} "); if (DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.fajr!).isAfter(current)) { final namazTime = DateFormat('hh:mm a').format(DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.fajr!)); nextPrayerToShowWithTime = "${patientCallConfigurations.fajrText} at $namazTime"; notifyListeners(); return; } if (DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.dhuhr!).isAfter(current)) { final namazTime = DateFormat('hh:mm a').format(DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.dhuhr!)); nextPrayerToShowWithTime = "${patientCallConfigurations.dhuhrText} at $namazTime"; notifyListeners(); return; } if (DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.asr!).isAfter(current)) { final namazTime = DateFormat('hh:mm a').format(DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.asr!)); nextPrayerToShowWithTime = "${patientCallConfigurations.asrText} at $namazTime"; notifyListeners(); return; } if (DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.maghrib!).isAfter(current)) { final namazTime = DateFormat('hh:mm a').format(DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.maghrib!)); nextPrayerToShowWithTime = "${patientCallConfigurations.maghribText} at $namazTime"; notifyListeners(); return; } if (DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.isha!).isAfter(current)) { final namazTime = DateFormat('hh:mm a').format(DateTime.fromMillisecondsSinceEpoch(currentPrayersWidgetModel.isha!)); nextPrayerToShowWithTime = "${patientCallConfigurations.ishaText} at $namazTime"; notifyListeners(); return; } } PrayersWidgetModel currentPrayersWidgetModel = PrayersWidgetModel(); Future getPrayerDetailsFromServer() async { PrayersWidgetModel? prayersWidgetModel = await API.getPrayerDetailsFromServer( latitude: currentWidgetsConfigModel!.projectLatitude ?? 0, longitude: currentWidgetsConfigModel!.projectLongitude ?? 0, onFailure: (error) => log("Api call failed with this error: ${error.toString()}")); if (prayersWidgetModel != null) { currentPrayersWidgetModel = prayersWidgetModel; log("I got this data from Prayers: ${prayersWidgetModel.toString()}"); getNextPrayerToShow(); notifyListeners(); } } RssFeedModel currentRssFeedModel = RssFeedModel(); Future getRssFeedDetailsFromServer() async { RssFeedModel? rssFeedModel = await API.getRssFeedDetailsFromServer(languageId: 0, onFailure: (error) => log("Api call failed with this error: ${error.toString()}")); if (rssFeedModel != null) { currentRssFeedModel = rssFeedModel; log("I got this data from RssFeed: ${currentRssFeedModel.rssFeed}"); notifyListeners(); } } Future getInfoWidgetsDetailsFromServer() async { // if (currentWidgetsConfigModel == null) return; await getInfoWidgetsConfigurationsFromServer().whenComplete(() async { if (currentWidgetsConfigModel!.isWeatherReq!) { await getWeatherDetailsFromServer(); } if (currentWidgetsConfigModel!.isPrayerTimeReq!) { await getPrayerDetailsFromServer(); } if (currentWidgetsConfigModel!.isRssFeedReq!) { await getRssFeedDetailsFromServer(); } }); int currentDate = DateTime.now().millisecondsSinceEpoch; await setLastTimeUpdatedInCache(lasTimeUpdated: currentDate.toString()); } int counter = 0; Future getTheWidgetsConfigurationsEveryMidnight() async { if (currentWidgetsConfigModel == null) return; if (!currentWidgetsConfigModel!.isWeatherReq! && !currentWidgetsConfigModel!.isPrayerTimeReq! && !currentWidgetsConfigModel!.isRssFeedReq!) { return; } DateTime current = DateTime.now(); Stream timer = Stream.periodic(const Duration(minutes: 1), (i) { current = current.add(const Duration(minutes: 1)); return current; }); timer.listen((data) async { DateTime dateTime = DateTime.parse(data.toString()); counter++; log("counterValue: $counter"); if (counter == 60 && currentWidgetsConfigModel!.isRssFeedReq!) { await getRssFeedDetailsFromServer(); } if (currentWidgetsConfigModel!.isWeatherReq!) { if (dateTime.day > currentLastTimeUpdated.day) { await getWeatherDetailsFromServer(); } } if (currentWidgetsConfigModel!.isPrayerTimeReq!) { if (dateTime.day > currentLastTimeUpdated.day) { await getPrayerDetailsFromServer(); } } if (currentWidgetsConfigModel!.isRssFeedReq!) { if (dateTime.day > currentLastTimeUpdated.day) { await getRssFeedDetailsFromServer(); } } getNextPrayerToShow(); }); } Future startSignalHubConnection() async { if (!signalRHelper.getConnectionState()) { await getCurrentIP().whenComplete(() => signalRHelper.startSignalRConnection( currentDeviceIp, onUpdateAvailable: onPingReceived, onConnect: onConnect, onConnecting: onConnecting, onDisconnect: onDisconnect, )); } } Future callPatientsAPI() async { log("calling callPatientsAPI"); patientTickets.clear(); API.getCallRequestInfoByClinicInfo(currentDeviceIp, onSuccess: (waitingCalls, isQueuePatientsCalls, callConfigs) async { patientCallConfigurations = callConfigs; if (waitingCalls.length > patientCallConfigurations.screenMaxDisplayPatients) { patientTickets = waitingCalls.sublist(0, patientCallConfigurations.screenMaxDisplayPatients); } else { patientTickets = waitingCalls; } if (isQueuePatientsCalls.length > patientCallConfigurations.screenMaxDisplayPatients) { isQueuePatients = isQueuePatientsCalls.sublist(0, patientCallConfigurations.screenMaxDisplayPatients); } else { isQueuePatients = isQueuePatientsCalls; } notifyListeners(); if (patientTickets.isNotEmpty) { updateCurrentScreenRotation(patientTickets.first.screenRotationEnum); voiceCallPatientTicket(patientTickets.first, "callPatientsAPI"); updatePatientTicket(patientTickets.first); } }, onFailure: (error) => log("Api call failed with this error: ${error.toString()}")); } onPingReceived(data) async { log("A new Ping Received"); log("isCallingInProgress from onPingReceived: $isCallingInProgress"); log("isApiCallNeeded: $isApiCallNeeded"); if (patientTickets.isNotEmpty) { if (isCallingInProgress) { isApiCallNeeded = true; } else { await callPatientsAPI(); } } else { await callPatientsAPI(); } } String getCallTypeText(PatientTicketModel ticket) { final callType = ticket.getCallType(); switch (callType) { case CallType.vitalSign: return ticket.callForVitalSignText; case CallType.doctor: return ticket.callForDoctorText; case CallType.procedure: return ticket.callForProcedureText; case CallType.vaccination: return ticket.callForVaccinationText; case CallType.nebulization: return ticket.callForNebulizationText; default: return ticket.callForVitalSignText; } } CallByVoice? voiceCaller; PatientTicketModel currentPatient = PatientTicketModel(); // testCalling() async { // voiceCaller = CallByVoice( // preVoice: "رقم التذكرة", // ticketNo: "AMG A-78", // postVoice: "دعوة للتطعيم", // lang: "ar", // flutterTts: flutterTts, // ); // await voiceCaller!.startCalling(true); // } voiceCallPatientTicket(PatientTicketModel patientTicket, String calledFrom) async { log("voiceCallPatientTicket calledFrom : $calledFrom"); currentPatient = patientTicket; isCallingInProgress = true; log("Setting isCallingInProgress : $isCallingInProgress"); log("isVoiceReq: ${patientTicket.isVoiceReq}"); log("voiceCaller: ${voiceCaller == null}"); log("isQueue: ${patientTicket.isQueue}"); log("isToneReq: ${patientTicket.isToneReq}"); if (patientTicket.isToneReq && !patientTicket.isQueue) { audioPlayer.setAsset("assets/tones/call_tone.mp3"); await audioPlayer.play(); Future.delayed(const Duration(seconds: 3)); } if (patientTicket.isVoiceReq && voiceCaller == null && !patientTicket.isQueue) { log("patientTicket.voiceLanguage: ${patientTicket.voiceLanguage}"); final postVoice = getCallTypeText(patientTicket); voiceCaller = CallByVoice( preVoice: patientTicket.ticketNoText, ticketNo: patientTicket.queueNo.trim().toString(), postVoice: postVoice, lang: patientTicket.voiceLanguage == 1 ? "en" : "ar", flutterTts: flutterTts, ); await voiceCaller!.startCalling(patientTicket.queueNo.trim().toString() != patientTicket.callNoStr.trim().toString()).whenComplete(() { voiceCaller = null; // if (isQueuePatients.isNotEmpty) { // isQueuePatients.removeAt(0); // } }); } else { isCallingInProgress = false; log("Setting isCallingInProgress : $isCallingInProgress"); if (isApiCallNeeded) { Timer(Duration(seconds: patientCallConfigurations.concurrentCallDelaySec), () async { await callPatientsAPI(); isApiCallNeeded = false; }); } } } Future listenAudioPlayerEvents() async { audioPlayer.playerStateStream.listen((playerState) async { if (playerState.processingState == ProcessingState.completed) { if (currentPatient.isVoiceReq) { isCallingInProgress = true; return; } if (isQueuePatients.isNotEmpty) { isCallingInProgress = true; log("i am here on line 375"); log("isQueuePatients.length 1: ${isQueuePatients.length}"); // for (int i = 0; i < length; i++) { await Future.delayed(Duration(seconds: patientCallConfigurations.concurrentCallDelaySec)).whenComplete(() async { PatientTicketModel temp = PatientTicketModel(); if (patientTickets.isNotEmpty && isQueuePatients.length > 1) { temp = patientTickets.elementAt(0); patientTickets.removeAt(0); } notifyListeners(); if (isQueuePatients.isNotEmpty) { isQueuePatients.removeAt(0); } if (patientTickets.isNotEmpty && isQueuePatients.length > 1) { patientTickets.add(temp); } notifyListeners(); log("isQueuePatients.length 2: ${isQueuePatients.length}"); if (isQueuePatients.isNotEmpty) { await voiceCallPatientTicket(patientTickets.first, "listenAudioPlayerEvents"); updatePatientTicket(patientTickets.first); } }); // } } if (isQueuePatients.isEmpty) { isCallingInProgress = false; } if (isApiCallNeeded && isQueuePatients.isEmpty) { log("Setting isCallingInProgress : $isCallingInProgress"); Timer(Duration(seconds: patientCallConfigurations.concurrentCallDelaySec), () async { await callPatientsAPI(); isApiCallNeeded = false; }); } } }); flutterTts.setCompletionHandler(() async { log("My Value is in setCompletionHandler: $isVoiceActualCompletedGlobally"); log("isQueuePatients.length in setCompletionHandler: ${isQueuePatients.length}"); if (!isVoiceActualCompletedGlobally) { return; } if (isQueuePatients.isNotEmpty) { // final length = isQueuePatients.length; // for (int i = 0; i < length; i++) { await Future.delayed(Duration(seconds: patientCallConfigurations.concurrentCallDelaySec)).whenComplete( () async { PatientTicketModel temp = PatientTicketModel(); if (patientTickets.isNotEmpty && isQueuePatients.length > 1) { temp = patientTickets.elementAt(0); patientTickets.removeAt(0); } notifyListeners(); if (isQueuePatients.isNotEmpty) { isQueuePatients.removeAt(0); } if (patientTickets.isNotEmpty && isQueuePatients.length > 1) { patientTickets.add(temp); } notifyListeners(); if (isQueuePatients.isNotEmpty) { await voiceCallPatientTicket(patientTickets.first, "setCompletionHandler"); updatePatientTicket(patientTickets.first); } }, ); // } } log("here logg: ${isQueuePatients.length}"); if (isQueuePatients.isEmpty) { isCallingInProgress = false; } if (isApiCallNeeded && isQueuePatients.isEmpty) { log("Setting isCallingInProgress : $isCallingInProgress"); Timer(Duration(seconds: patientCallConfigurations.concurrentCallDelaySec), () async { await callPatientsAPI(); isApiCallNeeded = false; }); } }); } // updatePatientTickets() { // if (patientTickets.isNotEmpty) { // List _ticketsToUpdate = patientTickets.where((t) => t.callUpdated == false).toList(); // API.callUpdateNotIsQueueRecordByIDAsync(currentDeviceIp, ticket: _ticketsToUpdate.first, onSuccess: (ticketsUpdated) { // log("[${ticketsUpdated.length}] Tickets Updated: $ticketsUpdated"); // }, onFailure: (e) { // log(" Tickets Update Failed with : ${e.toString()}"); // }); // } // } updatePatientTicket(PatientTicketModel patientTicket) { if (!patientTicket.isQueue) { API.callUpdateNotIsQueueRecordByIDAsync(currentDeviceIp, ticket: patientTicket, onSuccess: (ticketsUpdated) { log("[${patientTicket.callNoStr}] Ticket Updated: $ticketsUpdated"); }, onFailure: (e) { log(" Tickets Update ${patientTicket.callNoStr} Failed with Error : ${e.toString()}"); }); } } onConnect() { log("SignalR: onConnect"); } onDisconnect(exception) { log("SignalR: onDisconnect"); signalRHelper.startSignalRConnection(currentDeviceIp, onUpdateAvailable: onPingReceived, onConnect: onConnect, onConnecting: onConnecting, onDisconnect: onDisconnect); } onConnecting() { log("SignalR: onConnecting"); } listenNetworkConnectivity() async { Connectivity().onConnectivityChanged.listen((event) async { switch (event) { case ConnectivityResult.wifi: updateInternetConnection(true); await getCurrentIP(); if (signalRHelper.connection != null) { if (signalRHelper.connection!.state != HubConnectionState.connected) signalRHelper.connection!.start(); } break; case ConnectivityResult.none: updateInternetConnection(false); signalRHelper.closeConnection(); break; case ConnectivityResult.mobile: break; } }); } //SHARED PREFERENCE HANDLING DateTime currentLastTimeUpdated = DateTime.now(); Future getLastTimeUpdatedFromCache() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); if (prefs.containsKey("lastTimeUpdated")) { String? lastTimeUpdated = prefs.getString("lastTimeUpdated"); currentLastTimeUpdated = DateTime.fromMillisecondsSinceEpoch(int.parse(lastTimeUpdated!)); return lastTimeUpdated; } else { return null; } } Future setLastTimeUpdatedInCache({required String lasTimeUpdated}) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString("lastTimeUpdated", lasTimeUpdated); } }