import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'package:connectivity_plus/connectivity_plus.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/main.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(); initializeFlutterTTS(); // 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; initializeFlutterTTS() async { flutterTts.setCompletionHandler(() => onVoiceCompleted()); } Future onVoiceCompleted() async { logger.i("My Value is in setCompletionHandler: $isVoiceActualCompletedGlobally"); logger.i("isQueuePatients.length in setCompletionHandler: ${isQueuePatients.length}"); if (!isVoiceActualCompletedGlobally) { return; } if (isQueuePatients.isNotEmpty) { 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); } }, ); } logger.i("here logger.ig: ${isQueuePatients.length}"); if (isQueuePatients.isEmpty) { isCallingInProgress = false; } if (isApiCallNeeded && isQueuePatients.isEmpty) { logger.i("Setting isCallingInProgress : $isCallingInProgress"); Timer(Duration(seconds: patientCallConfigurations.concurrentCallDelaySec), () async { await callPatientsAPI(); 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) { logger.i("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) => logger.i("Api call failed with this error: ${error.toString()}"), ); if (weathersWidgetModel != null) { currentWeathersWidgetModel = weathersWidgetModel; notifyListeners(); } } String nextPrayerToShowWithTime = ''; void getNextPrayerToShow() { final current = DateTime.now(); logger.i("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) => logger.i("Api call failed with this error: ${error.toString()}")); if (prayersWidgetModel != null) { currentPrayersWidgetModel = prayersWidgetModel; logger.i("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) => logger.i("Api call failed with this error: ${error.toString()}")); if (rssFeedModel != null) { currentRssFeedModel = rssFeedModel; logger.i("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++; logger.i("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 { logger.i("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); await voiceCallPatientTicket(patientTickets.first, "callPatientsAPI"); updatePatientTicket(patientTickets.first); } }, onFailure: (error) => logger.i("Api call failed with this error: ${error.toString()}")); } onPingReceived(data) async { logger.i("A new Ping Received when isQueuePatients: ${isQueuePatients.length}"); logger.i("isCallingInProgress from onPingReceived: $isCallingInProgress"); logger.i("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 { logger.i("voiceCallPatientTicket calledFrom : $calledFrom"); currentPatient = patientTicket; isCallingInProgress = true; logger.i("Setting isCallingInProgress : $isCallingInProgress"); logger.i("isVoiceReq: ${patientTicket.isVoiceReq}"); logger.i("voiceCaller: ${voiceCaller == null}"); logger.i("isQueue: ${patientTicket.isQueue}"); logger.i("isToneReq: ${patientTicket.isToneReq}"); if (patientTicket.isToneReq && !patientTicket.isQueue) { audioPlayer.setAsset("assets/tones/call_tone.mp3"); await audioPlayer.play(); await Future.delayed(const Duration(seconds: 2)); } if (patientTicket.isVoiceReq && voiceCaller == null && !patientTicket.isQueue) { logger.i("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; onVoiceCompleted(); logger.i("Completed Calling!! ${isQueuePatients.length}"); }); } else { isCallingInProgress = false; logger.i("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) { logger.i("Tone Completed"); if (currentPatient.isVoiceReq) { isCallingInProgress = true; return; } if (isQueuePatients.isNotEmpty) { isCallingInProgress = true; logger.i("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(); logger.i("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) { logger.i("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) { // logger.i("[${ticketsUpdated.length}] Tickets Updated: $ticketsUpdated"); // }, onFailure: (e) { // logger.i(" Tickets Update Failed with : ${e.toString()}"); // }); // } // } updatePatientTicket(PatientTicketModel patientTicket) { if (!patientTicket.isQueue) { API.callUpdateNotIsQueueRecordByIDAsync(currentDeviceIp, ticket: patientTicket, onSuccess: (ticketsUpdated) { logger.i("[${patientTicket.callNoStr}] Ticket Updated: $ticketsUpdated"); }, onFailure: (e) { logger.i(" Tickets Update ${patientTicket.callNoStr} Failed with Error : ${e.toString()}"); }); } } onConnect() { logger.i("SignalR: onConnect"); } onDisconnect(exception) { logger.i("SignalR: onDisconnect"); signalRHelper.startSignalRConnection( currentDeviceIp, onUpdateAvailable: onPingReceived, onConnect: onConnect, onConnecting: onConnecting, onDisconnect: onDisconnect, ); } onConnecting() { logger.i("SignalR: onConnecting"); } listenNetworkConnectivity() async { Connectivity().onConnectivityChanged.listen((List event) async { switch (event.first) { case ConnectivityResult.wifi: case ConnectivityResult.ethernet: 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; case ConnectivityResult.bluetooth: // TODO: Handle this case. break; // TODO: Handle this case. case ConnectivityResult.vpn: // TODO: Handle this case. break; case ConnectivityResult.other: // TODO: Handle this case. 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); } }