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 < void > 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 < PatientTicketModel > patientTickets = [ ] ;
List < PatientTicketModel > isQueuePatients = [ ] ;
String currentDeviceIp = " " ;
bool isCallingInProgress = false ;
bool isInternetConnectionAvailable = true ;
bool isApiCallNeeded = false ;
initializeFlutterTTS ( ) async {
flutterTts . setCompletionHandler ( ( ) = > onVoiceCompleted ( ) ) ;
}
Future < void > 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 < void > 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 < void > 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 < void > 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 < void > 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 < void > 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 < void > 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 < void > 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 < void > startSignalHubConnection ( ) async {
if ( ! signalRHelper . getConnectionState ( ) ) {
await getCurrentIP ( ) . whenComplete ( ( ) = > signalRHelper . startSignalRConnection (
currentDeviceIp ,
onUpdateAvailable: onPingReceived ,
onConnect: onConnect ,
onConnecting: onConnecting ,
onDisconnect: onDisconnect ,
) ) ;
}
}
Future < void > 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 < void > 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<Tickets> _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 < ConnectivityResult > 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 < String ? > 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 < void > setLastTimeUpdatedInCache ( { required String lasTimeUpdated } ) async {
final SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
await prefs . setString ( " lastTimeUpdated " , lasTimeUpdated ) ;
}
}